aoqi@0: /* aoqi@0: * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: /* aoqi@0: * @test aoqi@0: * @bug 8002099 8006694 aoqi@0: * @summary Add support for intersection types in cast expression aoqi@0: * temporarily workaround combo tests are causing time out in several platforms aoqi@0: * @library ../../lib aoqi@0: * @build JavacTestingAbstractThreadedTest aoqi@0: * @run main/othervm/timeout=360 IntersectionTypeCastTest aoqi@0: */ aoqi@0: aoqi@0: // use /othervm to avoid jtreg timeout issues (CODETOOLS-7900047) aoqi@0: // see JDK-8006746 aoqi@0: aoqi@0: import java.net.URI; aoqi@0: import java.util.Arrays; aoqi@0: import javax.tools.Diagnostic; aoqi@0: import javax.tools.JavaCompiler; aoqi@0: import javax.tools.JavaFileObject; aoqi@0: import javax.tools.SimpleJavaFileObject; aoqi@0: import javax.tools.ToolProvider; aoqi@0: aoqi@0: import com.sun.source.util.JavacTask; aoqi@0: import com.sun.tools.javac.util.List; aoqi@0: import com.sun.tools.javac.util.ListBuffer; aoqi@0: aoqi@0: public class IntersectionTypeCastTest aoqi@0: extends JavacTestingAbstractThreadedTest aoqi@0: implements Runnable { aoqi@0: aoqi@0: interface Type { aoqi@0: boolean subtypeOf(Type that); aoqi@0: String asString(); aoqi@0: boolean isClass(); aoqi@0: boolean isInterface(); aoqi@0: } aoqi@0: aoqi@0: enum InterfaceKind implements Type { aoqi@0: A("interface A { }\n", "A", null), aoqi@0: B("interface B { }\n", "B", null), aoqi@0: C("interface C extends A { }\n", "C", A); aoqi@0: aoqi@0: String declStr; aoqi@0: String typeStr; aoqi@0: InterfaceKind superInterface; aoqi@0: aoqi@0: InterfaceKind(String declStr, String typeStr, aoqi@0: InterfaceKind superInterface) { aoqi@0: this.declStr = declStr; aoqi@0: this.typeStr = typeStr; aoqi@0: this.superInterface = superInterface; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean subtypeOf(Type that) { aoqi@0: return this == that || superInterface == that || aoqi@0: that == ClassKind.OBJECT; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String asString() { aoqi@0: return typeStr; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean isClass() { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean isInterface() { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: enum ClassKind implements Type { aoqi@0: OBJECT(null, "Object"), aoqi@0: CA("#M class CA implements A { }\n", "CA", aoqi@0: InterfaceKind.A), aoqi@0: CB("#M class CB implements B { }\n", "CB", aoqi@0: InterfaceKind.B), aoqi@0: CAB("#M class CAB implements A, B { }\n", "CAB", aoqi@0: InterfaceKind.A, InterfaceKind.B), aoqi@0: CC("#M class CC implements C { }\n", "CC", aoqi@0: InterfaceKind.C, InterfaceKind.A), aoqi@0: CCA("#M class CCA implements C, A { }\n", "CCA", aoqi@0: InterfaceKind.C, InterfaceKind.A), aoqi@0: CCB("#M class CCB implements C, B { }\n", "CCB", aoqi@0: InterfaceKind.C, InterfaceKind.A, InterfaceKind.B), aoqi@0: CCAB("#M class CCAB implements C, A, B { }\n", "CCAB", aoqi@0: InterfaceKind.C, InterfaceKind.A, InterfaceKind.B); aoqi@0: aoqi@0: String declTemplate; aoqi@0: String typeStr; aoqi@0: List superInterfaces; aoqi@0: aoqi@0: ClassKind(String declTemplate, String typeStr, aoqi@0: InterfaceKind... superInterfaces) { aoqi@0: this.declTemplate = declTemplate; aoqi@0: this.typeStr = typeStr; aoqi@0: this.superInterfaces = List.from(superInterfaces); aoqi@0: } aoqi@0: aoqi@0: String getDecl(ModifierKind mod) { aoqi@0: return declTemplate != null ? aoqi@0: declTemplate.replaceAll("#M", mod.modStr) : aoqi@0: ""; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean subtypeOf(Type that) { aoqi@0: return this == that || superInterfaces.contains(that) || aoqi@0: that == OBJECT; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public String asString() { aoqi@0: return typeStr; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean isClass() { aoqi@0: return true; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public boolean isInterface() { aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: enum ModifierKind { aoqi@0: NONE(""), aoqi@0: FINAL("final"); aoqi@0: aoqi@0: String modStr; aoqi@0: aoqi@0: ModifierKind(String modStr) { aoqi@0: this.modStr = modStr; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: enum CastKind { aoqi@0: CLASS("(#C)", 0), aoqi@0: INTERFACE("(#I0)", 1), aoqi@0: INTERSECTION2("(#C & #I0)", 1), aoqi@0: INTERSECTION3("(#C & #I0 & #I1)", 2); aoqi@0: //INTERSECTION4("(#C & #I0 & #I1 & #I2)", 3); aoqi@0: aoqi@0: String castTemplate; aoqi@0: int interfaceBounds; aoqi@0: aoqi@0: CastKind(String castTemplate, int interfaceBounds) { aoqi@0: this.castTemplate = castTemplate; aoqi@0: this.interfaceBounds = interfaceBounds; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: static class CastInfo { aoqi@0: CastKind kind; aoqi@0: Type[] types; aoqi@0: aoqi@0: CastInfo(CastKind kind, Type... types) { aoqi@0: this.kind = kind; aoqi@0: this.types = types; aoqi@0: } aoqi@0: aoqi@0: String getCast() { aoqi@0: String temp = kind.castTemplate.replaceAll("#C", aoqi@0: types[0].asString()); aoqi@0: for (int i = 0; i < kind.interfaceBounds ; i++) { aoqi@0: temp = temp.replace(String.format("#I%d", i), aoqi@0: types[i + 1].asString()); aoqi@0: } aoqi@0: return temp; aoqi@0: } aoqi@0: aoqi@0: boolean hasDuplicateTypes() { aoqi@0: for (int i = 0 ; i < types.length ; i++) { aoqi@0: for (int j = 0 ; j < types.length ; j++) { aoqi@0: if (i != j && types[i] == types[j]) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: boolean compatibleWith(ModifierKind mod, CastInfo that) { aoqi@0: for (Type t1 : types) { aoqi@0: for (Type t2 : that.types) { aoqi@0: boolean compat = aoqi@0: t1.subtypeOf(t2) || aoqi@0: t2.subtypeOf(t1) || aoqi@0: (t1.isInterface() && t2.isInterface()) || //side-cast (1) aoqi@0: (mod == ModifierKind.NONE && aoqi@0: (t1.isInterface() != t2.isInterface())); //side-cast (2) aoqi@0: if (!compat) return false; aoqi@0: } aoqi@0: } aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: public static void main(String... args) throws Exception { aoqi@0: for (ModifierKind mod : ModifierKind.values()) { aoqi@0: for (CastInfo cast1 : allCastInfo()) { aoqi@0: for (CastInfo cast2 : allCastInfo()) { aoqi@0: pool.execute( aoqi@0: new IntersectionTypeCastTest(mod, cast1, cast2)); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: checkAfterExec(); aoqi@0: } aoqi@0: aoqi@0: static List allCastInfo() { aoqi@0: ListBuffer buf = new ListBuffer<>(); aoqi@0: for (CastKind kind : CastKind.values()) { aoqi@0: for (ClassKind clazz : ClassKind.values()) { aoqi@0: if (kind == CastKind.INTERFACE && clazz != ClassKind.OBJECT) { aoqi@0: continue; aoqi@0: } else if (kind.interfaceBounds == 0) { aoqi@0: buf.append(new CastInfo(kind, clazz)); aoqi@0: continue; aoqi@0: } else { aoqi@0: for (InterfaceKind intf1 : InterfaceKind.values()) { aoqi@0: if (kind.interfaceBounds == 1) { aoqi@0: buf.append(new CastInfo(kind, clazz, intf1)); aoqi@0: continue; aoqi@0: } else { aoqi@0: for (InterfaceKind intf2 : InterfaceKind.values()) { aoqi@0: if (kind.interfaceBounds == 2) { aoqi@0: buf.append( aoqi@0: new CastInfo(kind, clazz, intf1, intf2)); aoqi@0: continue; aoqi@0: } else { aoqi@0: for (InterfaceKind intf3 : InterfaceKind.values()) { aoqi@0: buf.append( aoqi@0: new CastInfo(kind, clazz, intf1, aoqi@0: intf2, intf3)); aoqi@0: continue; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return buf.toList(); aoqi@0: } aoqi@0: aoqi@0: ModifierKind mod; aoqi@0: CastInfo cast1, cast2; aoqi@0: JavaSource source; aoqi@0: DiagnosticChecker diagChecker; aoqi@0: aoqi@0: IntersectionTypeCastTest(ModifierKind mod, CastInfo cast1, CastInfo cast2) { aoqi@0: this.mod = mod; aoqi@0: this.cast1 = cast1; aoqi@0: this.cast2 = cast2; aoqi@0: this.source = new JavaSource(); aoqi@0: this.diagChecker = new DiagnosticChecker(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void run() { aoqi@0: final JavaCompiler tool = ToolProvider.getSystemJavaCompiler(); aoqi@0: aoqi@0: JavacTask ct = (JavacTask)tool.getTask(null, fm.get(), diagChecker, aoqi@0: null, null, Arrays.asList(source)); aoqi@0: try { aoqi@0: ct.analyze(); aoqi@0: } catch (Throwable ex) { aoqi@0: throw new AssertionError("Error thrown when compiling the following code:\n" + aoqi@0: source.getCharContent(true)); aoqi@0: } aoqi@0: check(); aoqi@0: } aoqi@0: aoqi@0: class JavaSource extends SimpleJavaFileObject { aoqi@0: aoqi@0: String bodyTemplate = "class Test {\n" + aoqi@0: " void test() {\n" + aoqi@0: " Object o = #C1#C2null;\n" + aoqi@0: " } }"; aoqi@0: aoqi@0: String source = ""; aoqi@0: aoqi@0: public JavaSource() { aoqi@0: super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); aoqi@0: for (ClassKind ck : ClassKind.values()) { aoqi@0: source += ck.getDecl(mod); aoqi@0: } aoqi@0: for (InterfaceKind ik : InterfaceKind.values()) { aoqi@0: source += ik.declStr; aoqi@0: } aoqi@0: source += bodyTemplate.replaceAll("#C1", cast1.getCast()). aoqi@0: replaceAll("#C2", cast2.getCast()); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public CharSequence getCharContent(boolean ignoreEncodingErrors) { aoqi@0: return source; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void check() { aoqi@0: checkCount.incrementAndGet(); aoqi@0: aoqi@0: boolean errorExpected = cast1.hasDuplicateTypes() || aoqi@0: cast2.hasDuplicateTypes(); aoqi@0: aoqi@0: errorExpected |= !cast2.compatibleWith(mod, cast1); aoqi@0: aoqi@0: if (errorExpected != diagChecker.errorFound) { aoqi@0: throw new Error("invalid diagnostics for source:\n" + aoqi@0: source.getCharContent(true) + aoqi@0: "\nFound error: " + diagChecker.errorFound + aoqi@0: "\nExpected error: " + errorExpected); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: static class DiagnosticChecker aoqi@0: implements javax.tools.DiagnosticListener { aoqi@0: aoqi@0: boolean errorFound; aoqi@0: aoqi@0: public void report(Diagnostic diagnostic) { aoqi@0: if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { aoqi@0: errorFound = true; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: }