Java8 IntersectionCast support.
Add support for the following cast expression syntax:
CastExpression:
(ReferenceType {& InterfaceType} ) UnaryExpression
(ReferenceType {& InterfaceType} ) LambdaExpression
Change-Id: I8066d5f5d3d1b071c90a3016b265f418e051cb1e
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java b/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
index d17441e..e427346 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtUtil.java
@@ -265,4 +265,36 @@
return null;
}
}
+
+ public static String signature(FieldBinding binding) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(binding.declaringClass.constantPoolName());
+ sb.append('.');
+ sb.append(binding.name);
+ sb.append(':');
+ sb.append(binding.type.signature());
+ return sb.toString();
+ }
+
+ public static String signature(MethodBinding binding) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(binding.declaringClass.constantPoolName());
+ sb.append('.');
+ sb.append(binding.selector);
+ sb.append('(');
+ for (TypeBinding paramType : binding.parameters) {
+ sb.append(paramType.signature());
+ }
+ sb.append(')');
+ sb.append(binding.returnType.signature());
+ return sb.toString();
+ }
+
+ public static String signature(TypeBinding binding) {
+ if (binding.isBaseType()) {
+ return String.valueOf(binding.sourceName());
+ } else {
+ return String.valueOf(binding.constantPoolName());
+ }
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index 25a60fa..8ba59f7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -115,6 +115,7 @@
import com.google.gwt.thirdparty.guava.common.base.Function;
import com.google.gwt.thirdparty.guava.common.base.Preconditions;
import com.google.gwt.thirdparty.guava.common.collect.Interner;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
@@ -208,12 +209,15 @@
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodVerifier;
import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
@@ -540,11 +544,16 @@
@Override
public void endVisit(CastExpression x, BlockScope scope) {
+ /**
+ * Our output of a ((A & I1 & I2) a) looks like this:
+ *
+ * ((A)(I1)(I2)a).
+ */
try {
SourceInfo info = makeSourceInfo(x);
- JType type = typeMap.get(x.resolvedType);
+ JType[] type = processCastType(x.resolvedType);
JExpression expression = pop(x.expression);
- push(new JCastOperation(info, type, expression));
+ push(buildCastOperation(info, type, expression));
} catch (Throwable e) {
throw translateException(x, e);
}
@@ -1201,17 +1210,26 @@
TypeBinding binding = x.expectedType();
// Find the single abstract method of this interface
MethodBinding samBinding = binding.getSingleAbstractMethod(blockScope, false);
+ assert (samBinding != null && samBinding.isValidBinding());
// Lookup the JMethod version
JMethod interfaceMethod = typeMap.get(samBinding);
// And its JInterface container we must implement
- JInterfaceType funcType = (JInterfaceType) typeMap.get(binding);
+ // There may be more than more JInterface containers to be implemented
+ // if the lambda expression is cast to a IntersectionCastType.
+ JInterfaceType[] funcType;
+ if (binding instanceof IntersectionTypeBinding18) {
+ funcType = processIntersectionTypeForLambda((IntersectionTypeBinding18) binding, blockScope,
+ JdtUtil.signature(samBinding));
+ } else {
+ funcType = new JInterfaceType[] {(JInterfaceType) typeMap.get(binding)};
+ }
SourceInfo info = makeSourceInfo(x);
// Create an inner class to implement the interface and SAM method.
// class lambda$0$Type implements T {}
JClassType innerLambdaClass = createInnerClass(JdtUtil.asDottedString(x.binding.declaringClass.compoundName) +
- "$" + new String(x.binding.selector), x, funcType, info);
+ "$" + new String(x.binding.selector), x, info, funcType);
JConstructor ctor = new JConstructor(info, innerLambdaClass);
// locals captured by the lambda and saved as fields on the anonymous inner class
@@ -1400,11 +1418,13 @@
return outerParam;
}
- private JClassType createInnerClass(String name, FunctionalExpression x,
- JInterfaceType funcType, SourceInfo info) {
+ private JClassType createInnerClass(String name, FunctionalExpression x, SourceInfo info,
+ JInterfaceType... funcType) {
JClassType innerLambdaClass = new JClassType(info, name + "$Type", false, true);
innerLambdaClass.setEnclosingType((JDeclaredType) typeMap.get(x.binding.declaringClass));
- innerLambdaClass.addImplements(funcType);
+ for (JInterfaceType type : funcType) {
+ innerLambdaClass.addImplements(type);
+ }
innerLambdaClass.setSuperClass(javaLangObject);
createSyntheticMethod(info, CLINIT_NAME, innerLambdaClass, JPrimitiveType.VOID, false, true,
@@ -1461,7 +1481,7 @@
ReferenceBinding targetType =
scope.enclosingSourceType().enclosingTypeAt(
(x.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT);
- receiver = makeThisReference(info, targetType, true, scope);
+ receiver = resolveThisReference(info, targetType, true, scope);
} else if (x.receiver.sourceStart == 0) {
// Synthetic this ref with bad source info; fix the info.
JThisRef oldRef = (JThisRef) receiver;
@@ -1635,7 +1655,7 @@
// Java8 super reference to default method from subtype, X.super.someDefaultMethod
push(makeThisRef(info));
} else {
- push(makeThisReference(info, targetType, true, scope));
+ push(resolveThisReference(info, targetType, true, scope));
}
} catch (Throwable e) {
throw translateException(x, e);
@@ -1647,7 +1667,7 @@
try {
SourceInfo info = makeSourceInfo(x);
ReferenceBinding targetType = (ReferenceBinding) x.qualification.resolvedType;
- push(makeThisReference(info, targetType, true, scope));
+ push(resolveThisReference(info, targetType, true, scope));
} catch (Throwable e) {
throw translateException(x, e);
}
@@ -1718,7 +1738,7 @@
List<JExpression> enclosingThisRefs = new ArrayList<JExpression>();
if (innerLambdaClass == null) {
- innerLambdaClass = createInnerClass(lambdaName, x, funcType, info);
+ innerLambdaClass = createInnerClass(lambdaName, x, info, funcType);
lambdaNameToInnerLambdaType.put(lambdaName, innerLambdaClass);
newTypes.add(innerLambdaClass);
@@ -1745,7 +1765,7 @@
if (JdtUtil.isInnerClass(targetBinding)) {
for (ReferenceBinding argType : targetBinding.syntheticEnclosingInstanceTypes()) {
argType = (ReferenceBinding) argType.erasure();
- JExpression enclosingThisRef = makeThisReference(info, argType, false, blockScope);
+ JExpression enclosingThisRef = resolveThisReference(info, argType, false, blockScope);
JField enclosingInstance = createAndBindCapturedLambdaParameter(info,
String.valueOf(argType.readableName()).replace('.', '_'),
enclosingThisRef.getType(), ctor, ctorBody);
@@ -2906,7 +2926,7 @@
return new JThisRef(info, curClass.getClassOrInterface());
}
- private JExpression makeThisReference(SourceInfo info, ReferenceBinding targetType,
+ private JExpression resolveThisReference(SourceInfo info, ReferenceBinding targetType,
boolean exactMatch, BlockScope scope) {
targetType = (ReferenceBinding) targetType.erasure();
@@ -3095,7 +3115,7 @@
call.addArg(qualifier);
} else {
// Get implicit outer object.
- call.addArg(makeThisReference(call.getSourceInfo(), targetType, false, curMethod.scope));
+ call.addArg(resolveThisReference(call.getSourceInfo(), targetType, false, curMethod.scope));
}
}
}
@@ -3197,7 +3217,7 @@
if (qualifier != null && argType == targetEnclosingType) {
call.addArg(qualExpr);
} else {
- JExpression thisRef = makeThisReference(info, argType, false, scope);
+ JExpression thisRef = resolveThisReference(info, argType, false, scope);
call.addArg(thisRef);
}
}
@@ -3329,7 +3349,7 @@
assert field != null;
JExpression thisRef = null;
if (!b.isStatic()) {
- thisRef = makeThisReference(info, (ReferenceBinding) x.actualReceiverType, false, scope);
+ thisRef = resolveThisReference(info, (ReferenceBinding) x.actualReceiverType, false, scope);
}
result = new JFieldRef(info, thisRef, field, curClass.type);
} else {
@@ -3438,6 +3458,119 @@
implementMethod(method, call);
}
}
+
+ private JCastOperation buildCastOperation(SourceInfo info, JType[] castTypes,
+ JExpression expression) {
+ return buildCastOperation(info, castTypes, expression, 0);
+ }
+
+ private JCastOperation buildCastOperation(SourceInfo info, JType[] castTypes,
+ JExpression expression, int idx) {
+ if (idx == castTypes.length - 1) {
+ return new JCastOperation(info, castTypes[idx], expression);
+ } else {
+ return new JCastOperation(info, castTypes[idx],
+ buildCastOperation(info, castTypes, expression, idx + 1));
+ }
+ }
+
+ private JReferenceType[] processIntersectionCastType(IntersectionTypeBinding18 type) {
+ JReferenceType[] castTypes = new JReferenceType[type.intersectingTypes.length];
+ int i = 0;
+ for (ReferenceBinding intersectingTypeBinding : type.intersectingTypes) {
+ JType intersectingType = typeMap.get(intersectingTypeBinding);
+ assert (intersectingType instanceof JReferenceType);
+ castTypes[i++] = ((JReferenceType) intersectingType);
+ }
+ return castTypes;
+ }
+
+ private JType[] processCastType(TypeBinding type) {
+ if (type instanceof IntersectionTypeBinding18) {
+ return processIntersectionCastType((IntersectionTypeBinding18) type);
+ } else {
+ return new JType[] {typeMap.get(type)};
+ }
+ }
+
+ private JInterfaceType[] processIntersectionTypeForLambda(IntersectionTypeBinding18 type,
+ BlockScope scope, String samSignature) {
+ List<JInterfaceType> interfaces = Lists.newArrayList();
+ for (ReferenceBinding intersectingTypeBinding : type.intersectingTypes) {
+ if (shouldImplements(intersectingTypeBinding, scope, samSignature)) {
+ JType intersectingType = typeMap.get(intersectingTypeBinding);
+ assert (intersectingType instanceof JInterfaceType);
+ interfaces.add(((JInterfaceType) intersectingType));
+ }
+ }
+ return Iterables.toArray(interfaces, JInterfaceType.class);
+ }
+
+ private boolean isFunctionalInterfaceWithMethod(ReferenceBinding referenceBinding, Scope scope,
+ String samSignature) {
+ if (!referenceBinding.isInterface()) {
+ return false;
+ }
+ MethodBinding abstractMethod = referenceBinding.getSingleAbstractMethod(scope, false);
+ return abstractMethod != null && abstractMethod.isValidBinding()
+ && JdtUtil.signature(abstractMethod).equals(samSignature);
+ }
+
+ private boolean isInterfaceHasNoAbstractMethod(ReferenceBinding referenceBinding, Scope scope) {
+ List<MethodBinding> abstractMethods = getInterfaceAbstractMethods(referenceBinding, scope);
+ return abstractMethods != null && abstractMethods.size() == 0;
+ }
+
+ private boolean shouldImplements(ReferenceBinding referenceBinding, Scope scope,
+ String samSignature) {
+ return isFunctionalInterfaceWithMethod(referenceBinding, scope, samSignature)
+ || isInterfaceHasNoAbstractMethod(referenceBinding, scope);
+ }
+
+ /**
+ * Collect all abstract methods in an interface and its super interfaces.
+ *
+ * In the case of multiple inheritance like this,
+ *
+ * interface I {m();}
+ * interface J {default m();}
+ * interface K extends I, J{}
+ *
+ * the abstract methods of K include m();
+ */
+ private List<MethodBinding> getInterfaceAbstractMethods(ReferenceBinding referenceBinding,
+ Scope scope) {
+ if (!referenceBinding.isInterface() || !referenceBinding.isValidBinding()) {
+ return null;
+ }
+ List<MethodBinding> abstractMethods = Lists.newLinkedList();
+
+ // add all abstract methods from super interfaces.
+ for (ReferenceBinding superInterface : referenceBinding.superInterfaces()) {
+ List<MethodBinding> abstractMethodsFromSupers =
+ getInterfaceAbstractMethods(superInterface, scope);
+ if (abstractMethodsFromSupers != null && abstractMethodsFromSupers.size() > 0) {
+ abstractMethods.addAll(abstractMethodsFromSupers);
+ }
+ }
+ for (MethodBinding method : referenceBinding.methods()) {
+ if (method == null || method.isStatic() || method.redeclaresPublicObjectMethod(scope)) {
+ continue;
+ }
+ // remove the overridden methods in the super interfaces.
+ for (MethodBinding abstractMethodFromSupers : abstractMethods) {
+ if (MethodVerifier.doesMethodOverride(method, abstractMethodFromSupers,
+ scope.environment())) {
+ abstractMethods.remove(abstractMethodFromSupers);
+ }
+ }
+ // add method to abstract methods if it is not a default method.
+ if (!method.isDefaultMethod()) {
+ abstractMethods.add(method);
+ }
+ }
+ return abstractMethods;
+ }
}
static class ClassInfo {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java
index 59b38cb..865985f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java
@@ -81,7 +81,7 @@
public JField get(FieldBinding binding) {
binding = binding.original();
- String key = signature(binding);
+ String key = JdtUtil.signature(binding);
JField sourceField = sourceFields.get(key);
if (sourceField != null) {
assert !sourceField.isExternal();
@@ -98,7 +98,7 @@
public JMethod get(MethodBinding binding) {
binding = binding.original();
- String key = signature(binding);
+ String key = JdtUtil.signature(binding);
JMethod sourceMethod = sourceMethods.get(key);
if (sourceMethod != null) {
assert !sourceMethod.isExternal();
@@ -119,7 +119,7 @@
public JType get(TypeBinding binding) {
binding = binding.erasure();
- String key = signature(binding);
+ String key = JdtUtil.signature(binding);
JReferenceType sourceType = sourceTypes.get(key);
if (sourceType != null) {
@@ -184,17 +184,17 @@
}
public void setField(FieldBinding binding, JField field) {
- String key = signature(binding);
+ String key = JdtUtil.signature(binding);
sourceFields.put(key, field);
}
public void setMethod(MethodBinding binding, JMethod method) {
- String key = signature(binding);
+ String key = JdtUtil.signature(binding);
sourceMethods.put(key, method);
}
public void setSourceType(SourceTypeBinding binding, JDeclaredType type) {
- String key = signature(binding);
+ String key = JdtUtil.signature(binding);
sourceTypes.put(key, type);
}
@@ -350,36 +350,4 @@
types.put(type.getName(), type);
}
}
-
- private String signature(FieldBinding binding) {
- StringBuilder sb = new StringBuilder();
- sb.append(binding.declaringClass.constantPoolName());
- sb.append('.');
- sb.append(binding.name);
- sb.append(':');
- sb.append(binding.type.signature());
- return sb.toString();
- }
-
- private String signature(MethodBinding binding) {
- StringBuilder sb = new StringBuilder();
- sb.append(binding.declaringClass.constantPoolName());
- sb.append('.');
- sb.append(binding.selector);
- sb.append('(');
- for (TypeBinding paramType : binding.parameters) {
- sb.append(paramType.signature());
- }
- sb.append(')');
- sb.append(binding.returnType.signature());
- return sb.toString();
- }
-
- private String signature(TypeBinding binding) {
- if (binding.isBaseType()) {
- return String.valueOf(binding.sourceName());
- } else {
- return String.valueOf(binding.constantPoolName());
- }
- }
}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java
index 7e27e0f..069703b 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/Java8AstTest.java
@@ -486,6 +486,164 @@
formatSource(samMethod.toSource()));
}
+ public void testIntersectionCast() throws Exception {
+ addSnippetClassDecl("static class A {void print() {} }");
+ addSnippetClassDecl("interface I1 {}");
+ addSnippetClassDecl("interface I2 {}");
+ addSnippetClassDecl("interface I3 {}");
+ addSnippetClassDecl("static class B extends A implements I1 {}");
+ addSnippetClassDecl("static class C extends A implements I1, I2, I3 {}");
+ String cast1 = "B b = new B(); ((A & I1) b).print();";
+ assertEqualBlock("EntryPoint$B b=new EntryPoint$B();((EntryPoint$A)(EntryPoint$I1)b).print();",
+ cast1);
+ String cast2 = "C c = new C(); ((A & I1 & I2 & I3)c).print();";
+ assertEqualBlock("EntryPoint$C c=new EntryPoint$C();"
+ + "((EntryPoint$A)(EntryPoint$I1)(EntryPoint$I2)(EntryPoint$I3)c).print();", cast2);
+ }
+
+ public void testIntersectionCastOfLambda() throws Exception {
+ addSnippetClassDecl("interface I1 { public void foo(); }");
+ addSnippetClassDecl("interface I2 { }");
+ String lambda = "Object o = (I2 & I1) () -> {};";
+ assertEqualBlock("Object o=(EntryPoint$I2)(EntryPoint$I1)new EntryPoint$lambda$0$Type();",
+ lambda);
+
+ JProgram program = compileSnippet("void", lambda, false);
+ // created by JDT, should exist
+ assertNotNull(getMethod(program, "lambda$0"));
+
+ // created by GwtAstBuilder
+ JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type");
+ assertNotNull(lambdaInnerClass);
+
+ // no fields
+ assertEquals(0, lambdaInnerClass.getFields().size());
+
+ // should have constructor taking no args
+ JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type");
+ assertTrue(ctor instanceof JConstructor);
+ assertEquals(0, ctor.getParams().size());
+
+ // should implements I1 and I2
+ assertTrue(
+ lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I1")));
+ assertTrue(
+ lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I2")));
+ // should implement foo method
+ JMethod samMethod = findMethod(lambdaInnerClass, "foo");
+ assertEquals("public final void foo(){EntryPoint.lambda$0();}",
+ formatSource(samMethod.toSource()));
+ }
+
+ public void testMultipleIntersectionCastOfLambda() throws Exception {
+ addSnippetClassDecl("interface I1 { public void foo(); }");
+ addSnippetClassDecl("interface I2 { }");
+ addSnippetClassDecl("interface I3 { }");
+ String lambda = "I2 o = (I3 & I2 & I1) () -> {};";
+ assertEqualBlock(
+ "EntryPoint$I2 o=(EntryPoint$I3)(EntryPoint$I2)(EntryPoint$I1)new EntryPoint$lambda$0$Type();",
+ lambda);
+
+ JProgram program = compileSnippet("void", lambda, false);
+ // created by JDT, should exist
+ assertNotNull(getMethod(program, "lambda$0"));
+
+ // created by GwtAstBuilder
+ JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type");
+ assertNotNull(lambdaInnerClass);
+
+ // no fields
+ assertEquals(0, lambdaInnerClass.getFields().size());
+
+ // should have constructor taking no args
+ JMethod ctor = findMethod(lambdaInnerClass, "EntryPoint$lambda$0$Type");
+ assertTrue(ctor instanceof JConstructor);
+ assertEquals(0, ctor.getParams().size());
+
+ // should extends java.lang.Object, implements I1, I2 and I3
+ assertEquals("java.lang.Object", lambdaInnerClass.getSuperClass().getName());
+ assertTrue(
+ lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I1")));
+ assertTrue(
+ lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I2")));
+ assertTrue(
+ lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I3")));
+ // should implement foo method
+ JMethod samMethod = findMethod(lambdaInnerClass, "foo");
+ assertEquals("public final void foo(){EntryPoint.lambda$0();}",
+ formatSource(samMethod.toSource()));
+ }
+
+ public void testIntersectionCastOfLambdaWithClassType() throws Exception {
+ addSnippetClassDecl("interface I1 { public void foo(); }");
+ addSnippetClassDecl("class A { }");
+ String lambda = "Object o = (A & I1) () -> {};";
+ assertEqualBlock("Object o=(EntryPoint$A)(EntryPoint$I1)new EntryPoint$lambda$0$Type();",
+ lambda);
+
+ JProgram program = compileSnippet("void", lambda, false);
+
+ assertNotNull(getMethod(program, "lambda$0"));
+
+ JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type");
+ assertNotNull(lambdaInnerClass);
+ assertEquals("java.lang.Object", lambdaInnerClass.getSuperClass().getName());
+ assertEquals(1, lambdaInnerClass.getImplements().size());
+ assertTrue(
+ lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I1")));
+ // should implement foo method
+ JMethod samMethod = findMethod(lambdaInnerClass, "foo");
+ assertEquals("public final void foo(){EntryPoint.lambda$0();}",
+ formatSource(samMethod.toSource()));
+ }
+
+ public void testIntersectionCastOfLambdaOneAbstractMethod() throws Exception {
+ addSnippetClassDecl("interface I1 { public void foo(); }");
+ addSnippetClassDecl("interface I2 extends I1{ public void foo();}");
+ String lambda = "Object o = (I1 & I2) () -> {};";
+ // (I1 & I2) is resolved to I2 by JDT.
+ assertEqualBlock("Object o=(EntryPoint$I2)new EntryPoint$lambda$0$Type();",
+ lambda);
+
+ JProgram program = compileSnippet("void", lambda, false);
+
+ assertNotNull(getMethod(program, "lambda$0"));
+
+ JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type");
+ assertNotNull(lambdaInnerClass);
+ assertEquals("java.lang.Object", lambdaInnerClass.getSuperClass().getName());
+ assertEquals(1, lambdaInnerClass.getImplements().size()); // only implements I2.
+ assertTrue(
+ lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I2")));
+ // should implement foo method
+ JMethod samMethod = findMethod(lambdaInnerClass, "foo");
+ assertEquals("public final void foo(){EntryPoint.lambda$0();}",
+ formatSource(samMethod.toSource()));
+ }
+
+ public void testIntersectionCastMultipleAbstractMethods() throws Exception {
+ addSnippetClassDecl("interface I1 { public void foo(); }");
+ addSnippetClassDecl("interface I2 { public void bar(); public void fun();}");
+ String lambda = "Object o = (I1 & I2) () -> {};";
+ assertEqualBlock("Object o=(EntryPoint$I1)(EntryPoint$I2)new EntryPoint$lambda$0$Type();",
+ lambda);
+
+ JProgram program = compileSnippet("void", lambda, false);
+
+ assertNotNull(getMethod(program, "lambda$0"));
+
+ JClassType lambdaInnerClass = (JClassType) getType(program, "test.EntryPoint$lambda$0$Type");
+ assertNotNull(lambdaInnerClass);
+ assertEquals("java.lang.Object", lambdaInnerClass.getSuperClass().getName());
+ assertEquals(1, lambdaInnerClass.getImplements().size());
+ assertTrue(
+ lambdaInnerClass.getImplements().contains(program.getFromTypeMap("test.EntryPoint$I1")));
+ // should implement foo method
+ JMethod samMethod = findMethod(lambdaInnerClass, "foo");
+ assertEquals("public final void foo(){EntryPoint.lambda$0();}",
+ formatSource(samMethod.toSource()));
+ }
+
private static final MockJavaResource LAMBDA_METAFACTORY =
JavaResourceBase.createMockJavaResource("java.lang.invoke.LambdaMetafactory",
"package java.lang.invoke;",
diff --git a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
index 391d52a..2dbad11 100644
--- a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -659,4 +659,119 @@
assertEquals("B.n1;I.n;I.m;A.n;A.m;B.m;", outerClass.n1());
assertEquals("B.n2;I.m;A.n;A.m;B.m;B.m;", outerClass.n2());
}
+
+ class EmptyA { }
+ interface EmptyI { }
+ interface EmptyJ { }
+ class EmptyB extends EmptyA implements EmptyI { }
+ class EmptyC extends EmptyA implements EmptyI, EmptyJ { }
+ public void testBaseIntersectionCast() {
+ EmptyA localB = new EmptyB();
+ EmptyA localC = new EmptyC();
+ EmptyB b2BI = (EmptyB & EmptyI) localB;
+ EmptyC c2CIJ = (EmptyC & EmptyI & EmptyJ) localC;
+ EmptyI ii1 = (EmptyB & EmptyI) localB;
+ EmptyI ii2 = (EmptyC & EmptyI) localC;
+ EmptyI ii3 = (EmptyC & EmptyJ) localC;
+ EmptyI ii4 = (EmptyC & EmptyI & EmptyJ) localC;
+ EmptyJ jj1 = (EmptyC & EmptyI & EmptyJ) localC;
+ EmptyJ jj2 = (EmptyC & EmptyI) localC;
+ EmptyJ jj3 = (EmptyC & EmptyJ) localC;
+ EmptyJ jj4 = (EmptyI & EmptyJ) localC;
+
+ try {
+ EmptyC b2CIJ = (EmptyC & EmptyI & EmptyJ) localB;
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // Expected.
+ }
+ try {
+ EmptyB c2BI = (EmptyB & EmptyI) localC;
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // Expected.
+ }
+ try {
+ EmptyJ jj = (EmptyB & EmptyJ) localB;
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // Expected.
+ }
+ }
+
+ interface SimpleI {
+ int fun();
+ }
+ interface SimpleJ {
+ int foo();
+ int bar();
+ }
+ interface SimpleK {
+ }
+ public void testIntersectionCastWithLambdaExpr() {
+ SimpleI simpleI1 = (SimpleI & EmptyI) () -> { return 11; };
+ assertEquals(11, simpleI1.fun());
+ SimpleI simpleI2 = (EmptyI & SimpleI) () -> { return 22; };
+ assertEquals(22, simpleI2.fun());
+ EmptyI emptyI = (EmptyI & SimpleI) () -> { return 33; };
+ try {
+ ((EmptyA & SimpleI) () -> { return 33; }).fun();
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // expected.
+ }
+ try {
+ ((SimpleI & SimpleJ) () -> { return 44; }).fun();
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // expected.
+ }
+ try {
+ ((SimpleI & SimpleJ) () -> { return 44; }).foo();
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // expected.
+ }
+ try {
+ ((SimpleI & SimpleJ) () -> { return 44; }).bar();
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // expected.
+ }
+ assertEquals(55, ((SimpleI & SimpleK) () -> { return 55; }).fun());
+ }
+
+ class SimpleA {
+ public int bar() {
+ return 11;
+ }
+ }
+
+ class SimpleB extends SimpleA implements SimpleI {
+ public int fun() {
+ return 22;
+ }
+ }
+
+ class SimpleC extends SimpleA implements SimpleI {
+ public int fun() {
+ return 33;
+ }
+
+ public int bar() {
+ return 44;
+ }
+ }
+
+ public void testIntersectionCastPolymorphism() {
+ SimpleA bb = new SimpleB();
+ assertEquals(22, ((SimpleB & SimpleI) bb).fun());
+ assertEquals(11, ((SimpleB & SimpleI) bb).bar());
+ SimpleA cc = new SimpleC();
+ assertEquals(33, ((SimpleC & SimpleI) cc).fun());
+ assertEquals(44, ((SimpleC & SimpleI) cc).bar());
+ assertEquals(33, ((SimpleA & SimpleI) cc).fun());
+ SimpleI ii = (SimpleC & SimpleI) cc;
+ assertEquals(33, ii.fun());
+ }
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
index d6428e0..a50dc0f 100644
--- a/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
+++ b/user/test/com/google/gwt/dev/jjs/test/Java8Test.java
@@ -144,4 +144,13 @@
public void testNestedInterfaceClass() {
}
+
+ public void testBaseIntersectionCast() {
+ }
+
+ public void testIntersectionCastWithLambdaExpr() {
+ }
+
+ public void testIntersectionCastPolymorphism() {
+ }
}
\ No newline at end of file