Skip to content

Commit

Permalink
Implement abstract method bridges for functional expressions.
Browse files Browse the repository at this point in the history
The synthetic inner class that implements lambdas and method references
might need bridges for the single abstract method (and also for
default methods).

This patch creates the bridge methods for the single abstract method,
leaving the task of creating bridges for the default methods for a
follow up patch.

Bug: #9406
Bug-Link: #9406
Change-Id: I80bb32b3f81e08a1f583ed78f1765df8cdd21caf
  • Loading branch information
rluble committed Aug 26, 2016
1 parent 15bd4aa commit 7da4bf3
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 25 deletions.
104 changes: 79 additions & 25 deletions dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
Expand Up @@ -1255,13 +1255,30 @@ public void endVisit(LambdaExpression x, BlockScope blockScope) {
ctor.freezeParamTypes(); ctor.freezeParamTypes();
samMethod.freezeParamTypes(); samMethod.freezeParamTypes();


// Create necessary bridges.
createFunctionalExpressionBridges(innerLambdaClass, x, samMethod);

// replace (x,y,z) -> expr with 'new Lambda(args)' // replace (x,y,z) -> expr with 'new Lambda(args)'
replaceLambdaWithInnerClassAllocation(x, info, innerLambdaClass, ctor, synthArgs); replaceLambdaWithInnerClassAllocation(x, info, innerLambdaClass, ctor, synthArgs);
popMethodInfo(); popMethodInfo();
// Add the newly generated type // Add the newly generated type
newTypes.add(innerLambdaClass); newTypes.add(innerLambdaClass);
} }


private void createFunctionalExpressionBridges(
JClassType functionalExpressionImplementationClass,
FunctionalExpression functionalExpression,
JMethod functionalInterfaceAbstractMethod) {
// TODO(rluble): create bridges that might be needed for default methods.
if (functionalExpression.getRequiredBridges() != null) {
for (MethodBinding methodBinding : functionalExpression.getRequiredBridges()) {
// Create bridges.
createBridgeMethod(functionalExpressionImplementationClass, methodBinding,
functionalInterfaceAbstractMethod);
}
}
}

