Make each method reference be its own different lambda. Even method references to same method (with or without explicit qualifier) might need different impelementations due to generics. Instead of attempting to reuse, this patch takes the simplest approach, which is to consider each method reference a different implementation in the same way lambdas are treated. A future optimization could be introduced to deduplicate lambdas and that would not only optimize member references. Bug: #9333 Bug-Link: http://github.com/gwtproject/gwt/issues/9333 Change-Id: Ifef2bb703ad4172977e965f786a0807bdccd5f7b
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 4044774..fa7c8a0 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
@@ -1702,7 +1702,7 @@ } } - private Map<String, JClassType> lambdaNameToInnerLambdaType = Maps.newHashMap(); + private int nextReferenceExpressionId = 0; @Override public void endVisit(ReferenceExpression x, BlockScope blockScope) { @@ -1756,162 +1756,159 @@ JMethod referredMethod = typeMap.get(referredMethodBinding); boolean hasQualifier = hasQualifier(x); - // Constructors and overloading means we need generate unique names - String lambdaName = classNameForMethodReference(funcType, - referredMethod, - hasQualifier); + // Constructors, overloading and generics means that the safest approach is to consider + // each different member reference as a different lambda implementation. + String lambdaName = JdtUtil.getClassName(curClass.typeDecl.binding) + "$" + + String.valueOf(nextReferenceExpressionId++) + "methodref$" + + (x.binding.isConstructor() ? "ctor" : String.valueOf(x.binding.selector)); List<JExpression> enclosingThisRefs = Lists.newArrayList(); // Create an inner class to hold the implementation of the interface - JClassType innerLambdaClass = lambdaNameToInnerLambdaType.get(lambdaName); - if (innerLambdaClass == null) { - innerLambdaClass = createInnerClass(lambdaName, x, info, funcType); - lambdaNameToInnerLambdaType.put(lambdaName, innerLambdaClass); - newTypes.add(innerLambdaClass); + JClassType innerLambdaClass = createInnerClass(lambdaName, x, info, funcType); + newTypes.add(innerLambdaClass); - JConstructor ctor = new JConstructor(info, innerLambdaClass, AccessModifier.PRIVATE); + JConstructor ctor = new JConstructor(info, innerLambdaClass, AccessModifier.PRIVATE); - JMethodBody ctorBody = new JMethodBody(info); - JThisRef thisRef = new JThisRef(info, innerLambdaClass); - JExpression instance = null; + JMethodBody ctorBody = new JMethodBody(info); + JThisRef thisRef = new JThisRef(info, innerLambdaClass); + JExpression instance = null; - List<JField> enclosingInstanceFields = new ArrayList<JField>(); - // If we have a qualifier instance, we have to stash it in the constructor - if (hasQualifier) { - // this.$$outer = $$outer - JField outerField = createAndBindCapturedLambdaParameter(info, OUTER_LAMBDA_PARAM_NAME, - innerLambdaClass.getEnclosingType(), ctor, ctorBody); - instance = new JFieldRef(info, - new JThisRef(info, innerLambdaClass), outerField, innerLambdaClass); - } else if (referredMethod instanceof JConstructor) { - // the method we are invoking is a constructor and may need enclosing instances passed to - // it. - // For example, an class Foo { class Inner { Inner(int x) { } } } needs - // it's constructor invoked with an enclosing instance, Inner::new - // Java8 doesn't allow the qualifified case, e.g. x.new Foo() -> x.Foo::new - ReferenceBinding targetBinding = referredMethodBinding.declaringClass; - if (JdtUtil.isInnerClass(targetBinding)) { - for (ReferenceBinding argType : targetBinding.syntheticEnclosingInstanceTypes()) { - argType = (ReferenceBinding) argType.erasure(); - JExpression enclosingThisRef = resolveThisReference(info, argType, false, blockScope); - JField enclosingInstance = createAndBindCapturedLambdaParameter(info, - String.valueOf(argType.readableName()).replace('.', '_'), - enclosingThisRef.getType(), ctor, ctorBody); - enclosingInstanceFields.add(enclosingInstance); - enclosingThisRefs.add(enclosingThisRef); - } + List<JField> enclosingInstanceFields = new ArrayList<JField>(); + // If we have a qualifier instance, we have to stash it in the constructor + if (hasQualifier) { + // this.$$outer = $$outer + JField outerField = createAndBindCapturedLambdaParameter(info, OUTER_LAMBDA_PARAM_NAME, + innerLambdaClass.getEnclosingType(), ctor, ctorBody); + instance = new JFieldRef(info, + new JThisRef(info, innerLambdaClass), outerField, innerLambdaClass); + } else if (referredMethod instanceof JConstructor) { + // the method we are invoking is a constructor and may need enclosing instances passed to + // it. + // For example, an class Foo { class Inner { Inner(int x) { } } } needs + // it's constructor invoked with an enclosing instance, Inner::new + // Java8 doesn't allow the qualifified case, e.g. x.new Foo() -> x.Foo::new + ReferenceBinding targetBinding = referredMethodBinding.declaringClass; + if (JdtUtil.isInnerClass(targetBinding)) { + for (ReferenceBinding argType : targetBinding.syntheticEnclosingInstanceTypes()) { + argType = (ReferenceBinding) argType.erasure(); + JExpression enclosingThisRef = resolveThisReference(info, argType, false, blockScope); + JField enclosingInstance = createAndBindCapturedLambdaParameter(info, + String.valueOf(argType.readableName()).replace('.', '_'), + enclosingThisRef.getType(), ctor, ctorBody); + enclosingInstanceFields.add(enclosingInstance); + enclosingThisRefs.add(enclosingThisRef); } } - ctor.setBody(ctorBody); - innerLambdaClass.addMethod(ctor); - - // Create an implementation of the target interface that invokes the method referred to - // void onClick(ClickEvent e) { outer.referredMethod(e); } - JMethod samMethod = new JMethod(info, interfaceMethod.getName(), - innerLambdaClass, interfaceMethod.getType(), - false, false, true, interfaceMethod.getAccess()); - for (JParameter origParam : interfaceMethod.getParams()) { - samMethod.cloneParameter(origParam); - } - JMethodBody samMethodBody = new JMethodBody(info); - - Iterator<JParameter> paramIt = samMethod.getParams().iterator(); - // here's where it gets tricky. A method can have an implicit qualifier, e.g. - // String::compareToIgnoreCase, it's non-static, it only has one argument, but it binds to - // Comparator<T>. - // The first argument serves as the qualifier, so for example, the method dispatch looks - // like this: int compare(T a, T b) { a.compareTo(b); } - if (!hasQualifier && !referredMethod.isStatic() && instance == null && - samMethod.getParams().size() == referredMethod.getParams().size() + 1) { - // the instance qualifier is the first parameter in this case. - // Needs to be cast the actual type due to generics. - instance = new JCastOperation(info, typeMap.get(referredMethodBinding.declaringClass), - paramIt.next().makeRef(info)); - } - JMethodCall samCall = null; - - if (referredMethod.isConstructor()) { - // Constructors must be invoked with JNewInstance - samCall = new JNewInstance(info, (JConstructor) referredMethod); - for (JField enclosingInstance : enclosingInstanceFields) { - samCall.addArg(new JFieldRef(enclosingInstance.getSourceInfo(), thisRef, - enclosingInstance, innerLambdaClass)); - } - } else { - // For static methods, instance will be null - samCall = new JMethodCall(info, instance, referredMethod); - // if super::method, we need static dispatch - if (x.lhs instanceof SuperReference) { - samCall.setStaticDispatchOnly(); - } - } - - // Add the rest of the parameters from the interface method to methodcall - // boxing or unboxing and dealing with varargs - int paramNumber = 0; - - // need to build up an array of passed parameters if we have varargs - List<JExpression> varArgInitializers = null; - int varArg = referredMethodBinding.parameters.length - 1; - - // interface Foo { m(int x, int y); } bound to reference foo(int... args) - // if varargs and incoming param is not already a var-arg, we'll need to convert - // trailing args of the target interface into an array - if (referredMethodBinding.isVarargs() && !samBinding.parameters[varArg].isArrayType()) { - varArgInitializers = Lists.newArrayList(); - } - - while (paramIt.hasNext()) { - JParameter param = paramIt.next(); - JExpression paramExpr = param.makeRef(info); - // params may need to be boxed or unboxed - TypeBinding destParam = null; - // The method declared in the functional interface might have more parameters than the - // method referred by the method reference. In the case of an instance method without - // an explicit qualifier (A::m vs instance::m) the method in the functional interface will - // have an additional parameter for the instance preceding all the method parameters. - TypeBinding samParameterBinding = - samBinding.parameters[paramNumber - + (samBinding.parameters.length - referredMethodBinding.parameters.length)]; - // if it is not the trailing param or varargs, or interface method is already varargs - if (varArgInitializers == null || !referredMethodBinding.isVarargs() || (paramNumber < varArg)) { - destParam = referredMethodBinding.parameters[paramNumber]; - paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam); - samCall.addArg(paramExpr); - } else if (!samParameterBinding.isArrayType()) { - // else add trailing parameters to var-args initializer list for an array - destParam = referredMethodBinding.parameters[varArg].leafComponentType(); - paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam); - varArgInitializers.add(paramExpr); - } - paramNumber++; - } - - // add trailing new T[] { initializers } var-arg array - if (varArgInitializers != null) { - JArrayType lastParamType = - (JArrayType) typeMap.get( - referredMethodBinding.parameters[referredMethodBinding.parameters.length - 1]); - JNewArray newArray = - JNewArray.createArrayWithInitializers(info, lastParamType, varArgInitializers); - samCall.addArg(newArray); - } - - // TODO(rluble): Make this a call to JjsUtils.makeMethodEndStatement once boxing/unboxing - // is handled there. - if (samMethod.getType() != JPrimitiveType.VOID) { - JExpression samExpression = boxOrUnboxExpression(samCall, referredMethodBinding.returnType, - samBinding.returnType); - samMethodBody.getBlock().addStmt(simplify(samExpression, x).makeReturnStatement()); - } else { - samMethodBody.getBlock().addStmt(samCall.makeStatement()); - } - samMethod.setBody(samMethodBody); - innerLambdaClass.addMethod(samMethod); - ctor.freezeParamTypes(); - samMethod.freezeParamTypes(); } + ctor.setBody(ctorBody); + innerLambdaClass.addMethod(ctor); + + // Create an implementation of the target interface that invokes the method referred to + // void onClick(ClickEvent e) { outer.referredMethod(e); } + JMethod samMethod = new JMethod(info, interfaceMethod.getName(), + innerLambdaClass, interfaceMethod.getType(), + false, false, true, interfaceMethod.getAccess()); + for (JParameter origParam : interfaceMethod.getParams()) { + samMethod.cloneParameter(origParam); + } + JMethodBody samMethodBody = new JMethodBody(info); + + Iterator<JParameter> paramIt = samMethod.getParams().iterator(); + // here's where it gets tricky. A method can have an implicit qualifier, e.g. + // String::compareToIgnoreCase, it's non-static, it only has one argument, but it binds to + // Comparator<T>. + // The first argument serves as the qualifier, so for example, the method dispatch looks + // like this: int compare(T a, T b) { a.compareTo(b); } + if (!hasQualifier && !referredMethod.isStatic() && instance == null && + samMethod.getParams().size() == referredMethod.getParams().size() + 1) { + // the instance qualifier is the first parameter in this case. + // Needs to be cast the actual type due to generics. + instance = new JCastOperation(info, typeMap.get(referredMethodBinding.declaringClass), + paramIt.next().makeRef(info)); + } + JMethodCall samCall = null; + + if (referredMethod.isConstructor()) { + // Constructors must be invoked with JNewInstance + samCall = new JNewInstance(info, (JConstructor) referredMethod); + for (JField enclosingInstance : enclosingInstanceFields) { + samCall.addArg(new JFieldRef(enclosingInstance.getSourceInfo(), thisRef, + enclosingInstance, innerLambdaClass)); + } + } else { + // For static methods, instance will be null + samCall = new JMethodCall(info, instance, referredMethod); + // if super::method, we need static dispatch + if (x.lhs instanceof SuperReference) { + samCall.setStaticDispatchOnly(); + } + } + + // Add the rest of the parameters from the interface method to methodcall + // boxing or unboxing and dealing with varargs + int paramNumber = 0; + + // need to build up an array of passed parameters if we have varargs + List<JExpression> varArgInitializers = null; + int varArg = referredMethodBinding.parameters.length - 1; + + // interface Foo { m(int x, int y); } bound to reference foo(int... args) + // if varargs and incoming param is not already a var-arg, we'll need to convert + // trailing args of the target interface into an array + if (referredMethodBinding.isVarargs() && !samBinding.parameters[varArg].isArrayType()) { + varArgInitializers = Lists.newArrayList(); + } + + while (paramIt.hasNext()) { + JParameter param = paramIt.next(); + JExpression paramExpr = param.makeRef(info); + // params may need to be boxed or unboxed + TypeBinding destParam = null; + // The method declared in the functional interface might have more parameters than the + // method referred by the method reference. In the case of an instance method without + // an explicit qualifier (A::m vs instance::m) the method in the functional interface will + // have an additional parameter for the instance preceding all the method parameters. + TypeBinding samParameterBinding = + samBinding.parameters[paramNumber + + (samBinding.parameters.length - referredMethodBinding.parameters.length)]; + // if it is not the trailing param or varargs, or interface method is already varargs + if (varArgInitializers == null || !referredMethodBinding.isVarargs() || (paramNumber < varArg)) { + destParam = referredMethodBinding.parameters[paramNumber]; + paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam); + samCall.addArg(paramExpr); + } else if (!samParameterBinding.isArrayType()) { + // else add trailing parameters to var-args initializer list for an array + destParam = referredMethodBinding.parameters[varArg].leafComponentType(); + paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam); + varArgInitializers.add(paramExpr); + } + paramNumber++; + } + + // add trailing new T[] { initializers } var-arg array + if (varArgInitializers != null) { + JArrayType lastParamType = + (JArrayType) typeMap.get( + referredMethodBinding.parameters[referredMethodBinding.parameters.length - 1]); + JNewArray newArray = + JNewArray.createArrayWithInitializers(info, lastParamType, varArgInitializers); + samCall.addArg(newArray); + } + + // TODO(rluble): Make this a call to JjsUtils.makeMethodEndStatement once boxing/unboxing + // is handled there. + if (samMethod.getType() != JPrimitiveType.VOID) { + JExpression samExpression = boxOrUnboxExpression(samCall, referredMethodBinding.returnType, + samBinding.returnType); + samMethodBody.getBlock().addStmt(simplify(samExpression, x).makeReturnStatement()); + } else { + samMethodBody.getBlock().addStmt(samCall.makeStatement()); + } + samMethod.setBody(samMethodBody); + innerLambdaClass.addMethod(samMethod); + ctor.freezeParamTypes(); + samMethod.freezeParamTypes(); JConstructor lambdaCtor = null; for (JMethod method : innerLambdaClass.getMethods()) { @@ -1941,18 +1938,6 @@ push(allocLambda); } - /** - * Java8 Method References such as String::equalsIgnoreCase should produce inner class names - * that are a function of the samInterface (e.g. Runnable), the method being referred to, - * and the qualifying disposition (this::foo vs Class::foo if foo is an instance method) - */ - private String classNameForMethodReference( - JInterfaceType functionalInterface, JMethod referredMethod, boolean hasReceiver) { - - return JjsUtils.classNameForMethodReference(typeMap.get(curCud.cud.types[0].binding), - functionalInterface, referredMethod, hasReceiver); - } - private JExpression boxOrUnboxExpression(JExpression expr, TypeBinding fromType, TypeBinding toType) { if (fromType == TypeBinding.VOID || toType == TypeBinding.VOID) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java index ee98dfa..27f6879 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
@@ -61,9 +61,7 @@ import com.google.gwt.dev.js.ast.JsNumberLiteral; import com.google.gwt.dev.js.ast.JsObjectLiteral; import com.google.gwt.dev.js.ast.JsStringLiteral; -import com.google.gwt.dev.util.StringInterner; import com.google.gwt.lang.LongLib; -import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting; import com.google.gwt.thirdparty.guava.common.base.Function; import com.google.gwt.thirdparty.guava.common.base.Joiner; import com.google.gwt.thirdparty.guava.common.base.Predicate; @@ -97,42 +95,6 @@ return javahSignatureName + "_classLit"; } - /** - * Java8 Method References such as String::equalsIgnoreCase should produce inner class names - * that are a function of the samInterface (e.g. Runnable), the method being referred to, - * and the qualifying disposition (this::foo vs Class::foo if foo is an instance method) - */ - public static String classNameForMethodReference(JType cuType, - JInterfaceType functionalInterface, JMethod referredMethod, boolean hasReceiver) { - String prefix = classNamePrefixForMethodReference(cuType.getPackageName(), cuType.getName(), - functionalInterface.getName(), referredMethod.getEnclosingType().getName(), - referredMethod.getName(), hasReceiver); - - return StringInterner.get().intern( - constructManglingSignature(referredMethod, prefix)); - } - - /** - * Java8 Method References such as String::equalsIgnoreCase should produce inner class names - * that are a function of the samInterface (e.g. Runnable), the method being referred to, - * and the qualifying disposition (this::foo vs Class::foo if foo is an instance method) - */ - @VisibleForTesting - static String classNamePrefixForMethodReference(String packageName, String cuTypeName, - String functionalInterfaceName, String referredMethodEnclosingClassName, - String referredMethodName, boolean hasReceiver) { - return packageName + "." + Joiner.on("$$").join( - // Make sure references to the same methods in different compilation units do not create - // inner classses with the same name. - mangledNameString(cuTypeName), - "__", - mangledNameString(functionalInterfaceName), - "__", - hasReceiver ? "instance" : "static", - mangledNameString(referredMethodEnclosingClassName), - mangledNameString(referredMethodName)); - } - public static boolean closureStyleLiteralsNeeded(boolean incremental, boolean closureOutputFormat) { return !incremental && closureOutputFormat;
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/GwtAstBuilderTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/GwtAstBuilderTest.java index a061374..79728e7 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/GwtAstBuilderTest.java +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/GwtAstBuilderTest.java
@@ -217,13 +217,9 @@ JDeclaredType lambdaNested = program.getFromTypeMap("test.NestedClasses$lambda$0$Type"); assertEquals(JDeclaredType.NestedClassDisposition.LAMBDA, lambdaNested.getClassDisposition()); - String generatedInnnerClassNameForMethodReferenceLambda = - JjsUtils.classNamePrefixForMethodReference("test", "test.NestedClasses", - "test.NestedClasses$Lambda", "test.NestedClasses", "referencedMethod", false) + - "__V$Type"; JDeclaredType referenceNested = - program.getFromTypeMap(generatedInnnerClassNameForMethodReferenceLambda); + program.getFromTypeMap("test.NestedClasses$0methodref$referencedMethod$Type"); assertEquals(JDeclaredType.NestedClassDisposition.LAMBDA, referenceNested.getClassDisposition());
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 90f3728..23c6d6f 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
@@ -619,9 +619,7 @@ addSnippetClassDecl("public static Integer foo(int x, int y) { return x + y; }"); String lambda = "new AcceptsLambda<Integer>().accept(EntryPoint::foo);"; - String generatedInnerClassName = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", "test.Lambda", - "test.EntryPoint", "foo", false) + "__IILjava_lang_Integer_2$Type"; + String generatedInnerClassName = "test.EntryPoint$0methodref$foo$Type"; String simpleLambdaInnerClassName = generatedInnerClassName.substring("test.".length()); assertEqualBlock( @@ -658,9 +656,7 @@ addSnippetClassDecl("public Integer foo(int x, int y) { return x + y; }"); String lambda = "new AcceptsLambda<Integer>().accept(this::foo);"; - String generatedInnerClassName = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", "test.Lambda", - "test.EntryPoint", "foo", true) + "__IILjava_lang_Integer_2$Type"; + String generatedInnerClassName = "test.EntryPoint$0methodref$foo$Type"; String simpleLambdaInnerClassName = generatedInnerClassName.substring("test.".length()); assertEqualBlock( @@ -704,14 +700,13 @@ "new AcceptsLambda<Integer>().accept(instance1::fooInstance);\n" + "new AcceptsLambda<Integer>().accept(instance2::fooInstance);"; - String generatedInnerClassName = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", "test.Lambda", - "test.Pojo", "fooInstance", true) + "__IILjava_lang_Integer_2$Type"; - String simpleLambdaInnerClassName = generatedInnerClassName.substring("test.".length()); + String generatedInnerClassName = "test.EntryPoint$0methodref$fooInstance$Type"; + String simpleLambdaInnerClassName1 = generatedInnerClassName.substring("test.".length()); + String simpleLambdaInnerClassName2 = "EntryPoint$1methodref$fooInstance$Type"; assertEqualBlock( - "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName + "(this.instance1));\n" - + "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName + "(this.instance2));", + "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName1 + "(this.instance1));\n" + + "(new AcceptsLambda()).accept(new " + simpleLambdaInnerClassName2 + "(this.instance2));", reference); JProgram program = compileSnippet("void", reference, false); @@ -721,7 +716,7 @@ assertEquals(1, Collections.frequency(program.getDeclaredTypes(), lambdaInnerClass)); // should have constructor taking the instance - JMethod ctor = findMethod(lambdaInnerClass, simpleLambdaInnerClassName); + JMethod ctor = findMethod(lambdaInnerClass, simpleLambdaInnerClassName1); assertTrue(ctor instanceof JConstructor); // instance capture assertEquals(1, ctor.getParams().size()); @@ -771,17 +766,11 @@ "f(b::getId);" ); - String generatedInnerClassNameForA = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", - "test.EntryPoint$Function", "test.EntryPoint$TestMF_A", "getId", true) + - "__Ljava_lang_String_2$Type"; + String generatedInnerClassNameForA = "test.EntryPoint$0methodref$getId$Type"; String simpleLambdaInnerClassNameForA = generatedInnerClassNameForA.substring("test.".length()); - String generatedInnerClassNameForB = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", - "test.EntryPoint$Function", "test.EntryPoint$TestMF_B", "getId", true) + - "__Ljava_lang_String_2$Type"; + String generatedInnerClassNameForB = "test.EntryPoint$1methodref$getId$Type"; String simpleLambdaInnerClassNameForB = generatedInnerClassNameForB.substring("test.".length()); @@ -859,17 +848,11 @@ " }"); String reference = "f(TestMF_A::getId);\n" + "f(TestMF_B::getId);"; - String generatedInnerClassNameForA = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", - "test.EntryPoint$Function", "test.EntryPoint$TestMF_A", "getId", false) + - "__Ljava_lang_String_2$Type"; + String generatedInnerClassNameForA = "test.EntryPoint$0methodref$getId$Type"; String simpleLambdaInnerClassNameForA = generatedInnerClassNameForA.substring("test.".length()); - String generatedInnerClassNameForB = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", - "test.EntryPoint$Function", "test.EntryPoint$TestMF_B", "getId", false) + - "__Ljava_lang_String_2$Type"; + String generatedInnerClassNameForB = "test.EntryPoint$1methodref$getId$Type"; String simpleLambdaInnerClassNameForB = generatedInnerClassNameForB.substring("test.".length()); @@ -904,10 +887,7 @@ public void testCompileImplicitQualifierReferenceBinding() throws Exception { String lambda = "new AcceptsLambda<String>().accept2(String::equalsIgnoreCase);"; - String generatedInnerClassName = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", - "test.Lambda2", "java.lang.String", "equalsIgnoreCase", false) + - "__Ljava_lang_String_2Z$Type"; + String generatedInnerClassName = "test.EntryPoint$0methodref$equalsIgnoreCase$Type"; String simpleLambdaInnerClassName = generatedInnerClassName.substring("test.".length()); @@ -944,9 +924,7 @@ public void testCompileConstructorReferenceBinding() throws Exception { String lambda = "new AcceptsLambda<Pojo>().accept(Pojo::new);"; - String generatedInnerClassName = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", - "test.Lambda", "test.Pojo", "Pojo", false) + "__IIV$Type"; + String generatedInnerClassName = "test.EntryPoint$0methodref$ctor$Type"; String simpleLambdaInnerClassName = generatedInnerClassName.substring("test.".length()); @@ -995,10 +973,7 @@ String lambda = "new AcceptsLambda<Pojo2>().accept(Pojo2::new);"; - String generatedInnerClassName = - JjsUtils.classNamePrefixForMethodReference("test", "test.EntryPoint", - "test.Lambda", "test.EntryPoint$Pojo2", "EntryPoint$Pojo2", false) + - "__Ltest_EntryPoint_2IIV$Type"; + String generatedInnerClassName = "test.EntryPoint$0methodref$ctor$Type"; String simpleLambdaInnerClassName = generatedInnerClassName.substring("test.".length()); @@ -1260,24 +1235,6 @@ compileSnippetToJS(entryPointClass); } - public void testMangledMethodReferencePrefixTest() { - String compilationUnitPackage = "a.b"; - String compilationUnitMainType = compilationUnitPackage + ".SomeEnclosingClass"; - String functionalInterface = "d.e.SomeFunctionalInterface"; - String methodReferenceClass = "f.g.SomeClass"; - String methodName = "foo"; - - assertEquals("a.b.a_b_SomeEnclosingClass$$__$$d_e_SomeFunctionalInterface$$" + - "__$$instance$$f_g_SomeClass$$foo", - JjsUtils.classNamePrefixForMethodReference(compilationUnitPackage, - compilationUnitMainType, functionalInterface, methodReferenceClass, methodName, true)); - - assertEquals("a.b.a_b_SomeEnclosingClass$$__$$d_e_SomeFunctionalInterface$$" + - "__$$static$$f_g_SomeClass$$foo", - JjsUtils.classNamePrefixForMethodReference(compilationUnitPackage, - compilationUnitMainType, functionalInterface, methodReferenceClass, methodName, false)); - } - @Override protected void optimizeJava() { }
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 45f38de..91a9183 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
@@ -1314,10 +1314,20 @@ return true; } } + private static <T> String getClassName(T obj) { + return obj.getClass().getSimpleName(); + } public void testMethodReference_generics() { P<B> p = B::getTrue; assertTrue(p.apply(new B())); + // The next two method references must result in two different lambda implementations due + // to generics, see bug # 9333. + MyFunction1<B, String> f1 = Java8Test::getClassName; + MyFunction1<Double, String> f2 = Java8Test::getClassName; + + assertEquals(B.class.getSimpleName(), f1.apply(new B())); + assertEquals(Double.class.getSimpleName(), f2.apply(new Double(2))); } public void testDefaultMethod_staticInitializer() {