/* * 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.devtools.j2objc.ast.ArrayAccess; import com.google.devtools.j2objc.ast.ArrayCreation; import com.google.devtools.j2objc.ast.ArrayInitializer; import com.google.devtools.j2objc.ast.Assignment; import com.google.devtools.j2objc.ast.CompilationUnit; import com.google.devtools.j2objc.ast.Expression; import com.google.devtools.j2objc.ast.FieldAccess; import com.google.devtools.j2objc.ast.FunctionInvocation; import com.google.devtools.j2objc.ast.InstanceofExpression; import com.google.devtools.j2objc.ast.MethodInvocation; import com.google.devtools.j2objc.ast.NumberLiteral; import com.google.devtools.j2objc.ast.PrefixExpression; import com.google.devtools.j2objc.ast.QualifiedName; import com.google.devtools.j2objc.ast.SimpleName; import com.google.devtools.j2objc.ast.TreeUtil; import com.google.devtools.j2objc.ast.TypeLiteral; import com.google.devtools.j2objc.ast.UnitTreeVisitor; 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.TranslationUtil; import com.google.devtools.j2objc.util.TypeUtil; import com.google.devtools.j2objc.util.UnicodeUtils; 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.ArrayType; import javax.lang.model.type.TypeMirror; /** * Rewrites array creation into a method invocation on an IOSArray class. * * @author Keith Stanger */ public class ArrayRewriter extends UnitTreeVisitor { public ArrayRewriter(CompilationUnit unit) { super(unit); } @Override public void endVisit(ArrayCreation node) { node.replaceWith(createInvocation(node)); } private MethodInvocation createInvocation(ArrayCreation node) { ArrayType arrayType = node.getTypeMirror(); boolean retainedResult = node.hasRetainedResult() || options.useARC(); ArrayInitializer initializer = node.getInitializer(); if (initializer != null) { return newInitializedArrayInvocation(arrayType, initializer.getExpressions(), retainedResult); } else { List<Expression> dimensions = node.getDimensions(); if (dimensions.size() == 1) { return newSingleDimensionArrayInvocation(arrayType, dimensions.get(0), retainedResult); } else { return newMultiDimensionArrayInvocation(arrayType, dimensions, retainedResult); } } } private MethodInvocation newInitializedArrayInvocation( ArrayType arrayType, List<Expression> elements, boolean retainedResult) { TypeMirror componentType = arrayType.getComponentType(); TypeElement iosArrayElement = typeUtil.getIosArray(componentType); GeneratedExecutableElement methodElement = GeneratedExecutableElement.newMethodWithSelector( getInitializeSelector(componentType, retainedResult), iosArrayElement.asType(), iosArrayElement) .addModifiers(Modifier.PUBLIC, Modifier.STATIC); methodElement.addParameter(GeneratedVariableElement.newParameter( "values", new PointerType(componentType), methodElement)); methodElement.addParameter(GeneratedVariableElement.newParameter( "count", typeUtil.getInt(), methodElement)); if (!componentType.getKind().isPrimitive()) { methodElement.addParameter(GeneratedVariableElement.newParameter( "type", TypeUtil.IOS_CLASS.asType(), methodElement)); } MethodInvocation invocation = new MethodInvocation( new ExecutablePair(methodElement), arrayType, new SimpleName(iosArrayElement)); // Create the array initializer and add it as the first parameter. ArrayInitializer arrayInit = new ArrayInitializer(arrayType); for (Expression element : elements) { arrayInit.addExpression(element.copy()); } invocation.addArgument(arrayInit); // Add the array size parameter. invocation.addArgument( NumberLiteral.newIntLiteral(arrayInit.getExpressions().size(), typeUtil)); // Add the type argument for object arrays. if (!componentType.getKind().isPrimitive()) { invocation.addArgument(new TypeLiteral(componentType, typeUtil)); } return invocation; } private String paramNameForPrimitive(TypeMirror t) { switch (t.getKind()) { case BOOLEAN: return "Booleans"; case BYTE: return "Bytes"; case CHAR: return "Chars"; case DOUBLE: return "Doubles"; case FLOAT: return "Floats"; case INT: return "Ints"; case LONG: return "Longs"; case SHORT: return "Shorts"; default: throw new AssertionError("Not a primitive type: " + t); } } private String getInitializeSelector(TypeMirror componentType, boolean retainedResult) { String selectorFmt = "arrayWith%s:count:"; if (retainedResult) { selectorFmt = "newArrayWith%s:count:"; } String paramName; if (componentType.getKind().isPrimitive()) { paramName = paramNameForPrimitive(componentType); } else { paramName = "Objects"; selectorFmt += "type:"; } return UnicodeUtils.format(selectorFmt, paramName); } private MethodInvocation newSingleDimensionArrayInvocation( ArrayType arrayType, Expression dimensionExpr, boolean retainedResult) { TypeMirror componentType = arrayType.getComponentType(); TypeElement iosArrayElement = typeUtil.getIosArray(componentType); boolean isPrimitive = componentType.getKind().isPrimitive(); String selector = (retainedResult ? "newArray" : "array") + "WithLength:" + (isPrimitive ? "" : "type:"); GeneratedExecutableElement methodElement = GeneratedExecutableElement.newMethodWithSelector( selector, iosArrayElement.asType(), iosArrayElement) .addModifiers(Modifier.PUBLIC, Modifier.STATIC); methodElement.addParameter(GeneratedVariableElement.newParameter( "length", typeUtil.getInt(), methodElement)); if (!isPrimitive) { methodElement.addParameter(GeneratedVariableElement.newParameter( "type", TypeUtil.IOS_CLASS.asType(), methodElement)); } MethodInvocation invocation = new MethodInvocation( new ExecutablePair(methodElement), arrayType, new SimpleName(iosArrayElement)); // Add the array length argument. invocation.addArgument(dimensionExpr.copy()); // Add the type argument for object arrays. if (!isPrimitive) { invocation.addArgument(new TypeLiteral(componentType, typeUtil)); } return invocation; } private MethodInvocation newMultiDimensionArrayInvocation( ArrayType arrayType, List<Expression> dimensions, boolean retainedResult) { assert dimensions.size() > 1; TypeMirror componentType = arrayType; for (int i = 0; i < dimensions.size(); i++) { assert TypeUtil.isArray(componentType); componentType = ((ArrayType) componentType).getComponentType(); } TypeElement iosArrayElement = typeUtil.getIosArray(componentType); ExecutableElement methodElement = getMultiDimensionMethod(componentType, iosArrayElement, retainedResult); MethodInvocation invocation = new MethodInvocation( new ExecutablePair(methodElement), arrayType, new SimpleName(iosArrayElement)); // Add the dimension count argument. invocation.addArgument(NumberLiteral.newIntLiteral(dimensions.size(), typeUtil)); // Create the dimensions array. ArrayInitializer dimensionsArg = new ArrayInitializer(typeUtil.getArrayType(typeUtil.getInt())); for (Expression e : dimensions) { dimensionsArg.addExpression(e.copy()); } invocation.addArgument(dimensionsArg); if (!componentType.getKind().isPrimitive()) { invocation.addArgument(new TypeLiteral(componentType, typeUtil)); } return invocation; } private ExecutableElement getMultiDimensionMethod( TypeMirror componentType, TypeElement iosArrayType, boolean retainedResult) { boolean isPrimitive = componentType.getKind().isPrimitive(); String selector = (retainedResult ? "newArray" : "array") + "WithDimensions:lengths:" + (isPrimitive ? "" : "type:"); GeneratedExecutableElement element = GeneratedExecutableElement.newMethodWithSelector( selector, TypeUtil.IOS_OBJECT_ARRAY.asType(), iosArrayType) .addModifiers(Modifier.PUBLIC, Modifier.STATIC); TypeMirror intType = typeUtil.getInt(); element.addParameter(GeneratedVariableElement.newParameter("dimensions", intType, element)); element.addParameter(GeneratedVariableElement.newParameter( "dimensionLengths", new PointerType(intType), element)); if (!isPrimitive) { element.addParameter(GeneratedVariableElement.newParameter( "type", TypeUtil.IOS_CLASS.asType(), element)); } return element; } // We must handle object array assignment before its children because if the // rhs is an array creation, we can optimize with "SetAndConsume". @Override public boolean visit(Assignment node) { Expression lhs = node.getLeftHandSide(); TypeMirror lhsType = lhs.getTypeMirror(); if (lhs instanceof ArrayAccess && !lhsType.getKind().isPrimitive()) { FunctionInvocation newAssignment = newArrayAssignment(node, (ArrayAccess) lhs, lhsType); node.replaceWith(newAssignment); newAssignment.accept(this); return false; } return true; } @Override public void endVisit(ArrayAccess node) { TypeMirror componentType = node.getTypeMirror(); TypeElement iosArrayElement = typeUtil.getIosArray(componentType); node.replaceWith(newArrayAccess( node, componentType, iosArrayElement, TranslationUtil.isAssigned(node))); } private Expression newArrayAccess( ArrayAccess arrayAccessNode, TypeMirror componentType, TypeElement iosArrayElement, boolean assignable) { String funcName = ElementUtil.getName(iosArrayElement) + "_Get"; TypeMirror returnType = componentType; TypeMirror declaredReturnType = componentType.getKind().isPrimitive() ? componentType : TypeUtil.ID_TYPE; if (assignable) { funcName += "Ref"; returnType = declaredReturnType = new PointerType(componentType); } FunctionElement element = new FunctionElement(funcName, declaredReturnType, iosArrayElement) .addParameters(iosArrayElement.asType(), typeUtil.getInt()); FunctionInvocation invocation = new FunctionInvocation(element, returnType); invocation.addArgument(arrayAccessNode.getArray().copy()); invocation.addArgument(arrayAccessNode.getIndex().copy()); if (assignable) { return new PrefixExpression(componentType, PrefixExpression.Operator.DEREFERENCE, invocation); } return invocation; } private FunctionInvocation newArrayAssignment( Assignment assignmentNode, ArrayAccess arrayAccessNode, TypeMirror componentType) { Assignment.Operator op = assignmentNode.getOperator(); assert !componentType.getKind().isPrimitive(); assert op == Assignment.Operator.ASSIGN; Expression value = TreeUtil.remove(assignmentNode.getRightHandSide()); Expression retainedValue = TranslationUtil.retainResult(value); String funcName = "IOSObjectArray_Set"; if (retainedValue != null) { funcName = "IOSObjectArray_SetAndConsume"; value = retainedValue; } TypeElement objArrayType = TypeUtil.IOS_OBJECT_ARRAY; TypeMirror idType = TypeUtil.ID_TYPE; FunctionElement element = new FunctionElement(funcName, idType, objArrayType) .addParameters(objArrayType.asType(), typeUtil.getInt(), idType); FunctionInvocation invocation = new FunctionInvocation(element, componentType); List<Expression> args = invocation.getArguments(); args.add(TreeUtil.remove(arrayAccessNode.getArray())); args.add(TreeUtil.remove(arrayAccessNode.getIndex())); args.add(value); return invocation; } @Override public void endVisit(FieldAccess node) { maybeRewriteArrayLength(node, node.getName(), node.getExpression()); } @Override public void endVisit(QualifiedName node) { maybeRewriteArrayLength(node, node.getName(), node.getQualifier()); } private void maybeRewriteArrayLength(Expression node, SimpleName name, Expression expr) { TypeMirror exprType = expr.getTypeMirror(); if (name.getIdentifier().equals("length") && TypeUtil.isArray(exprType)) { VariableElement sizeField = GeneratedVariableElement.newField( "size", typeUtil.getInt(), typeUtil.getIosArray(((ArrayType) exprType).getComponentType())); node.replaceWith(new FieldAccess(sizeField, TreeUtil.remove(expr))); } } @Override public void endVisit(InstanceofExpression node) { TypeMirror type = node.getRightOperand().getTypeMirror(); if (!TypeUtil.isArray(type) || ((ArrayType) type).getComponentType().getKind().isPrimitive()) { return; } GeneratedExecutableElement element = GeneratedExecutableElement.newMethodWithSelector( "isInstance", typeUtil.getBoolean(), TypeUtil.IOS_CLASS); element.addParameter( GeneratedVariableElement.newParameter("object", TypeUtil.ID_TYPE, element)); MethodInvocation invocation = new MethodInvocation(new ExecutablePair(element), new TypeLiteral(type, typeUtil)); invocation.addArgument(TreeUtil.remove(node.getLeftOperand())); node.replaceWith(invocation); } }