Java tutorial
/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.devtools.j2objc.translate; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.devtools.j2objc.ast.AbstractTypeDeclaration; import com.google.devtools.j2objc.ast.AnnotationTypeDeclaration; import com.google.devtools.j2objc.ast.CompilationUnit; import com.google.devtools.j2objc.ast.EnumDeclaration; import com.google.devtools.j2objc.ast.Expression; import com.google.devtools.j2objc.ast.FunctionInvocation; import com.google.devtools.j2objc.ast.Name; import com.google.devtools.j2objc.ast.NativeDeclaration; import com.google.devtools.j2objc.ast.NativeExpression; import com.google.devtools.j2objc.ast.NativeStatement; import com.google.devtools.j2objc.ast.SuperMethodInvocation; import com.google.devtools.j2objc.ast.ThisExpression; import com.google.devtools.j2objc.ast.TreeUtil; import com.google.devtools.j2objc.ast.TreeVisitor; import com.google.devtools.j2objc.ast.TypeDeclaration; import com.google.devtools.j2objc.jdt.BindingConverter; import com.google.devtools.j2objc.types.FunctionBinding; import com.google.devtools.j2objc.util.BindingUtil; import com.google.devtools.j2objc.util.NameTable; import com.google.devtools.j2objc.util.UnicodeUtils; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import java.util.List; import java.util.Map; import java.util.Set; import javax.lang.model.type.TypeMirror; /** * Some super method invocations cannot be translated directly as an ObjC super * invocation. This occurs when the invocation is qualified by an outer type or * when the containing method has been functionized. To resolve these * invocations we declare a static function pointer and look up the * implementation during static initialization. * * @author Keith Stanger */ public class SuperMethodInvocationRewriter extends TreeVisitor { private Set<SuperMethodBindingPair> superMethods; private Map<ITypeBinding, AbstractTypeDeclaration> typeMap; @Override public boolean visit(CompilationUnit unit) { superMethods = Sets.newLinkedHashSet(); typeMap = Maps.newHashMap(); return true; } @Override public void endVisit(CompilationUnit unit) { for (SuperMethodBindingPair superMethod : superMethods) { String funcName = getSuperFunctionName(superMethod); String signature = getSuperFunctionSignature(superMethod.method); // Add declarations for the function pointers to call. unit.addNativeBlock(NativeDeclaration.newOuterDeclaration(null, "static " + UnicodeUtils.format(signature, funcName) + ";")); // Look up the implementations in the static initialization. AbstractTypeDeclaration typeNode = typeMap.get(superMethod.type.getTypeDeclaration()); assert typeNode != null : "Type is expected to be in this compilation unit"; String superclassName = nameTable.getFullName(superMethod.type.getSuperclass()); typeNode.addClassInitStatement(0, new NativeStatement( UnicodeUtils.format("%s = (%s)[%s instanceMethodForSelector:@selector(%s)];", funcName, UnicodeUtils.format(signature, ""), superclassName, nameTable.getMethodSelector(superMethod.method)))); } } private static String getSuperFunctionSignature(IMethodBinding method) { StringBuilder signature = new StringBuilder(NameTable.getPrimitiveObjCType(method.getReturnType())); signature.append(" (*%s)(id, SEL"); for (ITypeBinding paramType : method.getParameterTypes()) { signature.append(", ").append(NameTable.getPrimitiveObjCType(paramType)); } signature.append(")"); return signature.toString(); } private String getSuperFunctionName(SuperMethodBindingPair superMethod) { return UnicodeUtils.format("%s_super$_%s", nameTable.getFullName(superMethod.type), nameTable.getFunctionName(superMethod.method)); } @Override public void endVisit(SuperMethodInvocation node) { Name qualifier = node.getQualifier(); if (qualifier == null) { return; } IMethodBinding method = node.getMethodBinding(); TypeMirror exprType = node.getTypeMirror(); // Handle default method invocation: SomeInterface.super.method(...) if (BindingUtil.isDefault(method)) { FunctionBinding binding = new FunctionBinding(nameTable.getFullFunctionName(method), exprType, typeEnv.getIdTypeMirror()); binding.addParameters(typeEnv.getIdTypeMirror()); binding.addParameters(method.getParameterTypes()); FunctionInvocation invocation = new FunctionInvocation(binding, exprType); List<Expression> args = invocation.getArguments(); ITypeBinding thisClass = TreeUtil.getOwningType(node).getTypeBinding(); args.add(new ThisExpression(thisClass)); TreeUtil.copyList(node.getArguments(), args); node.replaceWith(invocation); return; } IVariableBinding var = TreeUtil.getVariableBinding(qualifier); assert var != null : "Expected qualifier to be a variable"; TypeMirror qualifierType = BindingConverter.getType(var.getType()); SuperMethodBindingPair superMethod = new SuperMethodBindingPair(qualifierType, method); superMethods.add(superMethod); FunctionBinding binding = new FunctionBinding(getSuperFunctionName(superMethod), exprType, qualifierType); binding.addParameters(qualifierType, typeEnv.getIdTypeMirror()); binding.addParameters(method.getParameterTypes()); FunctionInvocation invocation = new FunctionInvocation(binding, exprType); List<Expression> args = invocation.getArguments(); args.add(TreeUtil.remove(qualifier)); String selectorExpr = UnicodeUtils.format("@selector(%s)", nameTable.getMethodSelector(method)); args.add(new NativeExpression(selectorExpr, typeEnv.getIdType())); TreeUtil.copyList(node.getArguments(), args); node.replaceWith(invocation); } @Override public void endVisit(AnnotationTypeDeclaration node) { typeMap.put(node.getTypeBinding(), node); } @Override public void endVisit(EnumDeclaration node) { typeMap.put(node.getTypeBinding(), node); } @Override public void endVisit(TypeDeclaration node) { typeMap.put(node.getTypeBinding(), node); } private static class SuperMethodBindingPair { private final ITypeBinding type; private final IMethodBinding method; private SuperMethodBindingPair(TypeMirror type, IMethodBinding method) { this.type = BindingConverter.unwrapTypeMirrorIntoTypeBinding(type); this.method = method; } @Override public boolean equals(Object obj) { if (!(obj instanceof SuperMethodBindingPair)) { return false; } SuperMethodBindingPair other = (SuperMethodBindingPair) obj; return other.type == type && other.method == method; } @Override public int hashCode() { return 31 * System.identityHashCode(type) + System.identityHashCode(method); } } }