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.JAbsentArrayDimension;
020: import com.google.gwt.dev.jjs.ast.JArrayRef;
021: import com.google.gwt.dev.jjs.ast.JArrayType;
022: import com.google.gwt.dev.jjs.ast.JBinaryOperation;
023: import com.google.gwt.dev.jjs.ast.JBinaryOperator;
024: import com.google.gwt.dev.jjs.ast.JExpression;
025: import com.google.gwt.dev.jjs.ast.JIntLiteral;
026: import com.google.gwt.dev.jjs.ast.JLiteral;
027: import com.google.gwt.dev.jjs.ast.JMethod;
028: import com.google.gwt.dev.jjs.ast.JMethodCall;
029: import com.google.gwt.dev.jjs.ast.JModVisitor;
030: import com.google.gwt.dev.jjs.ast.JNewArray;
031: import com.google.gwt.dev.jjs.ast.JNullType;
032: import com.google.gwt.dev.jjs.ast.JPrimitiveType;
033: import com.google.gwt.dev.jjs.ast.JProgram;
034: import com.google.gwt.dev.jjs.ast.JReferenceType;
035: import com.google.gwt.dev.jjs.ast.JType;
036: import com.google.gwt.dev.jjs.ast.js.JsonArray;
037:
038: /**
039: * Replace array accesses and instantiations with calls to the Array class.
040: * Depends on {@link com.google.gwt.dev.jjs.impl.CompoundAssignmentNormalizer}
041: * and {@link com.google.gwt.dev.jjs.impl.CastNormalizer} having already run.
042: */
043: public class ArrayNormalizer {
044:
045: private class ArrayVisitor extends JModVisitor {
046:
047: @Override
048: public void endVisit(JBinaryOperation x, Context ctx) {
049: if (x.getOp() == JBinaryOperator.ASG
050: && x.getLhs() instanceof JArrayRef) {
051: JArrayRef arrayRef = (JArrayRef) x.getLhs();
052: if (arrayRef.getType() instanceof JNullType) {
053: // will generate a null pointer exception instead
054: return;
055: }
056: JArrayType arrayType = (JArrayType) arrayRef
057: .getInstance().getType();
058: JType elementType = arrayType.getElementType();
059:
060: /*
061: * See if we need to do a checked store. Primitives and (effectively)
062: * final are statically correct.
063: */
064: if (elementType instanceof JReferenceType) {
065: if (!((JReferenceType) elementType).isFinal()
066: || elementType != x.getRhs().getType()) {
067: // replace this assignment with a call to setCheck()
068: JMethodCall call = new JMethodCall(program, x
069: .getSourceInfo(), null, setCheckMethod);
070: call.getArgs().add(arrayRef.getInstance());
071: call.getArgs().add(arrayRef.getIndexExpr());
072: call.getArgs().add(x.getRhs());
073: ctx.replaceMe(call);
074: }
075: }
076: }
077: }
078:
079: @Override
080: public void endVisit(JNewArray x, Context ctx) {
081: JArrayType type = x.getArrayType();
082:
083: if (x.initializers != null) {
084: processInitializers(x, ctx, type);
085: } else if (type.getDims() == 1) {
086: processDim(x, ctx, type);
087: } else {
088: processDims(x, ctx, type);
089: }
090: }
091:
092: /**
093: * @see com.google.gwt.lang.Array regarding seed types
094: */
095: private JIntLiteral getSeedTypeLiteralFor(JType type) {
096: if (type instanceof JPrimitiveType) {
097: if (type == program.getTypePrimitiveBoolean()) {
098: // The boolean type, thus false (index 2)
099: return program.getLiteralInt(2);
100: } else {
101: // A numeric type, thus zero (index 1).
102: return program.getLiteralInt(1);
103: }
104: }
105: // An Object type, thus null (index 0).
106: return program.getLiteralInt(0);
107: }
108:
109: private void processDim(JNewArray x, Context ctx,
110: JArrayType arrayType) {
111: // override the type of the called method with the array's type
112: JMethodCall call = new JMethodCall(program, x
113: .getSourceInfo(), null, initDim, arrayType);
114: JLiteral classLit = program.getLiteralClass(arrayType);
115: JLiteral typeIdLit = program.getLiteralInt(program
116: .getTypeId(arrayType));
117: JLiteral queryIdLit = program
118: .getLiteralInt(tryGetQueryId(arrayType));
119: JType leafType = arrayType.getLeafType();
120: JExpression dim = x.dims.get(0);
121:
122: call.getArgs().add(classLit);
123: call.getArgs().add(typeIdLit);
124: call.getArgs().add(queryIdLit);
125: call.getArgs().add(dim);
126: call.getArgs().add(getSeedTypeLiteralFor(leafType));
127: ctx.replaceMe(call);
128: }
129:
130: private void processDims(JNewArray x, Context ctx,
131: JArrayType arrayType) {
132: // override the type of the called method with the array's type
133: JMethodCall call = new JMethodCall(program, x
134: .getSourceInfo(), null, initDims, arrayType);
135: JsonArray classLitList = new JsonArray(program);
136: JsonArray typeIdList = new JsonArray(program);
137: JsonArray queryIdList = new JsonArray(program);
138: JsonArray dimList = new JsonArray(program);
139: JType leafType = arrayType.getLeafType();
140: int outstandingDims = arrayType.getDims();
141: for (int i = 0; i < x.dims.size(); ++i) {
142: JExpression dim = x.dims.get(i);
143: if (dim instanceof JAbsentArrayDimension) {
144: break;
145: }
146:
147: /*
148: * For each non-empty dimension, reduce the number of dims on the end
149: * type.
150: *
151: * new int[2][ ][ ]->int[][]
152: *
153: * new int[2][3][ ]->int[]
154: *
155: * new int[2][3][4]->int
156: *
157: */
158: JArrayType cur = program.getTypeArray(leafType,
159: outstandingDims--);
160:
161: JLiteral classLit = program.getLiteralClass(cur);
162: classLitList.exprs.add(classLit);
163:
164: JLiteral typeIdLit = program.getLiteralInt(program
165: .getTypeId(cur));
166: typeIdList.exprs.add(typeIdLit);
167:
168: JLiteral queryIdLit = program
169: .getLiteralInt(tryGetQueryId(cur));
170: queryIdList.exprs.add(queryIdLit);
171:
172: dimList.exprs.add(dim);
173: }
174: JType targetType = leafType;
175: if (outstandingDims > 0) {
176: targetType = program.getTypeArray(targetType,
177: outstandingDims);
178: }
179:
180: call.getArgs().add(classLitList);
181: call.getArgs().add(typeIdList);
182: call.getArgs().add(queryIdList);
183: call.getArgs().add(dimList);
184: call.getArgs().add(getSeedTypeLiteralFor(targetType));
185: ctx.replaceMe(call);
186: }
187:
188: private void processInitializers(JNewArray x, Context ctx,
189: JArrayType arrayType) {
190: // override the type of the called method with the array's type
191: JMethodCall call = new JMethodCall(program, x
192: .getSourceInfo(), null, initValues, arrayType);
193: JLiteral classLit = program.getLiteralClass(arrayType);
194: JLiteral typeIdLit = program.getLiteralInt(program
195: .getTypeId(arrayType));
196: JLiteral queryIdLit = program
197: .getLiteralInt(tryGetQueryId(arrayType));
198: JsonArray initList = new JsonArray(program);
199: for (int i = 0; i < x.initializers.size(); ++i) {
200: initList.exprs.add(x.initializers.get(i));
201: }
202: call.getArgs().add(classLit);
203: call.getArgs().add(typeIdLit);
204: call.getArgs().add(queryIdLit);
205: call.getArgs().add(initList);
206: ctx.replaceMe(call);
207: }
208:
209: private int tryGetQueryId(JArrayType type) {
210: JType elementType = type.getElementType();
211: int leafTypeId = -1;
212: if (elementType instanceof JReferenceType) {
213: leafTypeId = program
214: .getQueryId((JReferenceType) elementType);
215: }
216: return leafTypeId;
217: }
218: }
219:
220: public static void exec(JProgram program) {
221: new ArrayNormalizer(program).execImpl();
222: }
223:
224: private final JMethod initDim;
225: private final JMethod initDims;
226: private final JMethod initValues;
227: private final JProgram program;
228: private final JMethod setCheckMethod;
229:
230: private ArrayNormalizer(JProgram program) {
231: this .program = program;
232: setCheckMethod = program.getIndexedMethod("Array.setCheck");
233: initDim = program.getIndexedMethod("Array.initDim");
234: initDims = program.getIndexedMethod("Array.initDims");
235: initValues = program.getIndexedMethod("Array.initValues");
236: }
237:
238: private void execImpl() {
239: ArrayVisitor visitor = new ArrayVisitor();
240: visitor.accept(program);
241: }
242:
243: }
|