/** * Creates a JMultiExpression from a set of JExpressionStatements, optionally terminated by a * JReturnStatement. If the method doesn't match this pattern, it returns <code>null</code>. * * <p>If a method has a non-void return statement and can be represented as a multi-expression, * the output of the multi-expression will be the return expression of the method. If the method * is void, the output of the multi-expression should be considered undefined. */ private JMultiExpression createMultiExpressionFromBody( JMethodBody body, boolean ignoringReturnValue) { JMultiExpression multi = new JMultiExpression(body.getSourceInfo()); CloneCalleeExpressionVisitor cloner = new CloneCalleeExpressionVisitor(); for (JStatement stmt : body.getStatements()) { if (stmt instanceof JExpressionStatement) { JExpressionStatement exprStmt = (JExpressionStatement) stmt; JExpression expr = exprStmt.getExpr(); JExpression clone = cloner.cloneExpression(expr); multi.addExpressions(clone); } else if (stmt instanceof JReturnStatement) { JReturnStatement returnStatement = (JReturnStatement) stmt; JExpression expr = returnStatement.getExpr(); if (expr != null) { if (!ignoringReturnValue || expr.hasSideEffects()) { JExpression clone = cloner.cloneExpression(expr); clone = maybeCast(clone, body.getMethod().getType()); multi.addExpressions(clone); } } // We hit an unconditional return; no need to evaluate anything else. break; } else { // Any other kind of statement won't be inlinable. return null; } } return multi; }
/** * Transform a call to a pruned instance method (or static impl) into a call to the null method, * which will be used to replace <code>x</code>. */ public static JMethodCall transformToNullMethodCall(JMethodCall x, JProgram program) { JExpression instance = x.getInstance(); List<JExpression> args = x.getArgs(); if (program.isStaticImpl(x.getTarget())) { instance = args.get(0); args = args.subList(1, args.size()); } else { /* * We assert that method must be non-static, otherwise it would have been * rescued. */ // assert !x.getTarget().isStatic(); /* * HACK HACK HACK: ControlFlowAnalyzer has special hacks for dealing with * ClassLiterals, which causes the body of ClassLiteralHolder's clinit to * never be rescured. This in turn causes invalid references to static * methods, which violates otherwise good assumptions about compiler * operation. * * TODO: Remove this when ControlFlowAnalyzer doesn't special-case * CLH.clinit(). */ if (x.getTarget().isStatic() && instance == null) { instance = program.getLiteralNull(); } } assert (instance != null); if (!instance.hasSideEffects()) { instance = program.getLiteralNull(); } JMethodCall newCall = new JMethodCall( x.getSourceInfo(), instance, program.getNullMethod(), primitiveTypeOrNullTypeOrArray(program, x.getType())); // Retain the original arguments, they will be evaluated for side effects. for (JExpression arg : args) { if (arg.hasSideEffects()) { newCall.addArg(arg); } } return newCall; }
@Override public boolean hasSideEffects() { for (JExpression expression : expressions) { if (expression.hasSideEffects()) { return true; } } return false; }
/** * Creates a multi expression from a list of expressions, removing expressions that do not have * side effects if possible. */ public static JExpression createOptimizedMultiExpression( boolean ignoringResult, List<JExpression> expressions) { int numberOfExpressions = expressions.size(); JExpression result = expressions.get(numberOfExpressions - 1); numberOfExpressions = expressions.size(); if (numberOfExpressions == 0) { return new JMultiExpression(SourceOrigin.UNKNOWN); } expressions = Lists.newArrayList( Collections2.filter( expressions.subList(0, numberOfExpressions - 1), Predicates.and( Predicates.notNull(), new Predicate<JExpression>() { @Override public boolean apply(JExpression expression) { return expression.hasSideEffects(); } }))); if (result != null && (!ignoringResult || result.hasSideEffects())) { expressions.add(result); } if (expressions.size() == 1) { // Do not create a multi expression if it consists only of the result. return expressions.iterator().next(); } SourceInfo info = expressions.size() > 0 ? expressions.get(0).getSourceInfo() : SourceOrigin.UNKNOWN; return new JMultiExpression(info, expressions); }
/** * Transform a reference to a pruned instance field into a reference to the null field, which will * be used to replace <code>x</code>. */ public static JFieldRef transformToNullFieldRef(JFieldRef x, JProgram program) { JExpression instance = x.getInstance(); /* * We assert that field must be non-static if it's an rvalue, otherwise it * would have been rescued. */ // assert !x.getField().isStatic(); /* * HACK HACK HACK: ControlFlowAnalyzer has special hacks for dealing with * ClassLiterals, which causes the body of ClassLiteralHolder's clinit to * never be rescured. This in turn causes invalid references to static * methods, which violates otherwise good assumptions about compiler * operation. * * TODO: Remove this when ControlFlowAnalyzer doesn't special-case * CLH.clinit(). */ if (x.getField().isStatic() && instance == null) { instance = program.getLiteralNull(); } assert instance != null; if (!instance.hasSideEffects()) { instance = program.getLiteralNull(); } JFieldRef fieldRef = new JFieldRef( x.getSourceInfo(), instance, program.getNullField(), x.getEnclosingType(), primitiveTypeOrNullTypeOrArray(program, x.getType())); return fieldRef; }
// Arguments for pruned parameters will be pushed right into a multiexpression that will be // evaluated with the next arg, e.g. m(arg1, (prunnedArg2, prunnedArg3, arg4)). private void maybeReplaceForPrunedParameters(JMethodCall x, Context ctx) { if (!priorParametersByMethod.containsKey(x.getTarget())) { // No parameter was pruned. return; } JMethodCall replacementCall = x.cloneWithoutParameters(); assert !x.getTarget().canBePolymorphic(); List<JParameter> originalParams = priorParametersByMethod.get(x.getTarget()); // The method and the call agree in the number of parameters. assert originalParams.size() == x.getArgs().size(); // Traverse the call arguments left to right. SourceInfo sourceInfo = x.getSourceInfo(); JMultiExpression unevaluatedArgumentsForPrunedParameters = new JMultiExpression(sourceInfo); List<JExpression> args = x.getArgs(); for (int currentArgumentIndex = 0; currentArgumentIndex < args.size(); ++currentArgumentIndex) { JExpression arg = args.get(currentArgumentIndex); // If the parameter was not pruned . if (referencedNonTypes.contains(originalParams.get(currentArgumentIndex))) { // Add the current argument to the list of unevaluated arguments and pass the multi // expression to the call. unevaluatedArgumentsForPrunedParameters.addExpressions(arg); replacementCall.addArg(unevaluatedArgumentsForPrunedParameters); // Reset the accumulating multi expression. unevaluatedArgumentsForPrunedParameters = new JMultiExpression(sourceInfo); } else if (arg.hasSideEffects()) { // If the argument was pruned and has sideffects accumulate it; otherwise discard. unevaluatedArgumentsForPrunedParameters.addExpressions(arg); } } if (unevaluatedArgumentsForPrunedParameters.isEmpty()) { // We are done, all (side effectful) parameters have been evaluated. ctx.replaceMe(replacementCall); return; } // If the last few parameters where pruned, we need to evaluate the (side effectful) arguments // for those parameters. if (replacementCall.getArgs().isEmpty()) { // All parameters have been pruned, replace by (prunedArg1, ..., prunedArgn, m()). unevaluatedArgumentsForPrunedParameters.addExpressions(replacementCall); ctx.replaceMe(unevaluatedArgumentsForPrunedParameters); return; } // Some parameters have been pruned from the end, replace by // m(arg1,..., (lastArg = lastUnprunedArg, remainingArgs, lastArg)) JExpression lastArg = Iterables.getLast(replacementCall.getArgs()); JLocal tempVar = createTempLocal( sourceInfo, Iterables.getLast(Iterables.filter(originalParams, Predicates.in(referencedNonTypes))) .getType()); unevaluatedArgumentsForPrunedParameters.addExpressions( 0, JProgram.createAssignment( lastArg.getSourceInfo(), new JLocalRef(sourceInfo, tempVar), lastArg)); unevaluatedArgumentsForPrunedParameters.addExpressions(new JLocalRef(sourceInfo, tempVar)); replacementCall.setArg( replacementCall.getArgs().size() - 1, unevaluatedArgumentsForPrunedParameters); ctx.replaceMe(replacementCall); }