Skip to content

Commit

Permalink
Create default method shims for all possible selectors of a default m…
Browse files Browse the repository at this point in the history
…ethod.

	Change on 2016/04/29 by kstanger <kstanger@google.com>

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=121144162
  • Loading branch information
kstanger authored and Keith Stanger committed May 2, 2016
1 parent d0e7f60 commit f900e31
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 60 deletions.
Expand Up @@ -29,8 +29,8 @@
import com.google.devtools.j2objc.ast.TreeVisitor;
import com.google.devtools.j2objc.ast.TypeDeclaration;
import com.google.devtools.j2objc.types.FunctionBinding;
import com.google.devtools.j2objc.types.GeneratedMethodBinding;
import com.google.devtools.j2objc.types.GeneratedVariableBinding;
import com.google.devtools.j2objc.types.IOSMethodBinding;
import com.google.devtools.j2objc.util.BindingUtil;
import com.google.devtools.j2objc.util.UnicodeUtils;

Expand Down Expand Up @@ -135,48 +135,55 @@ private void addDefaultMethodShims(AbstractTypeDeclaration node) {
continue;
}

// Create the method binding and declaration.
GeneratedMethodBinding binding = new GeneratedMethodBinding(method);

// Don't carry over the default method flag from the original binding.
binding.removeModifiers(Modifier.DEFAULT);
// Mark synthetic to avoid writing metadata.
binding.addModifiers(BindingUtil.ACC_SYNTHETIC);

binding.setDeclaringClass(type);
MethodDeclaration methodDecl = new MethodDeclaration(binding);
methodDecl.setHasDeclaration(false);

// The shim's only purpose is to call the default method implementation and returns it value
// if required.
String name = nameTable.getFullFunctionName(method);
FunctionBinding fb = new FunctionBinding(name, method.getReturnType(), type);
fb.addParameters(type);
fb.addParameters(method.getParameterTypes());
FunctionInvocation invocation = new FunctionInvocation(fb, method.getReturnType());

// All default method implementations require self as the first function call argument.
invocation.getArguments().add(new ThisExpression(type));

// For each parameter in the default method, assign a name, and use the name in both the
// method declaration and the function invocation.
for (int i = 0; i < method.getParameterTypes().length; i++) {
ITypeBinding paramType = method.getParameterTypes()[i];
String paramName = UnicodeUtils.format("arg%d", i);
GeneratedVariableBinding varBinding = new GeneratedVariableBinding(paramName, 0, paramType,
false, true, type, null);
methodDecl.getParameters().add(new SingleVariableDeclaration(varBinding));
invocation.getArguments().add(new SimpleName(varBinding));
for (String selector : nameTable.getAllSelectors(method)) {
node.getBodyDeclarations().add(createDefaultMethodShim(selector, method, type));
}
}
}

Statement stmt = BindingUtil.isVoid(method.getReturnType())
? new ExpressionStatement(invocation)
: new ReturnStatement(invocation);

Block block = new Block();
block.getStatements().add(stmt);
methodDecl.setBody(block);
node.getBodyDeclarations().add(methodDecl);
private MethodDeclaration createDefaultMethodShim(
String selector, IMethodBinding method, ITypeBinding type) {
// Create the method binding and declaration.
IOSMethodBinding binding = IOSMethodBinding.newMappedMethod(selector, method);

// Don't carry over the default method flag from the original binding.
binding.removeModifiers(Modifier.DEFAULT);
// Mark synthetic to avoid writing metadata.
binding.addModifiers(BindingUtil.ACC_SYNTHETIC);

binding.setDeclaringClass(type);
MethodDeclaration methodDecl = new MethodDeclaration(binding);
methodDecl.setHasDeclaration(false);

// The shim's only purpose is to call the default method implementation and returns it value
// if required.
String name = nameTable.getFullFunctionName(method);
FunctionBinding fb = new FunctionBinding(name, method.getReturnType(), type);
fb.addParameters(type);
fb.addParameters(method.getParameterTypes());
FunctionInvocation invocation = new FunctionInvocation(fb, method.getReturnType());

// All default method implementations require self as the first function call argument.
invocation.getArguments().add(new ThisExpression(type));

// For each parameter in the default method, assign a name, and use the name in both the
// method declaration and the function invocation.
for (int i = 0; i < method.getParameterTypes().length; i++) {
ITypeBinding paramType = method.getParameterTypes()[i];
String paramName = UnicodeUtils.format("arg%d", i);
GeneratedVariableBinding varBinding = new GeneratedVariableBinding(paramName, 0, paramType,
false, true, type, null);
methodDecl.getParameters().add(new SingleVariableDeclaration(varBinding));
invocation.getArguments().add(new SimpleName(varBinding));
}

Statement stmt = BindingUtil.isVoid(method.getReturnType())
? new ExpressionStatement(invocation)
: new ReturnStatement(invocation);

Block block = new Block();
block.getStatements().add(stmt);
methodDecl.setBody(block);
return methodDecl;
}
}
Expand Up @@ -524,12 +524,6 @@ private String addParamNames(IMethodBinding method, String name, char delim) {
}

