/* * 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.Lists; import com.google.devtools.j2objc.ast.Block; import com.google.devtools.j2objc.ast.CompilationUnit; import com.google.devtools.j2objc.ast.ExpressionStatement; import com.google.devtools.j2objc.ast.FieldAccess; import com.google.devtools.j2objc.ast.FunctionInvocation; import com.google.devtools.j2objc.ast.MethodDeclaration; import com.google.devtools.j2objc.ast.MethodInvocation; import com.google.devtools.j2objc.ast.PrefixExpression; 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.TreeUtil; import com.google.devtools.j2objc.ast.TypeDeclaration; import com.google.devtools.j2objc.ast.UnitTreeVisitor; import com.google.devtools.j2objc.ast.VariableDeclarationFragment; import com.google.devtools.j2objc.types.ExecutablePair; import com.google.devtools.j2objc.types.FunctionElement; import com.google.devtools.j2objc.types.GeneratedExecutableElement; import com.google.devtools.j2objc.types.GeneratedVariableElement; import com.google.devtools.j2objc.types.PointerType; import com.google.devtools.j2objc.util.ElementUtil; import com.google.devtools.j2objc.util.NameTable; import com.google.devtools.j2objc.util.TypeUtil; import java.util.List; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; /** * Writes the __javaClone method in order to support correct Java clone() * behavior. * * @author Keith Stanger */ public class JavaCloneWriter extends UnitTreeVisitor { private static final String JAVA_CLONE_METHOD = "__javaClone:"; private final ExecutableElement releaseMethod = GeneratedExecutableElement.newMethodWithSelector( NameTable.RELEASE_METHOD, typeUtil.getVoid(), TypeUtil.NS_OBJECT) .addModifiers(Modifier.PUBLIC); public JavaCloneWriter(CompilationUnit unit) { super(unit); } @Override public void endVisit(TypeDeclaration node) { TypeElement type = node.getTypeElement(); VariableElement originalVar = GeneratedVariableElement.newParameter("original", type.asType(), null); List<Statement> adjustments = getFieldAdjustments(node, originalVar); if (adjustments.isEmpty()) { return; } TypeMirror voidType = typeUtil.getVoid(); ExecutableElement javaCloneElement = GeneratedExecutableElement.newMethodWithSelector(JAVA_CLONE_METHOD, voidType, type) .addParameter(originalVar); MethodDeclaration declaration = new MethodDeclaration(javaCloneElement); declaration.setHasDeclaration(false); node.addBodyDeclaration(declaration); declaration.addParameter(new SingleVariableDeclaration(originalVar)); Block body = new Block(); declaration.setBody(body); List<Statement> statements = body.getStatements(); ExecutableElement javaCloneSuperElement = GeneratedExecutableElement.newMethodWithSelector( JAVA_CLONE_METHOD, voidType, typeUtil.getJavaObject()); SuperMethodInvocation superCall = new SuperMethodInvocation(new ExecutablePair(javaCloneSuperElement)); superCall.addArgument(new SimpleName(originalVar)); statements.add(new ExpressionStatement(superCall)); statements.addAll(adjustments); } private List<Statement> getFieldAdjustments(TypeDeclaration node, VariableElement originalVar) { List<Statement> adjustments = Lists.newArrayList(); for (VariableDeclarationFragment decl : TreeUtil.getAllFields(node)) { VariableElement var = decl.getVariableElement(); if (ElementUtil.isStatic(var) || var.asType().getKind().isPrimitive()) { continue; } boolean isWeak = ElementUtil.isWeakReference(var); boolean isVolatile = ElementUtil.isVolatile(var); if (isVolatile) { adjustments.add(createVolatileCloneStatement(var, originalVar, isWeak)); } else if (isWeak) { adjustments.add(createReleaseStatement(var)); } } return adjustments; } private Statement createReleaseStatement(VariableElement var) { if (options.useARC()) { TypeMirror voidType = typeUtil.getVoid(); FunctionElement element = new FunctionElement("JreRelease", voidType, null) .addParameters(TypeUtil.ID_TYPE); FunctionInvocation invocation = new FunctionInvocation(element, voidType); invocation.addArgument(new SimpleName(var)); return new ExpressionStatement(invocation); } else { return new ExpressionStatement( new MethodInvocation(new ExecutablePair(releaseMethod), new SimpleName(var))); } } private Statement createVolatileCloneStatement( VariableElement var, VariableElement originalVar, boolean isWeak) { TypeMirror voidType = typeUtil.getVoid(); TypeMirror pointerType = new PointerType(var.asType()); String funcName = "JreCloneVolatile" + (isWeak ? "" : "Strong"); FunctionElement element = new FunctionElement(funcName, voidType, null) .addParameters(pointerType, pointerType); FunctionInvocation invocation = new FunctionInvocation(element, voidType); invocation.addArgument(new PrefixExpression( pointerType, PrefixExpression.Operator.ADDRESS_OF, new SimpleName(var))); invocation.addArgument(new PrefixExpression( pointerType, PrefixExpression.Operator.ADDRESS_OF, new FieldAccess(var, new SimpleName(originalVar)))); return new ExpressionStatement(invocation); } }