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.JCastOperation;
023: import com.google.gwt.dev.jjs.ast.JConditional;
024: import com.google.gwt.dev.jjs.ast.JExpression;
025: import com.google.gwt.dev.jjs.ast.JFieldRef;
026: import com.google.gwt.dev.jjs.ast.JIntLiteral;
027: import com.google.gwt.dev.jjs.ast.JLocalRef;
028: import com.google.gwt.dev.jjs.ast.JMethodCall;
029: import com.google.gwt.dev.jjs.ast.JNewArray;
030: import com.google.gwt.dev.jjs.ast.JNewInstance;
031: import com.google.gwt.dev.jjs.ast.JParameterRef;
032: import com.google.gwt.dev.jjs.ast.JPostfixOperation;
033: import com.google.gwt.dev.jjs.ast.JPrefixOperation;
034: import com.google.gwt.dev.jjs.ast.JThisRef;
035: import com.google.gwt.dev.jjs.ast.JVisitor;
036:
037: /**
038: * Analyzes an expression and make a number of static analysis flags available
039: * based on the information available solely through the expression.
040: *
041: * TODO: make this even smarter when we have real null analysis.
042: */
043: public class ExpressionAnalyzer extends JVisitor {
044: private boolean accessesField;
045: private boolean accessesLocal;
046: private boolean accessesParameter;
047: private boolean assignmentToField;
048: private boolean assignmentToLocal;
049: private boolean assignmentToParameter;
050: private boolean canThrowException;
051: private boolean createsObject;
052: private int inConditional;
053:
054: /**
055: * Does this expression read or write fields within the scope of the
056: * expression?
057: */
058: public boolean accessesField() {
059: return accessesField;
060: }
061:
062: /**
063: * Does this expression read or write locals within the scope of the
064: * expression?
065: */
066: public boolean accessesLocal() {
067: return accessesLocal;
068: }
069:
070: /**
071: * Does this expression read or write parameters within the scope of the
072: * expression?
073: */
074: public boolean accessesParameter() {
075: return accessesParameter;
076: }
077:
078: public boolean canThrowException() {
079: return canThrowException;
080: }
081:
082: public boolean createsObject() {
083: return createsObject;
084: }
085:
086: @Override
087: public void endVisit(JArrayRef x, Context ctx) {
088: /*
089: * In Java, array references can throw IndexOutOfBoundsExceptions, but this
090: * isn't the case for current GWT generated code. If we add a strict array
091: * bounds check later, this flag would need to reflect it.
092: */
093:
094: // If JArrayRef is null, this can throw a NullPointerException.
095: canThrowException = true;
096: }
097:
098: @Override
099: public void endVisit(JBinaryOperation x, Context ctx) {
100: if (x.isAssignment()) {
101: JExpression lhs = x.getLhs();
102: if (lhs instanceof JArrayRef) {
103: // Array store operations can throw ArrayStoreExceptions
104: canThrowException = true;
105: } else {
106: analyzeStore(lhs);
107: }
108: }
109: }
110:
111: @Override
112: public void endVisit(JCastOperation x, Context ctx) {
113: // Can throw ClassCastException
114: canThrowException = true;
115: }
116:
117: @Override
118: public void endVisit(JFieldRef x, Context ctx) {
119: accessesField = true;
120:
121: JExpression instance = x.getInstance();
122:
123: // Field references using this are always safe
124: if (instance instanceof JThisRef) {
125: return;
126: }
127:
128: /*
129: * We don't have enough information at this point to determine that a field
130: * reference is guaranteed to be safe.
131: *
132: * If a field reference is static, it can throw exceptions via a clinit().
133: *
134: * If a field is not static and isn't accessed using "this", the instance
135: * expression may be null.
136: */
137: canThrowException = true;
138: }
139:
140: @Override
141: public void endVisit(JLocalRef x, Context ctx) {
142: accessesLocal = true;
143: }
144:
145: @Override
146: public void endVisit(JMethodCall x, Context ctx) {
147: /*
148: * We can't assume anything about method calls right now, except that it
149: * can't assign to one of our locals or one of our parameters. It's possible
150: * that it could read from a field, assign to a field or throw an exception
151: * that we can't see.
152: */
153: assignmentToField = true;
154: accessesField = true;
155: canThrowException = true;
156: }
157:
158: @Override
159: public void endVisit(JNewArray x, Context ctx) {
160: /*
161: * If no array bounds, the new array is being automatically initialized. If
162: * there are side-effects, they'll show up when we visit the initializers.
163: */
164: if (x.dims == null) {
165: return;
166: }
167:
168: /*
169: * Can throw NegativeArraySizeException if we initialize an array with
170: * negative dimensions.
171: */
172: for (JExpression expression : x.dims) {
173: if (expression instanceof JIntLiteral) {
174: int value = ((JIntLiteral) expression).getValue();
175: if (value >= 0) {
176: continue;
177: }
178: }
179: canThrowException = true;
180: }
181: }
182:
183: @Override
184: public void endVisit(JNewInstance x, Context ctx) {
185: /*
186: * Due to implementation details, a new instance operation has no other
187: * possible side-effects.
188: */
189: createsObject = true;
190: }
191:
192: @Override
193: public void endVisit(JParameterRef x, Context ctx) {
194: accessesParameter = true;
195: }
196:
197: @Override
198: public void endVisit(JPostfixOperation x, Context ctx) {
199: // Unary operations that are modifying cause assignment side-effects.
200: if (x.getOp().isModifying()) {
201: analyzeStore(x.getArg());
202: }
203: }
204:
205: @Override
206: public void endVisit(JPrefixOperation x, Context ctx) {
207: // Unary operations that are modifying cause assignment side-effects.
208: if (x.getOp().isModifying()) {
209: analyzeStore(x.getArg());
210: }
211: }
212:
213: /**
214: * Does this expression make assignments to variables within the scope of the
215: * expression?
216: */
217: public boolean hasAssignment() {
218: return assignmentToField || assignmentToLocal
219: || assignmentToParameter;
220: }
221:
222: /**
223: * Does this expression make assignments to fields within the scope of the
224: * expression?
225: */
226: public boolean hasAssignmentToField() {
227: return assignmentToField;
228: }
229:
230: /**
231: * Does this expression make assignments to locals within the scope of the
232: * expression?
233: */
234: public boolean hasAssignmentToLocal() {
235: return assignmentToLocal;
236: }
237:
238: /**
239: * Does this expression make assignments to parameters within the scope of the
240: * expression?
241: */
242: public boolean hasAssignmentToParameter() {
243: return assignmentToParameter;
244: }
245:
246: @Override
247: public boolean visit(JBinaryOperation x, Context ctx) {
248: if (x.getOp() == JBinaryOperator.AND
249: || x.getOp() == JBinaryOperator.OR) {
250: accept(x.getLhs());
251: inConditional++;
252: accept(x.getRhs());
253: inConditional--;
254: return false;
255: }
256: return true;
257: }
258:
259: @Override
260: public boolean visit(JConditional x, Context ctx) {
261: accept(x.getIfTest());
262: inConditional++;
263: accept(x.getThenExpr());
264: accept(x.getElseExpr());
265: inConditional--;
266:
267: return false;
268: }
269:
270: /**
271: * Determined if the current expression conditionally executes, based on its
272: * parent expressions.
273: */
274: protected boolean isInConditional() {
275: return inConditional > 0;
276: }
277:
278: private void analyzeStore(JExpression expr) {
279: if (expr instanceof JFieldRef) {
280: assignmentToField = true;
281: } else if (expr instanceof JParameterRef) {
282: assignmentToParameter = true;
283: } else if (expr instanceof JLocalRef) {
284: assignmentToLocal = true;
285: }
286: }
287: }
|