Skip to content

Commit

Permalink
Rewrites SuperMethodReference as a LambdaExpression node. Clean up re…
Browse files Browse the repository at this point in the history
…maining code that handles method reference nodes.

	Change on 2016/05/24 by kstanger <kstanger@google.com>

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123141735
  • Loading branch information
kstanger authored and tomball committed May 27, 2016
1 parent 0c59f67 commit eee3e2b
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 150 deletions.
Expand Up @@ -61,12 +61,6 @@ public ITypeBinding getTypeBinding() {
}

public IMethodBinding getMethodBinding() {
// Workaround for a JDT 4.5.2 bug.
if (methodBinding == null) {
IMethodBinding[] methods = typeBinding.getDeclaredMethods();
assert methods.length == 1;
methodBinding = methods[0];
}
return methodBinding;
}

Expand Down
Expand Up @@ -61,7 +61,6 @@
import com.google.devtools.j2objc.ast.MarkerAnnotation;
import com.google.devtools.j2objc.ast.MemberValuePair;
import com.google.devtools.j2objc.ast.MethodInvocation;
import com.google.devtools.j2objc.ast.MethodReference;
import com.google.devtools.j2objc.ast.Name;
import com.google.devtools.j2objc.ast.NameQualifiedType;
import com.google.devtools.j2objc.ast.NativeExpression;
Expand Down Expand Up @@ -426,77 +425,43 @@ public boolean visit(CreationReference node) {
throw new AssertionError("CreationReference nodes are rewritten by MethodReferenceRewriter.");
}

