All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dev.jjs.impl.CompoundAssignmentNormalizer Maven / Gradle / Ivy

/*
 * Copyright 2008 Google Inc.
 *
 * 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.gwt.dev.jjs.impl;

import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JArrayRef;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JFieldRef;
import com.google.gwt.dev.jjs.ast.JIntLiteral;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JLocalRef;
import com.google.gwt.dev.jjs.ast.JLongLiteral;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JThisRef;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JUnaryOperator;
import com.google.gwt.dev.jjs.ast.js.JMultiExpression;

/**
 * 

* Replace problematic compound assignments with a sequence of simpler * operations, all of which are either simple assignments or are non-assigning * operations. When doing so, be careful that side effects happen exactly once * and that the order of any side effects is preserved. The choice of which * assignments to replace is made in subclasses; they must override the three * shouldBreakUp() methods. *

* *

* Note that because AST nodes are mutable, they cannot be reused in different * parts of the same tree. Instead, the node must be cloned before each * insertion into a tree other than the first. *

*/ public abstract class CompoundAssignmentNormalizer { /** * Breaks apart certain complex assignments. */ private class BreakupAssignOpsVisitor extends JModVisitorWithTemporaryVariableCreation { /** * Replaces side effects in lvalue. */ private class ReplaceSideEffectsInLvalue extends JModVisitor { private final JMultiExpression multi; ReplaceSideEffectsInLvalue(JMultiExpression multi) { this.multi = multi; } public JMultiExpression getMultiExpr() { return multi; } @Override public boolean visit(JArrayRef x, Context ctx) { JExpression newInstance = possiblyReplace(x.getInstance()); JExpression newIndexExpr = possiblyReplace(x.getIndexExpr()); if (newInstance != x.getInstance() || newIndexExpr != x.getIndexExpr()) { JArrayRef newExpr = new JArrayRef(x.getSourceInfo(), newInstance, newIndexExpr); ctx.replaceMe(newExpr); } return false; } @Override public boolean visit(JFieldRef x, Context ctx) { if (x.getInstance() != null) { JExpression newInstance = possiblyReplace(x.getInstance()); if (newInstance != x.getInstance()) { JFieldRef newExpr = new JFieldRef(x.getSourceInfo(), newInstance, x.getField(), x.getEnclosingType()); ctx.replaceMe(newExpr); } } return false; } @Override public boolean visit(JLocalRef x, Context ctx) { return false; } @Override public boolean visit(JParameterRef x, Context ctx) { return false; } @Override public boolean visit(JThisRef x, Context ctx) { return false; } private JExpression possiblyReplace(JExpression x) { if (!x.hasSideEffects()) { return x; } // Create a temp local JLocal tempLocal = createTempLocal(x.getSourceInfo(), x.getType()); // Create an assignment for this temp and add it to multi. JLocalRef tempRef = new JLocalRef(x.getSourceInfo(), tempLocal); JBinaryOperation asg = new JBinaryOperation(x.getSourceInfo(), x.getType(), JBinaryOperator.ASG, tempRef, x); multi.addExpressions(asg); // Update me with the temp return cloner.cloneExpression(tempRef); } } @Override protected String newTemporaryLocalName(SourceInfo info, JType type, JMethodBody methodBody) { return CompoundAssignmentNormalizer.this.newTemporaryLocalName(info, type, methodBody); } @Override public void endVisit(JBinaryOperation x, Context ctx) { JBinaryOperator op = x.getOp(); if (op.getNonAssignmentOf() == null) { return; } if (!shouldBreakUp(x)) { return; } /* * Convert to an assignment and binary operation. Since the left hand size * must be computed twice, we have to replace any left-hand side * expressions that could have side effects with temporaries, so that they * are only run once. */ ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(new JMultiExpression(x.getSourceInfo())); JExpression newLhs = replacer.accept(x.getLhs()); JExpression operation = new JBinaryOperation(x.getSourceInfo(), newLhs.getType(), op.getNonAssignmentOf(), newLhs, x.getRhs()); operation = modifyResultOperation((JBinaryOperation) operation); // newLhs is cloned below because it was used in operation JBinaryOperation asg = new JBinaryOperation(x.getSourceInfo(), newLhs.getType(), JBinaryOperator.ASG, cloner .cloneExpression(newLhs), operation); JMultiExpression multiExpr = replacer.getMultiExpr(); if (multiExpr.isEmpty()) { // just use the split assignment expression ctx.replaceMe(asg); } else { // add the assignment as the last item in the multi multiExpr.addExpressions(asg); ctx.replaceMe(multiExpr); } } @Override public void endVisit(JPostfixOperation x, Context ctx) { JUnaryOperator op = x.getOp(); if (!op.isModifying()) { return; } if (!shouldBreakUp(x)) { return; } // Convert into a comma operation, such as: // (t = x, x += 1, t) // First, replace the arg with a non-side-effect causing one. JMultiExpression multi = new JMultiExpression(x.getSourceInfo()); ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(multi); JExpression newArg = replacer.accept(x.getArg()); JExpression expressionReturn = expressionToReturn(newArg); // Now generate the appropriate expressions. JLocal tempLocal = createTempLocal(x.getSourceInfo(), expressionReturn.getType()); // t = x JLocalRef tempRef = new JLocalRef(x.getSourceInfo(), tempLocal); JBinaryOperation asg = new JBinaryOperation(x.getSourceInfo(), x.getType(), JBinaryOperator.ASG, tempRef, expressionReturn); multi.addExpressions(asg); // x += 1 asg = createAsgOpFromUnary(newArg, op); // Break the resulting asg op before adding to multi. multi.addExpressions(accept(asg)); // t tempRef = new JLocalRef(x.getSourceInfo(), tempLocal); multi.addExpressions(tempRef); ctx.replaceMe(multi); } @Override public void endVisit(JPrefixOperation x, Context ctx) { JUnaryOperator op = x.getOp(); if (!op.isModifying()) { return; } if (!shouldBreakUp(x)) { return; } // Convert into the equivalent binary assignment operation, such as: // x += 1 JBinaryOperation asg = createAsgOpFromUnary(x.getArg(), op); // Visit the result to break it up even more. ctx.replaceMe(accept(asg)); } private JBinaryOperation createAsgOpFromUnary(JExpression arg, JUnaryOperator op) { JBinaryOperator newOp; if (op == JUnaryOperator.INC) { newOp = JBinaryOperator.ASG_ADD; } else if (op == JUnaryOperator.DEC) { newOp = JBinaryOperator.ASG_SUB; } else { throw new InternalCompilerException("Unexpected modifying unary operator: " + String.valueOf(op.getSymbol())); } JExpression one; if (arg.getType() == JPrimitiveType.LONG) { // use an explicit long, so that LongEmulationNormalizer does not get // confused one = JLongLiteral.get(1); } else { // int is safe to add to all other types one = JIntLiteral.get(1); } // arg is cloned below because the caller is allowed to use it somewhere JBinaryOperation asg = new JBinaryOperation(arg.getSourceInfo(), arg.getType(), newOp, cloner .cloneExpression(arg), one); return asg; } } private final CloneExpressionVisitor cloner = new CloneExpressionVisitor(); public void accept(JNode node) { BreakupAssignOpsVisitor breaker = new BreakupAssignOpsVisitor(); breaker.accept(node); } // Name to assign to temporaries. All temporaries are created with the same name, which is // not a problem as they are referred to by reference. // {@link GenerateJavaScriptAst.FixNameClashesVisitor} will resolve into unique names when // needed. private static final String TEMP_LOCAL_NAME = "$tmp"; /** * Gets a new temporary local variable name in {@code methodBody}. Locals might have duplicate * names as they are always referred to by reference. * {@link GenerateJavaScriptAST} will attempt coalesce variables of same name. * *

Subclasses might decide on different approaches to naming local temporaries. */ protected String newTemporaryLocalName(SourceInfo info, JType type, JMethodBody methodBody) { return TEMP_LOCAL_NAME; } /** * Decide what expression to return when breaking up a compound assignment of * the form lhs op= rhs. By default the lhs is * returned. */ protected JExpression expressionToReturn(JExpression lhs) { return lhs; } /** * Decide what expression to return when breaking up a compound assignment of * the form lhs op= rhs. The breakup creates an expression of the * form lhs = lhs op rhs, and the right hand side of the newly * created expression is passed to this method. */ protected JExpression modifyResultOperation(JBinaryOperation op) { return op; } protected abstract boolean shouldBreakUp(JBinaryOperation x); protected abstract boolean shouldBreakUp(JPostfixOperation x); protected abstract boolean shouldBreakUp(JPrefixOperation x); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy