@Override public boolean visit(ForStatement node) { List<Expression> initializers = node.getInitializers(); // The for-loop initializers can either be a single variable declaration // expression or a list of initializer expressions. if (initializers.size() == 1 && initializers.get(0) instanceof VariableDeclarationExpression) { VariableDeclarationExpression decl = (VariableDeclarationExpression) initializers.get(0); extractVariableDeclarationFragments( decl.getFragments(), TreeUtil.asStatementList(node).subList(0, 0)); } else { extractExpressionList(initializers, TreeUtil.asStatementList(node).subList(0, 0), false); } Expression expr = node.getExpression(); if (expr != null) { newExpression(expr); expr.accept(this); List<VariableAccess> toExtract = getUnsequencedAccesses(); if (!toExtract.isEmpty()) { // Convert "if (;cond;)" into "if (;;) { if (!(cond)) break; ...}". List<Statement> stmtList = TreeUtil.asStatementList(node.getBody()).subList(0, 0); extractOrderedAccesses(stmtList, currentTopNode, toExtract); stmtList.add(createLoopTermination(node.getExpression())); node.setExpression(null); } } extractExpressionList(node.getUpdaters(), TreeUtil.asStatementList(node.getBody()), true); node.getBody().accept(this); return false; }
private void extractVariableDeclarationFragments( List<VariableDeclarationFragment> fragments, List<Statement> stmtList) { for (int i = 0; i < fragments.size(); i++) { VariableDeclarationFragment frag = fragments.get(i); Expression init = frag.getInitializer(); if (init == null) { continue; } newExpression(init); init.accept(this); List<VariableAccess> toExtract = getUnsequencedAccesses(); if (!toExtract.isEmpty()) { if (i > 0) { // Extract all fragments before the current one to preserve ordering. VariableDeclarationStatement newDecl = new VariableDeclarationStatement(fragments.get(0).copy()); for (int j = 1; j < i; j++) { newDecl.getFragments().add(fragments.get(j).copy()); } stmtList.add(newDecl); fragments.subList(0, i).clear(); } extractOrderedAccesses(stmtList, currentTopNode, toExtract); i = 0; } } }
/** * Returns TRUE of FALSE if 'expr' is a boolean expression and its value is known statically. The * caller should be careful when replacing this expression as it may have side effects. */ private Boolean getKnownValue(Expression expr) { Object value = expr.getConstantValue(); if (value instanceof Boolean) { return (Boolean) value; } switch (expr.getKind()) { case BOOLEAN_LITERAL: return ((BooleanLiteral) expr).booleanValue(); case INFIX_EXPRESSION: { InfixExpression infixExpr = (InfixExpression) expr; InfixExpression.Operator op = infixExpr.getOperator(); if (op == CONDITIONAL_AND || op == CONDITIONAL_OR) { // We assume that this node has already been visited and pruned so // if it has a known value, it will be equal to the last operand. List<Expression> operands = infixExpr.getOperands(); Boolean lastOperand = getKnownValue(operands.get(operands.size() - 1)); if (lastOperand != null && lastOperand.booleanValue() == (op == CONDITIONAL_OR)) { return lastOperand; } } return null; } case PARENTHESIZED_EXPRESSION: return getKnownValue(((ParenthesizedExpression) expr).getExpression()); default: return null; } }
private boolean needsIdCast(Expression lhs, Expression rhs) { ITypeBinding lhsType = lhs.getTypeBinding(); ITypeBinding rhsType = rhs.getTypeBinding(); return !lhsType.isPrimitive() && !rhsType.isPrimitive() && !lhsType.isAssignmentCompatible(rhsType) && !rhsType.isAssignmentCompatible(lhsType); }
@Override public boolean visit(SuperConstructorInvocation node) { newExpression(node); for (Expression arg : node.getArguments()) { arg.accept(this); } extractUnsequenced(node); return false; }
@Override public boolean visit(Assignment node) { Expression lhs = node.getLeftHandSide(); IVariableBinding lhsVar = TreeUtil.getVariableBinding(lhs); // Access order is important. If the lhs is a variable, then we must record // its access after visiting the rhs. Otherwise, visit both sides. if (lhsVar == null) { lhs.accept(this); } node.getRightHandSide().accept(this); addVariableAccess(lhsVar, node, true); return false; }
@Override public void endVisit(SuperConstructorInvocation node) { Expression outerExpression = node.getExpression(); if (outerExpression == null) { return; } node.setExpression(null); ITypeBinding outerExpressionType = outerExpression.getTypeBinding(); GeneratedMethodBinding binding = new GeneratedMethodBinding(node.getMethodBinding().getMethodDeclaration()); node.setMethodBinding(binding); node.getArguments().add(0, outerExpression); binding.addParameter(0, outerExpressionType); }
private ITypeBinding getDeclaredType(Expression expr) { IVariableBinding var = TreeUtil.getVariableBinding(expr); if (var != null) { return var.getVariableDeclaration().getType(); } switch (expr.getKind()) { case CLASS_INSTANCE_CREATION: return typeEnv.resolveIOSType("id"); case FUNCTION_INVOCATION: { ITypeBinding returnType = ((FunctionInvocation) expr).getFunctionBinding().getReturnType(); if (returnType.isTypeVariable()) { return typeEnv.resolveIOSType("id"); } return returnType; } case METHOD_INVOCATION: { MethodInvocation invocation = (MethodInvocation) expr; IMethodBinding method = invocation.getMethodBinding(); // Object receiving the message, or null if it's a method in this class. Expression receiver = invocation.getExpression(); ITypeBinding receiverType = receiver != null ? receiver.getTypeBinding() : method.getDeclaringClass(); return getDeclaredReturnType(method, receiverType); } case PARENTHESIZED_EXPRESSION: return getDeclaredType(((ParenthesizedExpression) expr).getExpression()); case SUPER_METHOD_INVOCATION: { SuperMethodInvocation invocation = (SuperMethodInvocation) expr; IMethodBinding method = invocation.getMethodBinding(); if (invocation.getQualifier() != null) { // For a qualified super invocation, the statement generator will look // up the IMP using instanceMethodForSelector. if (!method.getReturnType().isPrimitive()) { return typeEnv.resolveIOSType("id"); } else { return null; } } return getDeclaredReturnType( method, TreeUtil.getOwningType(invocation).getTypeBinding().getSuperclass()); } default: return null; } }
private void visitAndExtract(Expression expr, Statement stmt) { if (expr != null) { newExpression(expr); expr.accept(this); extractUnsequenced(stmt); } }
/** * Returns an expression containing the side effects of the given expression. The evaluated result * of the expression may differ from the original. */ private Expression extractSideEffects(Expression expr) { switch (expr.getKind()) { case INFIX_EXPRESSION: { List<Expression> operands = ((InfixExpression) expr).getOperands(); Expression lastOperand = operands.remove(operands.size() - 1); lastOperand = extractSideEffects(lastOperand); if (lastOperand != null) { operands.add(lastOperand); } if (operands.size() == 1) { return operands.remove(0); } return TreeUtil.remove(expr); } case PARENTHESIZED_EXPRESSION: { Expression sideEffects = extractSideEffects(((ParenthesizedExpression) expr).getExpression()); if (sideEffects != null) { return ParenthesizedExpression.parenthesize(sideEffects); } return null; } default: return null; } }
@Override public void endVisit(CastExpression node) { ITypeBinding type = node.getType().getTypeBinding(); Expression expr = node.getExpression(); ITypeBinding exprType = expr.getTypeBinding(); // TODO(kirbs): Implement correct conversion of Java 8 intersection types to Objective-C. if (node.getType().isIntersectionType() && !Options.isJava8Translator()) { // Technically we can't currently get here, but as we add support and change flags in the // future this should alert us to implement intersection types. assert false : "not implemented yet"; } if (BindingUtil.isFloatingPoint(exprType)) { assert type.isPrimitive(); // Java wouldn't allow a cast from primitive to non-primitive. switch (type.getBinaryName().charAt(0)) { case 'J': node.replaceWith(rewriteFloatToIntegralCast(type, expr, "JreFpToLong", type)); return; case 'C': node.replaceWith(rewriteFloatToIntegralCast(type, expr, "JreFpToChar", type)); return; case 'B': case 'S': case 'I': node.replaceWith( rewriteFloatToIntegralCast(type, expr, "JreFpToInt", typeEnv.resolveJavaType("int"))); return; } // else fall-through. } // Lean on Java's type-checking. if (!type.isPrimitive() && exprType.isAssignmentCompatible(type.getErasure())) { node.replaceWith(TreeUtil.remove(expr)); return; } FunctionInvocation castCheck = createCastCheck(type, expr); if (castCheck != null) { node.setExpression(castCheck); } }
private IfStatement createLoopTermination(Expression loopCondition) { IfStatement newIf = new IfStatement(); newIf.setExpression( new PrefixExpression( typeEnv.resolveJavaType("boolean"), PrefixExpression.Operator.NOT, ParenthesizedExpression.parenthesize(loopCondition.copy()))); newIf.setThenStatement(new BreakStatement()); return newIf; }
private void extractExpressionList( List<Expression> expressions, List<Statement> stmtList, boolean extractModifiedExpression) { for (int i = 0; i < expressions.size(); i++) { Expression expr = expressions.get(i); newExpression(expr); expr.accept(this); List<VariableAccess> unsequencedAccesses = getUnsequencedAccesses(); if (!unsequencedAccesses.isEmpty()) { for (int j = 0; j < i; j++) { stmtList.add(new ExpressionStatement(expressions.get(j).copy())); } expressions.subList(0, i).clear(); extractOrderedAccesses(stmtList, currentTopNode, unsequencedAccesses); i = 0; if (extractModifiedExpression) { stmtList.add(new ExpressionStatement(expressions.get(0).copy())); expressions.remove(0); i = -1; } } } }
@Override public boolean visit(AssertStatement node) { Expression expr = node.getExpression(); visitAndExtract(expr, node); Expression msg = node.getMessage(); if (msg != null) { newExpression(msg); msg.accept(this); List<VariableAccess> toExtract = getUnsequencedAccesses(); if (!toExtract.isEmpty()) { // If the message expression needs any extraction, then we first extract // the entire boolean expression to preserve ordering between the two. IVariableBinding exprVar = new GeneratedVariableBinding( "unseq$" + count++, 0, expr.getTypeBinding(), false, false, null, currentMethod); TreeUtil.insertBefore( node, new VariableDeclarationStatement(exprVar, node.getExpression().copy())); node.setExpression(new SimpleName(exprVar)); extractOrderedAccesses( TreeUtil.asStatementList(node).subList(0, 0), currentTopNode, toExtract); } } return false; }
// Some native objective-c methods are declared to return NSUInteger. private boolean returnValueNeedsIntCast(Expression arg) { IMethodBinding methodBinding = TreeUtil.getMethodBinding(arg); assert methodBinding != null; if (arg.getParent() instanceof ExpressionStatement) { // Avoid "unused return value" warning. return false; } String methodName = nameTable.getMethodSelector(methodBinding); if (methodName.equals("hash") && methodBinding.getReturnType().isEqualTo(typeEnv.resolveJavaType("int"))) { return true; } if (typeEnv.isStringType(methodBinding.getDeclaringClass()) && methodName.equals("length")) { return true; } return false; }
private boolean needsCast(Expression expr, ITypeBinding expectedType, boolean shouldCastFromId) { ITypeBinding declaredType = getDeclaredType(expr); if (declaredType == null) { return false; } ITypeBinding exprType = typeEnv.mapType(expr.getTypeBinding()); declaredType = typeEnv.mapType(declaredType); if ( // In general we do not need to cast primitive types. exprType.isPrimitive() // In most cases we don't need to cast from an id type. However, if the // expression is being dereferenced then the compiler needs the type // info. || (typeEnv.isIdType(declaredType) && !shouldCastFromId) // If the declared type can be assigned into the actual type, or the // expected type, then the compiler already has sufficient type info. || typeEnv.isIdType(exprType) || typeEnv.isIdType(expectedType) || declaredType.isAssignmentCompatible(exprType) || declaredType.isAssignmentCompatible(expectedType)) { return false; } return true; }
private void addCast(Expression expr) { ITypeBinding exprType = typeEnv.mapType(expr.getTypeBinding()); CastExpression castExpr = new CastExpression(exprType, null); expr.replaceWith(ParenthesizedExpression.parenthesize(castExpr)); castExpr.setExpression(expr); }
private void extractConditionalExpression( List<Statement> stmtList, ConditionalExpression conditional, List<VariableAccess> toExtract) { Expression condition = conditional.getExpression(); Expression thenExpr = conditional.getThenExpression(); Expression elseExpr = conditional.getElseExpression(); List<VariableAccess> conditionAccesses = Lists.newArrayList(); List<VariableAccess> thenAccesses = Lists.newArrayList(); List<VariableAccess> elseAccesses = Lists.newArrayList(); boolean needsExtraction = false; for (VariableAccess access : toExtract) { TreeNode node = access.expression; while (node.getParent() != conditional) { node = node.getParent(); } if (node == condition) { conditionAccesses.add(access); } else if (node == thenExpr) { thenAccesses.add(access); } else if (node == elseExpr) { elseAccesses.add(access); } else { throw new AssertionError(); } if (node != condition && access.isModification) { // We only need to extract an if-statement if there is a modification // that executes conditionally. needsExtraction = true; } } extractOrderedAccesses(stmtList, condition, conditionAccesses); // The recursive call might replace the condition child. condition = conditional.getExpression(); if (needsExtraction) { IVariableBinding resultVar = new GeneratedVariableBinding( "unseq$" + count++, 0, conditional.getTypeBinding(), false, false, null, currentMethod); conditional.replaceWith(new SimpleName(resultVar)); stmtList.add(new VariableDeclarationStatement(resultVar, null)); IfStatement newIf = new IfStatement(); newIf.setExpression(condition.copy()); stmtList.add(newIf); Block thenBlock = new Block(); newIf.setThenStatement(thenBlock); List<Statement> thenStmts = thenBlock.getStatements(); extractOrderedAccesses(thenStmts, thenExpr, thenAccesses); // The recursive call might replace the then expression child. thenExpr = conditional.getThenExpression(); thenStmts.add( new ExpressionStatement(new Assignment(new SimpleName(resultVar), thenExpr.copy()))); Block elseBlock = new Block(); newIf.setElseStatement(elseBlock); List<Statement> elseStmts = elseBlock.getStatements(); extractOrderedAccesses(elseStmts, elseExpr, elseAccesses); // The recursive call might replace the else expression child. elseExpr = conditional.getElseExpression(); elseStmts.add( new ExpressionStatement(new Assignment(new SimpleName(resultVar), elseExpr.copy()))); } else { extractOrderedAccesses(stmtList, thenExpr, thenAccesses); extractOrderedAccesses(stmtList, elseExpr, elseAccesses); } }