001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.jjs.impl;
017:
018: import com.google.gwt.dev.jjs.ast.Context;
019: import com.google.gwt.dev.jjs.ast.JArrayRef;
020: import com.google.gwt.dev.jjs.ast.JBinaryOperation;
021: import com.google.gwt.dev.jjs.ast.JBinaryOperator;
022: import com.google.gwt.dev.jjs.ast.JExpression;
023: import com.google.gwt.dev.jjs.ast.JFieldRef;
024: import com.google.gwt.dev.jjs.ast.JLocal;
025: import com.google.gwt.dev.jjs.ast.JLocalRef;
026: import com.google.gwt.dev.jjs.ast.JMethodBody;
027: import com.google.gwt.dev.jjs.ast.JModVisitor;
028: import com.google.gwt.dev.jjs.ast.JNullLiteral;
029: import com.google.gwt.dev.jjs.ast.JParameterRef;
030: import com.google.gwt.dev.jjs.ast.JProgram;
031: import com.google.gwt.dev.jjs.ast.JThisRef;
032: import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
033:
034: import java.util.ArrayList;
035: import java.util.List;
036:
037: /**
038: * Replace any complex assignments that will cause problems down the road with
039: * broken expressions; replace side-effect expressions in the lhs with temps to
040: * prevent multiple evaluation.
041: */
042: public class CompoundAssignmentNormalizer {
043:
044: /**
045: * Breaks apart certain complex assignments.
046: */
047: private class BreakupAssignOpsVisitor extends JModVisitor {
048:
049: // @Override
050: public void endVisit(JBinaryOperation x, Context ctx) {
051: /*
052: * Convert to a normal divide operation so we can cast the result. Since
053: * the left hand size must be computed twice, we have to replace any
054: * left-hand side expressions that could have side effects with
055: * temporaries, so that they are only run once.
056: */
057: if (x.getOp() == JBinaryOperator.ASG_DIV
058: && x.getType() != program.getTypePrimitiveFloat()
059: && x.getType() != program.getTypePrimitiveDouble()) {
060:
061: /*
062: * Convert to a normal divide operation so we can cast the result. Since
063: * the left hand size must be computed twice, we have to replace any
064: * left-hand side expressions that could have side effects with
065: * temporaries, so that they are only run once.
066: */
067: final int pushUsedLocals = localIndex;
068: JMultiExpression multi = new JMultiExpression(program,
069: x.getSourceInfo());
070: ReplaceSideEffectsInLvalue replacer = new ReplaceSideEffectsInLvalue(
071: multi);
072: JExpression newLhs = replacer.accept(x.getLhs());
073: localIndex = pushUsedLocals;
074:
075: JNullLiteral litNull = program.getLiteralNull();
076: JBinaryOperation operation = new JBinaryOperation(
077: program, x.getSourceInfo(), newLhs.getType(),
078: JBinaryOperator.DIV, newLhs, x.getRhs());
079: JBinaryOperation asg = new JBinaryOperation(program, x
080: .getSourceInfo(), newLhs.getType(),
081: JBinaryOperator.ASG, newLhs, operation);
082:
083: JMultiExpression multiExpr = replacer.getMultiExpr();
084: if (multiExpr.exprs.isEmpty()) {
085: // just use the split assignment expression
086: ctx.replaceMe(asg);
087: } else {
088: // add the assignment as the last item in the multi
089: multi.exprs.add(asg);
090: ctx.replaceMe(multi);
091: }
092: }
093: }
094:
095: // @Override
096: public void endVisit(JMethodBody x, Context ctx) {
097: clearLocals();
098: currentMethodBody = null;
099: }
100:
101: // @Override
102: public boolean visit(JMethodBody x, Context ctx) {
103: currentMethodBody = x;
104: clearLocals();
105: return true;
106: }
107: }
108:
109: /**
110: * Replaces side effects in lvalue.
111: */
112: private class ReplaceSideEffectsInLvalue extends JModVisitor {
113:
114: private final JMultiExpression multi;
115:
116: ReplaceSideEffectsInLvalue(JMultiExpression multi) {
117: this .multi = multi;
118: }
119:
120: public JMultiExpression getMultiExpr() {
121: return multi;
122: }
123:
124: // @Override
125: public boolean visit(JArrayRef x, Context ctx) {
126: JExpression newInstance = possiblyReplace(x.getInstance());
127: JExpression newIndexExpr = possiblyReplace(x.getIndexExpr());
128: if (newInstance != x.getInstance()
129: || newIndexExpr != x.getIndexExpr()) {
130: JArrayRef newExpr = new JArrayRef(program, x
131: .getSourceInfo(), newInstance, newIndexExpr);
132: ctx.replaceMe(newExpr);
133: }
134: return false;
135: }
136:
137: // @Override
138: public boolean visit(JFieldRef x, Context ctx) {
139: if (x.getInstance() != null) {
140: JExpression newInstance = possiblyReplace(x
141: .getInstance());
142: if (newInstance != x.getInstance()) {
143: JFieldRef newExpr = new JFieldRef(program, x
144: .getSourceInfo(), newInstance,
145: x.getField(), x.getEnclosingType());
146: ctx.replaceMe(newExpr);
147: }
148: }
149: return false;
150: }
151:
152: // @Override
153: public boolean visit(JLocalRef x, Context ctx) {
154: return false;
155: }
156:
157: // @Override
158: public boolean visit(JParameterRef x, Context ctx) {
159: return false;
160: }
161:
162: // @Override
163: public boolean visit(JThisRef x, Context ctx) {
164: return false;
165: }
166:
167: private JExpression possiblyReplace(JExpression x) {
168: if (!x.hasSideEffects()) {
169: return x;
170: }
171:
172: // Create a temp local
173: JLocal tempLocal = getTempLocal();
174:
175: // Create an assignment for this temp and add it to multi.
176: JLocalRef tempRef = new JLocalRef(program, x
177: .getSourceInfo(), tempLocal);
178: JBinaryOperation asg = new JBinaryOperation(program, x
179: .getSourceInfo(), x.getType(), JBinaryOperator.ASG,
180: tempRef, x);
181: multi.exprs.add(asg);
182: // Update me with the temp
183: return tempRef;
184: }
185: }
186:
187: public static void exec(JProgram program) {
188: new CompoundAssignmentNormalizer(program).execImpl();
189: }
190:
191: private JMethodBody currentMethodBody;
192: private int localIndex;
193: private final JProgram program;
194: private final List/* <JLocal> */tempLocals = new ArrayList/* <JLocal> */();
195:
196: private CompoundAssignmentNormalizer(JProgram program) {
197: this .program = program;
198: }
199:
200: private void clearLocals() {
201: tempLocals.clear();
202: localIndex = 0;
203: }
204:
205: private void execImpl() {
206: BreakupAssignOpsVisitor breaker = new BreakupAssignOpsVisitor();
207: breaker.accept(program);
208: }
209:
210: private JLocal getTempLocal() {
211: if (localIndex < tempLocals.size()) {
212: return (JLocal) tempLocals.get(localIndex++);
213: }
214: JLocal newTemp = program.createLocal(null,
215: ("$t" + localIndex++).toCharArray(), program
216: .getTypeVoid(), false, currentMethodBody);
217: tempLocals.add(newTemp);
218: return newTemp;
219: }
220:
221: }
|