private void createLambdaSamMethod(LambdaExpression x, JMethod interfaceMethod, SourceInfo info, private void createLambdaSamMethod(LambdaExpression x, JMethod interfaceMethod, SourceInfo info,
JClassType innerLambdaClass, List<JField> locals, JField outerField, JMethod lambdaMethod, JClassType innerLambdaClass, List<JField> locals, JField outerField, JMethod lambdaMethod,
JMethod samMethod) { JMethod samMethod) {
Expand Down Expand Up @@ -1411,6 +1428,7 @@ public boolean apply(Entry<SyntheticArgumentBinding, JField> entry) {
assert capturedLocalReference != null; assert capturedLocalReference != null;
allocLambda.addArg(capturedLocalReference); allocLambda.addArg(capturedLocalReference);
} }

// put the result on the stack, and pop out synthetic method from the scope // put the result on the stack, and pop out synthetic method from the scope
push(allocLambda); push(allocLambda);
} }
Expand Down Expand Up @@ -1883,7 +1901,9 @@ public void endVisit(ReferenceExpression x, BlockScope blockScope) {
samBinding.parameters[paramNumber samBinding.parameters[paramNumber
+ (samBinding.parameters.length - referredMethodBinding.parameters.length)]; + (samBinding.parameters.length - referredMethodBinding.parameters.length)];
// if it is not the trailing param or varargs, or interface method is already varargs // if it is not the trailing param or varargs, or interface method is already varargs
if (varArgInitializers == null || !referredMethodBinding.isVarargs() || (paramNumber < varArg)) { if (varArgInitializers == null
|| !referredMethodBinding.isVarargs()
|| (paramNumber < varArg)) {
destParam = referredMethodBinding.parameters[paramNumber]; destParam = referredMethodBinding.parameters[paramNumber];
paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam); paramExpr = boxOrUnboxExpression(paramExpr, samParameterBinding, destParam);
samCall.addArg(paramExpr); samCall.addArg(paramExpr);
Expand Down Expand Up @@ -1920,6 +1940,8 @@ public void endVisit(ReferenceExpression x, BlockScope blockScope) {
ctor.freezeParamTypes(); ctor.freezeParamTypes();
samMethod.freezeParamTypes(); samMethod.freezeParamTypes();


createFunctionalExpressionBridges(innerLambdaClass, x, samMethod);

JConstructor lambdaCtor = null; JConstructor lambdaCtor = null;
for (JMethod method : innerLambdaClass.getMethods()) { for (JMethod method : innerLambdaClass.getMethods()) {
if (method instanceof JConstructor) { if (method instanceof JConstructor) {
Expand Down Expand Up @@ -2703,15 +2725,16 @@ protected boolean visit(TypeDeclaration x) {
* have been installed on the GWT types. * have been installed on the GWT types.
* </p> * </p>
*/ */
private void addBridgeMethods(SourceTypeBinding clazzBinding) { private void addBridgeMethods(SourceTypeBinding classBinding) {
/* /*
* JDT adds bridge methods in all the places GWT needs them. Use JDT's * JDT adds bridge methods in all the places GWT needs them. Use JDT's
* bridge methods. * bridge methods.
*/ */
if (clazzBinding.syntheticMethods() != null) { if (classBinding.syntheticMethods() != null) {
for (SyntheticMethodBinding synthmeth : clazzBinding.syntheticMethods()) { for (SyntheticMethodBinding syntheticMethodBinding : classBinding.syntheticMethods()) {
if (synthmeth.purpose == SyntheticMethodBinding.BridgeMethod && !synthmeth.isStatic()) { if (syntheticMethodBinding.purpose == SyntheticMethodBinding.BridgeMethod
createBridgeMethod(synthmeth); && !syntheticMethodBinding.isStatic()) {
createBridgeMethod(syntheticMethodBinding);
} }
} }
} }
Expand Down Expand Up @@ -2748,35 +2771,65 @@ private JExpression box(JExpression original, int implicitConversion) {
return call; return call;
} }


/**
* Create a bridge method. It calls a same-named method with the same
* arguments, but with a different type signature.
*/
private void createBridgeMethod(SyntheticMethodBinding jdtBridgeMethod) { private void createBridgeMethod(SyntheticMethodBinding jdtBridgeMethod) {
JMethod implmeth = typeMap.get(jdtBridgeMethod.targetMethod); JMethod targetMethod = typeMap.get(jdtBridgeMethod.targetMethod);
SourceInfo info = implmeth.getSourceInfo(); createBridgeMethod(curClass.type, jdtBridgeMethod, targetMethod);
}

private void createBridgeMethod(
JDeclaredType enclosingType, MethodBinding sourceMethodBinding, JMethod targetMethod) {
JType returnType = typeMap.get(sourceMethodBinding.returnType);
Iterable<JType> parameterTypes =
FluentIterable.from(Arrays.asList(sourceMethodBinding.parameters)).transform(
new Function<TypeBinding, JType>() {
@Override
public JType apply(TypeBinding typeBinding) {
return typeMap.get(typeBinding.erasure());
}
});

Iterable<JClassType> thrownExceptionTypes =
FluentIterable.from(Arrays.asList(sourceMethodBinding.thrownExceptions)).transform(
new Function<ReferenceBinding, JClassType>() {
@Override
public JClassType apply(ReferenceBinding exceptionReferenceBinding) {
return (JClassType) typeMap.get(exceptionReferenceBinding.erasure());
}
});

JMethod bridgeMethod = createBridgeMethod(
enclosingType, targetMethod, parameterTypes, returnType, thrownExceptionTypes);
typeMap.setMethod(sourceMethodBinding, bridgeMethod);
}

/**
* Create a bridge method. It calls a same-named method with the same
* arguments, but with a different type signature.
*/
private JMethod createBridgeMethod(JDeclaredType enclosingType,
JMethod targetMethod, Iterable<JType> parameterTypes, JType returnType,
Iterable<JClassType> thrownExceptions) {
SourceInfo info = targetMethod.getSourceInfo();
JMethod bridgeMethod = JMethod bridgeMethod =
new JMethod(info, implmeth.getName(), curClass.type, typeMap new JMethod(info, targetMethod.getName(), enclosingType, returnType, false, false,
.get(jdtBridgeMethod.returnType), false, false, implmeth.isFinal(), implmeth targetMethod.isFinal(), targetMethod.getAccess());
.getAccess());
typeMap.setMethod(jdtBridgeMethod, bridgeMethod);
bridgeMethod.setBody(new JMethodBody(info)); bridgeMethod.setBody(new JMethodBody(info));
curClass.type.addMethod(bridgeMethod); enclosingType.addMethod(bridgeMethod);
bridgeMethod.setSynthetic(); bridgeMethod.setSynthetic();
int paramIdx = 0; int paramIdx = 0;
List<JParameter> implParams = implmeth.getParams(); List<JParameter> implParams = targetMethod.getParams();
for (TypeBinding jdtParamType : jdtBridgeMethod.parameters) { for (JType parameterType : parameterTypes) {
JParameter param = implParams.get(paramIdx++); JParameter parameter = implParams.get(paramIdx++);
JType paramType = typeMap.get(jdtParamType.erasure()); bridgeMethod.createFinalParameter(
bridgeMethod.createFinalParameter(param.getSourceInfo(), param.getName(), paramType); parameter.getSourceInfo(), parameter.getName(), parameterType);
} }
for (ReferenceBinding exceptionReference : jdtBridgeMethod.thrownExceptions) { for (JClassType thrownException : thrownExceptions) {
bridgeMethod.addThrownException((JClassType) typeMap.get(exceptionReference.erasure())); bridgeMethod.addThrownException(thrownException);
} }
bridgeMethod.freezeParamTypes(); bridgeMethod.freezeParamTypes();


// create a call and pass all arguments through, casting if necessary // create a call and pass all arguments through, casting if necessary
JMethodCall call = new JMethodCall(info, makeThisRef(info), implmeth); JMethodCall call = new JMethodCall(info, makeThisRef(info), targetMethod);
for (int i = 0; i < bridgeMethod.getParams().size(); i++) { for (int i = 0; i < bridgeMethod.getParams().size(); i++) {
JParameter param = bridgeMethod.getParams().get(i); JParameter param = bridgeMethod.getParams().get(i);
call.addArg(maybeCast(implParams.get(i).getType(), param.makeRef(info))); call.addArg(maybeCast(implParams.get(i).getType(), param.makeRef(info)));
Expand All @@ -2788,6 +2841,7 @@ private void createBridgeMethod(SyntheticMethodBinding jdtBridgeMethod) {
} else { } else {
body.getBlock().addStmt(call.makeReturnStatement()); body.getBlock().addStmt(call.makeReturnStatement());
} }
return bridgeMethod;
} }


private void writeEnumValuesMethod(JEnumType type, JMethod method) { private void writeEnumValuesMethod(JEnumType type, JMethod method) {
Expand Down
Expand Up @@ -1623,4 +1623,47 @@ public Double m() {
assertEquals(2, f.callM().intValue()); assertEquals(2, f.callM().intValue());
assertEquals(5, createNative().callM().intValue()); assertEquals(5, createNative().callM().intValue());
} }

interface FunctionalExpressionBridges_I<T> {
T apply(T t);
// TODO(rluble): uncomment the line below to when bridges for default methods are created
// in functional expressions
// FunctionalExpressionBridges_I<T> m(T t);
}

@FunctionalInterface
interface FunctionalExpressionBridges_J<T extends Comparable>
extends FunctionalExpressionBridges_I<T> {
T apply(T t);

// Overrides I.m() and specializes return type
default FunctionalExpressionBridges_J<T> m(T t) {
return this;
}
}

public static String identity(String s) {
return s;
}

public void testFunctionalExpressionBridges() {
FunctionalExpressionBridges_J<String> ann = new FunctionalExpressionBridges_J<String>() {
@Override
public String apply(String string) {
return string;
}
};

assertBrigdeDispatchIsCorrect(ann);
assertBrigdeDispatchIsCorrect((String s) -> s + "");
assertBrigdeDispatchIsCorrect(Java8Test::identity);
}

private void assertBrigdeDispatchIsCorrect(
FunctionalExpressionBridges_J<String> functionalExpression) {
assertEquals("Hello", functionalExpression.m(null).apply("Hello"));
assertEquals("Hello", functionalExpression.apply("Hello"));
assertEquals("Hello",
((FunctionalExpressionBridges_I<String>) functionalExpression).apply("Hello"));
}
} }
4 changes: 4 additions & 0 deletions user/test/com/google/gwt/dev/jjs/test/Java8Test.java
Expand Up @@ -300,6 +300,10 @@ public void testJsFunction_withOverlay() {
assertFalse(isGwtSourceLevel8()); assertFalse(isGwtSourceLevel8());
} }


public void testFunctionalExpressionBridges() {
assertFalse(isGwtSourceLevel8());
}

private boolean isGwtSourceLevel8() { private boolean isGwtSourceLevel8() {
return JUnitShell.getCompilerOptions().getSourceLevel().compareTo(SourceLevel.JAVA8) >= 0; return JUnitShell.getCompilerOptions().getSourceLevel().compareTo(SourceLevel.JAVA8) >= 0;
} }
Expand Down

0 comments on commit 7da4bf3

Please sign in to comment.