public String getMethodSelector(IMethodBinding method) {
if (method instanceof IOSMethodBinding) {
return ((IOSMethodBinding) method).getSelector();
}
if (method.isConstructor() || BindingUtil.isStatic(method)) {
return selectorForOriginalBinding(method);
}
return selectorForOriginalBinding(getOriginalMethodBindings(method).get(0));
}

Expand All @@ -554,6 +548,9 @@ public String selectorForMethodName(IMethodBinding method, String name) {
}

private String selectorForOriginalBinding(IMethodBinding method) {
if (method instanceof IOSMethodBinding) {
return ((IOSMethodBinding) method).getSelector();
}
String selector = getRenamedMethodName(method);
return selectorForMethodName(
method, selector != null ? selector : getMethodName(method));
Expand All @@ -565,20 +562,19 @@ private String selectorForOriginalBinding(IMethodBinding method) {
* not returned by getMethodSelector().
*/
public List<String> getExtraSelectors(IMethodBinding method) {
if (method instanceof IOSMethodBinding || method.isConstructor()
|| BindingUtil.isStatic(method)) {
return Collections.emptyList();
}
List<IMethodBinding> originalMethods = getOriginalMethodBindings(method);
List<String> extraSelectors = new ArrayList<>();
String actualSelector = selectorForOriginalBinding(originalMethods.get(0));
for (int i = 1; i < originalMethods.size(); i++) {
String selector = selectorForOriginalBinding(originalMethods.get(i));
if (!selector.equals(actualSelector)) {
extraSelectors.add(selector);
List<String> allSelectors = getAllSelectors(method);
return allSelectors.subList(1, allSelectors.size());
}

public List<String> getAllSelectors(IMethodBinding method) {
List<String> selectors = new ArrayList<>();
for (IMethodBinding originalMethod : getOriginalMethodBindings(method)) {
String selector = selectorForOriginalBinding(originalMethod);
if (!selectors.contains(selector)) {
selectors.add(selector);
}
}
return extraSelectors;
return selectors;
}

/**
Expand Down Expand Up @@ -685,7 +681,8 @@ public static String getMethodNameFromAnnotation(IMethodBinding method) {
*/
private List<IMethodBinding> getOriginalMethodBindings(IMethodBinding method) {
method = method.getMethodDeclaration();
if (method.isConstructor() || BindingUtil.isStatic(method)) {
if (method.isConstructor() || BindingUtil.isStatic(method)
|| method instanceof IOSMethodBinding) {
return Collections.singletonList(method);
}
ITypeBinding declaringClass = method.getDeclaringClass();
Expand Down
Expand Up @@ -402,4 +402,19 @@ public void testAccessingOuterType() throws IOException {
String impl = translateSourceFile(source, "Test", "Test.m");
assertTranslatedLines(impl, "IOSClass *A_type(id<A> self) {", "return [self getClass];", "}");
}

public void testDefaultMethodWithMultipleSelectors() throws IOException {
addSourceFile("interface A <T> { void foo(T t); }", "A.java");
addSourceFile("interface B { void foo(String s); }", "B.java");
addSourceFile("interface C extends A<String>, B { default void foo(String s) {} }", "C.java");
addSourceFile("class D implements C {}", "D.java");
String headerC = translateSourceFile("C", "C.h");
String implD = translateSourceFile("D", "D.m");
assertTranslation(headerC, "- (void)fooWithId:(NSString *)s;");
assertTranslation(headerC, "- (void)fooWithNSString:(NSString *)s;");
assertTranslatedLines(implD,
"- (void)fooWithId:(NSString *)arg0 {", "C_fooWithNSString_(self, arg0);", "}");
assertTranslatedLines(implD,
"- (void)fooWithNSString:(NSString *)arg0 {", "C_fooWithNSString_(self, arg0);", "}");
}
}

0 comments on commit f900e31

Please sign in to comment.