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