Skip to content

Commit

Permalink
Fix a bug in method reference in Java8.
Browse files Browse the repository at this point in the history
When using method references to methods that have the
same signature but from different classes, wrong method was called.

For example, f(A::id); f(B::id); A and B both have method id()
with the same signature, B::id() is not called correctly.

The reason is that the generated names of inner lambda class were
the same since they have the same method signature, thus only one
lambda class was created for A::id and B::id.

Fix the issue by adding class name to the generated inner class
name to identify different methods.

Bug: issue 9040
Change-Id: Ibc6b12d26d473ab32b821bb2202e78ae92670774
  • Loading branch information
Ye Wang committed Mar 5, 2015
1 parent 39c9e6c commit bb9f41b
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 67 deletions.
61 changes: 14 additions & 47 deletions dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
Expand Up @@ -515,7 +515,7 @@ public boolean visit(JClassType x, Context ctx) {
}

// My seed function name
JsName jsName = topScope.declareName(getNameString(x), x.getShortName());
JsName jsName = topScope.declareName(JjsUtils.getNameString(x), x.getShortName());
names.put(x, jsName);
recordSymbol(x, jsName);

Expand Down Expand Up @@ -3405,11 +3405,6 @@ private GenerateJavaScriptAST(TreeLogger logger, JProgram program, JsProgram jsP
}
}

static String getNameString(HasName hasName) {
String s = hasName.getName().replaceAll("_", "_1").replace('.', '_');
return s;
}

/**
* Retrieves the runtime typeId for {@code type}.
*/
Expand All @@ -3425,12 +3420,13 @@ JExpression getRuntimeTypeReference(JReferenceType type) {
}

String mangleName(JField x) {
String s = getNameString(x.getEnclosingType()) + '_' + getNameString(x);
String s = JjsUtils.getNameString(x.getEnclosingType()) + '_' + JjsUtils.getNameString(x);
return s;
}