/**
* Prints a method reference using our created invocation, called from each individual method
* reference subclass visit.
*/
public boolean printMethodReference(MethodReference node) {
IMethodBinding functionalInterface = node.getTypeBinding().getFunctionalInterfaceMethod();
ITypeBinding returnType = functionalInterface.getReturnType();
printMethodReferenceCallWithoutBlocks(node);
printBlockPreExpression(node, returnType);
node.getInvocation().accept(this);
buffer.append("})");
return false;
private void printGenericArguments(IMethodBinding methodBinding) {
printGenericArgumentsInner(methodBinding, false);
}

private void printGenericArguments(IMethodBinding methodBinding, boolean isSelector) {
printGenericArgumentsInner(methodBinding, isSelector, false);
}


private void printGenericArgumentsInner(IMethodBinding methodBinding, boolean isSelector,
boolean withTypes) {
private void printGenericArgumentsInner(IMethodBinding methodBinding, boolean withTypes) {
char[] var = NameTable.incrementVariable(null);
if (isSelector) {
String fullSelector = nameTable.getMethodSelector(methodBinding);
String[] selectors = fullSelector.split(":");
if (selectors.length == 1 && fullSelector.charAt(fullSelector.length() - 1) != ':') {
buffer.append(' ');
buffer.append(fullSelector);
} else {
assert methodBinding.getParameterTypes().length
== selectors.length : "Selector and parameter counts differ.";
for (int i = 0; i < selectors.length; i++) {
if (!withTypes && methodBinding.isVarargs()) {
boolean delimiterFlag = false;
for (int i = 0; i < methodBinding.getParameterTypes().length - 1; i++) {
ITypeBinding t = methodBinding.getParameterTypes()[i];
if (delimiterFlag) {
buffer.append(", ");
} else {
delimiterFlag = true;
}
if (withTypes) {
buffer.append(nameTable.getObjCType(t));
buffer.append(' ');
buffer.append(selectors[i]);
buffer.append(':');
buffer.append(var);
var = NameTable.incrementVariable(var);
}
buffer.append(var);
var = NameTable.incrementVariable(var);
}
} else {
if (!withTypes && methodBinding.isVarargs()) {
boolean delimiterFlag = false;
for (int i = 0; i < methodBinding.getParameterTypes().length - 1; i++) {
ITypeBinding t = methodBinding.getParameterTypes()[i];
if (delimiterFlag) {
buffer.append(", ");
} else {
delimiterFlag = true;
}
if (withTypes) {
buffer.append(nameTable.getObjCType(t));
buffer.append(' ');
}
buffer.append(var);
var = NameTable.incrementVariable(var);
boolean delimiterFlag = false;
for (ITypeBinding t : methodBinding.getParameterTypes()) {
if (delimiterFlag) {
buffer.append(", ");
} else {
delimiterFlag = true;
}
} else {
boolean delimiterFlag = false;
for (ITypeBinding t : methodBinding.getParameterTypes()) {
if (delimiterFlag) {
buffer.append(", ");
} else {
delimiterFlag = true;
}
if (withTypes) {
buffer.append(nameTable.getObjCType(t));
buffer.append(' ');
}
buffer.append(var);
var = NameTable.incrementVariable(var);
if (withTypes) {
buffer.append(nameTable.getObjCType(t));
buffer.append(' ');
}
buffer.append(var);
var = NameTable.incrementVariable(var);
}
}
}
Expand All @@ -512,18 +477,12 @@ private void printBlockPreExpression(IMethodBinding functionalInterface,
buffer.append("(id _self");
if (functionalInterface.getParameterTypes().length > 0) {
buffer.append(", ");
boolean isSelector = false;
boolean withTypes = true;
printGenericArgumentsInner(functionalInterface, isSelector, withTypes);
printGenericArgumentsInner(functionalInterface, withTypes);
}
buffer.append(") {\n");
}

private void printBlockPreExpression(MethodReference node, ITypeBinding returnType) {
IMethodBinding functionalInterface = node.getTypeBinding().getFunctionalInterfaceMethod();
printBlockPreExpression(functionalInterface, returnType);
}

@Override
public boolean visit(Dimension node) {
// TODO(kirbs): Implement correct conversion of Java 8 features to Objective-C.
Expand Down Expand Up @@ -727,7 +686,6 @@ public boolean visit(LambdaExpression node) {
* underlying block specific to the instance.
*/
private void printBlockCallWrapper(IMethodBinding methodBinding) {
boolean isSelector = false;
ITypeBinding returnType = methodBinding.getReturnType();
printBlockPreExpression(methodBinding, returnType);
buffer.append(nameTable.getObjCType(returnType));
Expand All @@ -741,7 +699,7 @@ private void printBlockCallWrapper(IMethodBinding methodBinding) {
buffer.append("block(_self");
if (methodBinding.getParameterTypes().length > 0) {
buffer.append(", ");
printGenericArguments(methodBinding, isSelector);
printGenericArguments(methodBinding);
}
buffer.append(");\n}");
}
Expand All @@ -766,12 +724,6 @@ private void printLambdaCall(LambdaExpression node) {
*/
private void printLambdaCallWithoutBlocks(ITypeBinding functionalTypeBinding, String newClassName,
boolean isCapturing) {
printCallWithoutBlocksInner(functionalTypeBinding, newClassName,
isCapturing);
}

private void printCallWithoutBlocksInner(ITypeBinding functionalTypeBinding, String newClassName,
boolean isCapturing) {
String functionalClassName = nameTable.getFullName(functionalTypeBinding);
IMethodBinding functionalInterface = functionalTypeBinding.getFunctionalInterfaceMethod();
boolean hasDefaultMethods = BindingUtil.hasDefaultMethodsInFamily(functionalTypeBinding);
Expand Down Expand Up @@ -802,18 +754,6 @@ private void printCallWithoutBlocksInner(ITypeBinding functionalTypeBinding, Str
buffer.append("),\n");
}

/**
* Convenience method for calling printCallWithoutBlocksInner from method references.
*/
private void printMethodReferenceCallWithoutBlocks(MethodReference node) {
IMethodBinding methodBinding = node.getMethodBinding();
ITypeBinding functionalTypeBinding = node.getTypeBinding();
IMethodBinding functionalInterface = functionalTypeBinding.getFunctionalInterfaceMethod();
String newClassName = nameTable.getMethodReferenceName(methodBinding, functionalInterface);
boolean isCapturing = false;
printCallWithoutBlocksInner(functionalTypeBinding, newClassName, isCapturing);
}

/**
* The lambda wrapper and method blocks.
*/
Expand Down Expand Up @@ -1106,9 +1046,8 @@ public boolean visit(SuperMethodInvocation node) {

@Override
public boolean visit(SuperMethodReference node) {
assert Options
.isJava8Translator() : "SuperMethodReference in translator with -source less than 8.";
return printMethodReference(node);
throw new AssertionError(
"SuperMethodReference nodes are rewritten by MethodReferenceRewriter.");
}

@Override
Expand Down
Expand Up @@ -23,6 +23,8 @@
import com.google.devtools.j2objc.ast.MethodInvocation;
import com.google.devtools.j2objc.ast.Name;
import com.google.devtools.j2objc.ast.SimpleName;
import com.google.devtools.j2objc.ast.SuperMethodInvocation;
import com.google.devtools.j2objc.ast.SuperMethodReference;
import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.TreeVisitor;
import com.google.devtools.j2objc.ast.Type;
Expand Down Expand Up @@ -123,6 +125,18 @@ private IMethodBinding getMethodBinding(TypeMethodReference node) {
return node.getMethodBinding();
}

@Override
public void endVisit(SuperMethodReference node) {
ITypeBinding exprBinding = node.getTypeBinding();
LambdaExpression lambda = new LambdaExpression(
"SuperMethodReference:" + node.getLineNumber(), exprBinding);
SuperMethodInvocation invocation = new SuperMethodInvocation(node.getMethodBinding());
invocation.setQualifier(TreeUtil.remove(node.getQualifier()));
lambda.setBody(invocation);
addParamsToInvocation(createParameters(lambda), invocation.getArguments());
node.replaceWith(lambda);
}

private Iterator<IVariableBinding> createParameters(LambdaExpression lambda) {
IMethodBinding functionalInterface = lambda.getTypeBinding().getFunctionalInterfaceMethod();
ITypeBinding[] paramTypes = functionalInterface.getParameterTypes();
Expand Down
Expand Up @@ -30,18 +30,13 @@
import com.google.devtools.j2objc.ast.InfixExpression;
import com.google.devtools.j2objc.ast.LambdaExpression;
import com.google.devtools.j2objc.ast.MethodDeclaration;
import com.google.devtools.j2objc.ast.MethodInvocation;
import com.google.devtools.j2objc.ast.MethodReference;
import com.google.devtools.j2objc.ast.Name;
import com.google.devtools.j2objc.ast.ParenthesizedExpression;
import com.google.devtools.j2objc.ast.PropertyAnnotation;
import com.google.devtools.j2objc.ast.QualifiedName;
import com.google.devtools.j2objc.ast.ReturnStatement;
import com.google.devtools.j2objc.ast.SimpleName;
import com.google.devtools.j2objc.ast.SingleVariableDeclaration;
import com.google.devtools.j2objc.ast.Statement;
import com.google.devtools.j2objc.ast.SuperMethodInvocation;
import com.google.devtools.j2objc.ast.SuperMethodReference;
import com.google.devtools.j2objc.ast.SwitchStatement;
import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.TreeVisitor;
Expand All @@ -52,7 +47,6 @@
import com.google.devtools.j2objc.types.GeneratedVariableBinding;
import com.google.devtools.j2objc.util.BindingUtil;
import com.google.devtools.j2objc.util.ErrorUtil;
import com.google.devtools.j2objc.util.NameTable;
import com.google.j2objc.annotations.AutoreleasePool;
import com.google.j2objc.annotations.RetainedLocalRef;
import com.google.j2objc.annotations.Weak;
Expand Down Expand Up @@ -408,52 +402,6 @@ private String getLambdaUniqueName(LambdaExpression node) {
return nameTable.getFullName(owningType) + "$$Lambda$" + count;
}

@Override
public boolean visit(SuperMethodReference node) {
IMethodBinding methodBinding = node.getMethodBinding().getMethodDeclaration();
Name qualifier = node.getQualifier() == null ? null : node.getQualifier().copy();
SuperMethodInvocation invocation = new SuperMethodInvocation(methodBinding);
invocation.setQualifier(qualifier);
List<Expression> invocationArguments = invocation.getArguments();
buildMethodReferenceInvocationArguments(invocationArguments, node, null);
IMethodBinding functionalInterface = node.getTypeBinding().getFunctionalInterfaceMethod();
if (BindingUtil.isVoid(functionalInterface.getReturnType())) {
node.setInvocation(new ExpressionStatement(invocation));
} else {
node.setInvocation(new ReturnStatement(invocation));
}
return true;
}

/**
* Fill in the arguments in a method reference invocation. The argument list must come from the
* functional interface's method, not the invoked method: this is because it is possible to
* make a reference to a method with varargs, but the actual number of arguments is determined
* during compile time by the matching functional interface's method.
*/
// TODO(kirbs): In the case that we have a referenced method with an int arg, a functional
// interface method with an Integer arg, and an invocation with an int arg, we will end up
// immediately boxing and unboxing the value. We should solve this by making the types of the
// referenced method and the functional interface method the same, but this requires a rewrite of
// the selectors that target the method reference on invocation.
public void buildMethodReferenceInvocationArguments(List<Expression> invocationArguments,
MethodReference node,
MethodInvocation invocation) {
IMethodBinding functionalInterface = node.getTypeBinding().getFunctionalInterfaceMethod();
ITypeBinding[] functionalParams = functionalInterface.getParameterTypes();
char[] var = NameTable.incrementVariable(null);
int paramIdx = 0;

// Fill in the invocation parameters.
for (; paramIdx < functionalParams.length; paramIdx++) {
ITypeBinding functionalParam = functionalParams[paramIdx];
IVariableBinding variableBinding = new GeneratedVariableBinding(new String(var), 0,
functionalParam, false, true, null, null);
invocationArguments.add(new SimpleName(variableBinding));
var = NameTable.incrementVariable(var);
}
}

/**
* Verify, update property attributes. Accessor methods are not checked since a
* property annotation may apply to separate variables in a field declaration, so
Expand Down
Expand Up @@ -232,4 +232,16 @@ public void testCreationReferenceOfLocalCapturingType() throws IOException {
assertTranslatedSegments(translation,
"GetCapturingLambda", "return create_Test_1Runner_initWithJavaLangRunnable_(r);");
}

public void testQualifiedSuperMethodReference() throws IOException {
String translation = translateSourceFile(
"interface I { void bar(); }"
+ "class Test { void foo() {} static class TestSub extends Test { void foo() {}"
+ "class Inner { I test() { return TestSub.super::foo; } } } }",
"Test", "Test.m");
assertTranslatedSegments(translation,
"static void (*Test_TestSub_super$_foo)(id, SEL);",
"GetCapturingLambda(NULL, @protocol(I)",
"^void(id _self) {", "Test_TestSub_super$_foo(this$0_, @selector(foo));", "}");
}
}

0 comments on commit eee3e2b

Please sign in to comment.