String mangleNameForGlobal(JMethod x) {
String s = getNameString(x.getEnclosingType()) + '_' + getNameString(x) + "__";
String s =
JjsUtils.getNameString(x.getEnclosingType()) + '_' + JjsUtils.getNameString(x) + "__";
for (int i = 0; i < x.getOriginalParamTypes().size(); ++i) {
JType type = x.getOriginalParamTypes().get(i);
s += type.getJavahSignatureName();
Expand All @@ -3449,38 +3445,18 @@ String mangleNameForPackagePrivatePoly(JMethod x) {
*/
sb.append("package_private$");
JMethod topDefinition = typeOracle.getTopMostDefinition(x);
sb.append(getNameString(topDefinition.getEnclosingType()));
sb.append("$");
sb.append(getNameString(x));
constructManglingSignature(x, sb);
return StringInterner.get().intern(sb.toString());
}

/**
* 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)
*/
static String classNameForMethodReference(JInterfaceType samInterface, JMethod referredMethod,
boolean haveReceiver) {
StringBuilder sb = new StringBuilder();
sb.append(samInterface.getPackageName());
sb.append('.');
sb.append(samInterface.getShortName());
sb.append(JjsUtils.getNameString(topDefinition.getEnclosingType()));
sb.append("$");
if (!haveReceiver) {
sb.append("$");
}
sb.append(getNameString(referredMethod));
constructManglingSignature(referredMethod, sb);
sb.append(JjsUtils.getNameString(x));
JjsUtils.constructManglingSignature(x, sb);
return StringInterner.get().intern(sb.toString());
}

String mangleNameForPoly(JMethod x) {
assert !x.isPrivate() && !x.isStatic();
StringBuilder sb = new StringBuilder();
sb.append(getNameString(x));
constructManglingSignature(x, sb);
sb.append(JjsUtils.getNameString(x));
JjsUtils.constructManglingSignature(x, sb);
return StringInterner.get().intern(sb.toString());
}

Expand All @@ -3493,31 +3469,22 @@ String mangleNameForPrivatePoly(JMethod x) {
* class name to the mangled name.
*/
sb.append("private$");
sb.append(getNameString(x.getEnclosingType()));
sb.append(JjsUtils.getNameString(x.getEnclosingType()));
sb.append("$");
sb.append(getNameString(x));
constructManglingSignature(x, sb);
sb.append(JjsUtils.getNameString(x));
JjsUtils.constructManglingSignature(x, sb);
return StringInterner.get().intern(sb.toString());
}

String mangleNameForJsProperty(JMethod x) {
assert x.isJsProperty();
StringBuilder sb = new StringBuilder();
sb.append("jsproperty$");
sb.append(getNameString(x));
constructManglingSignature(x, sb);
sb.append(JjsUtils.getNameString(x));
JjsUtils.constructManglingSignature(x, sb);
return StringInterner.get().intern(sb.toString());
}

private static void constructManglingSignature(JMethod x, StringBuilder partialSignature) {
partialSignature.append("__");
for (int i = 0; i < x.getOriginalParamTypes().size(); ++i) {
JType type = x.getOriginalParamTypes().get(i);
partialSignature.append(type.getJavahSignatureName());
}
partialSignature.append(x.getOriginalReturnType().getJavahSignatureName());
}

String mangleNameSpecialObfuscate(JField x) {
assert (specialObfuscatedFields.containsKey(x));
switch (output) {
Expand Down
24 changes: 23 additions & 1 deletion dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
Expand Up @@ -1745,7 +1745,7 @@ public void endVisit(ReferenceExpression x, BlockScope blockScope) {
}

// Constructors and overloading mean we need generate unique names
String lambdaName = GenerateJavaScriptAST.classNameForMethodReference(funcType,
String lambdaName = classNameForMethodReference(funcType,
referredMethod,
haveReceiver);

Expand Down Expand Up @@ -1922,6 +1922,28 @@ public void endVisit(ReferenceExpression x, BlockScope blockScope) {
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 samInterface, JMethod referredMethod,
boolean haveReceiver) {
StringBuilder sb = new StringBuilder();
sb.append(samInterface.getPackageName());
sb.append('.');
sb.append(samInterface.getShortName());
sb.append("$");
if (!haveReceiver) {
sb.append("$");
}
sb.append(referredMethod.getEnclosingType().getName().replace('.', '$'));
sb.append("$");
sb.append(JjsUtils.getNameString(referredMethod));
JjsUtils.constructManglingSignature(referredMethod, sb);
return StringInterner.get().intern(sb.toString());
}

private JExpression boxOrUnboxExpression(JExpression expr, TypeBinding fromType,
TypeBinding toType) {
if (fromType == TypeBinding.VOID || toType == TypeBinding.VOID) {
Expand Down
16 changes: 16 additions & 0 deletions dev/core/src/com/google/gwt/dev/jjs/impl/JjsUtils.java
Expand Up @@ -18,6 +18,7 @@
import com.google.gwt.dev.PrecompileTaskOptions;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.HasName;
import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
import com.google.gwt.dev.jjs.ast.JCharLiteral;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
Expand All @@ -37,6 +38,7 @@
import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JThisRef;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
import com.google.gwt.dev.js.ast.JsBooleanLiteral;
import com.google.gwt.dev.js.ast.JsExpression;
Expand Down Expand Up @@ -174,6 +176,20 @@ public static void createForwardingMethod(JDeclaredType classToBeModified,
delegate.addOverriddenMethod(methodToDelegate);
}

public static void constructManglingSignature(JMethod x, StringBuilder partialSignature) {
partialSignature.append("__");
for (int i = 0; i < x.getOriginalParamTypes().size(); ++i) {
JType type = x.getOriginalParamTypes().get(i);
partialSignature.append(type.getJavahSignatureName());
}
partialSignature.append(x.getOriginalReturnType().getJavahSignatureName());
}

public static String getNameString(HasName hasName) {
String s = hasName.getName().replaceAll("_", "_1").replace('.', '_');
return s;
}

private enum LiteralTranslators {
BOOLEAN_LITERAL_TRANSLATOR() {
@Override
Expand Down

0 comments on commit bb9f41b

Please sign in to comment.