0001: /*
0002: * Copyright 2007 Google Inc.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005: * use this file except in compliance with the License. You may obtain a copy of
0006: * the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013: * License for the specific language governing permissions and limitations under
0014: * the License.
0015: */
0016: package com.google.gwt.dev.jjs.impl;
0017:
0018: import com.google.gwt.dev.jjs.HasSourceInfo;
0019: import com.google.gwt.dev.jjs.InternalCompilerException;
0020: import com.google.gwt.dev.jjs.SourceInfo;
0021: import com.google.gwt.dev.jjs.ast.HasEnclosingType;
0022: import com.google.gwt.dev.jjs.ast.JArrayRef;
0023: import com.google.gwt.dev.jjs.ast.JArrayType;
0024: import com.google.gwt.dev.jjs.ast.JAssertStatement;
0025: import com.google.gwt.dev.jjs.ast.JBinaryOperation;
0026: import com.google.gwt.dev.jjs.ast.JBinaryOperator;
0027: import com.google.gwt.dev.jjs.ast.JBlock;
0028: import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
0029: import com.google.gwt.dev.jjs.ast.JBreakStatement;
0030: import com.google.gwt.dev.jjs.ast.JCaseStatement;
0031: import com.google.gwt.dev.jjs.ast.JCastOperation;
0032: import com.google.gwt.dev.jjs.ast.JCharLiteral;
0033: import com.google.gwt.dev.jjs.ast.JClassLiteral;
0034: import com.google.gwt.dev.jjs.ast.JClassType;
0035: import com.google.gwt.dev.jjs.ast.JConditional;
0036: import com.google.gwt.dev.jjs.ast.JContinueStatement;
0037: import com.google.gwt.dev.jjs.ast.JDoStatement;
0038: import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
0039: import com.google.gwt.dev.jjs.ast.JEnumField;
0040: import com.google.gwt.dev.jjs.ast.JEnumType;
0041: import com.google.gwt.dev.jjs.ast.JExpression;
0042: import com.google.gwt.dev.jjs.ast.JExpressionStatement;
0043: import com.google.gwt.dev.jjs.ast.JField;
0044: import com.google.gwt.dev.jjs.ast.JFieldRef;
0045: import com.google.gwt.dev.jjs.ast.JFloatLiteral;
0046: import com.google.gwt.dev.jjs.ast.JForStatement;
0047: import com.google.gwt.dev.jjs.ast.JIfStatement;
0048: import com.google.gwt.dev.jjs.ast.JInstanceOf;
0049: import com.google.gwt.dev.jjs.ast.JIntLiteral;
0050: import com.google.gwt.dev.jjs.ast.JInterfaceType;
0051: import com.google.gwt.dev.jjs.ast.JLabel;
0052: import com.google.gwt.dev.jjs.ast.JLabeledStatement;
0053: import com.google.gwt.dev.jjs.ast.JLiteral;
0054: import com.google.gwt.dev.jjs.ast.JLocal;
0055: import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
0056: import com.google.gwt.dev.jjs.ast.JLocalRef;
0057: import com.google.gwt.dev.jjs.ast.JLongLiteral;
0058: import com.google.gwt.dev.jjs.ast.JMethod;
0059: import com.google.gwt.dev.jjs.ast.JMethodBody;
0060: import com.google.gwt.dev.jjs.ast.JMethodCall;
0061: import com.google.gwt.dev.jjs.ast.JNewArray;
0062: import com.google.gwt.dev.jjs.ast.JNewInstance;
0063: import com.google.gwt.dev.jjs.ast.JNode;
0064: import com.google.gwt.dev.jjs.ast.JParameter;
0065: import com.google.gwt.dev.jjs.ast.JParameterRef;
0066: import com.google.gwt.dev.jjs.ast.JPostfixOperation;
0067: import com.google.gwt.dev.jjs.ast.JPrefixOperation;
0068: import com.google.gwt.dev.jjs.ast.JPrimitiveType;
0069: import com.google.gwt.dev.jjs.ast.JProgram;
0070: import com.google.gwt.dev.jjs.ast.JReferenceType;
0071: import com.google.gwt.dev.jjs.ast.JReturnStatement;
0072: import com.google.gwt.dev.jjs.ast.JStatement;
0073: import com.google.gwt.dev.jjs.ast.JStringLiteral;
0074: import com.google.gwt.dev.jjs.ast.JSwitchStatement;
0075: import com.google.gwt.dev.jjs.ast.JThrowStatement;
0076: import com.google.gwt.dev.jjs.ast.JTryStatement;
0077: import com.google.gwt.dev.jjs.ast.JType;
0078: import com.google.gwt.dev.jjs.ast.JUnaryOperator;
0079: import com.google.gwt.dev.jjs.ast.JVariable;
0080: import com.google.gwt.dev.jjs.ast.JVariableRef;
0081: import com.google.gwt.dev.jjs.ast.JWhileStatement;
0082: import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
0083: import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
0084: import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
0085: import com.google.gwt.dev.jjs.ast.js.JsonObject;
0086: import com.google.gwt.dev.js.ast.JsContext;
0087: import com.google.gwt.dev.js.ast.JsExpression;
0088: import com.google.gwt.dev.js.ast.JsFunction;
0089: import com.google.gwt.dev.js.ast.JsModVisitor;
0090: import com.google.gwt.dev.js.ast.JsNameRef;
0091: import com.google.gwt.dev.js.ast.JsProgram;
0092: import com.google.gwt.dev.js.ast.JsSourceInfo;
0093:
0094: import org.eclipse.jdt.core.compiler.IProblem;
0095: import org.eclipse.jdt.internal.compiler.CompilationResult;
0096: import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
0097: import org.eclipse.jdt.internal.compiler.ast.ASTNode;
0098: import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
0099: import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
0100: import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
0101: import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
0102: import org.eclipse.jdt.internal.compiler.ast.ArrayReference;
0103: import org.eclipse.jdt.internal.compiler.ast.AssertStatement;
0104: import org.eclipse.jdt.internal.compiler.ast.Assignment;
0105: import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
0106: import org.eclipse.jdt.internal.compiler.ast.Block;
0107: import org.eclipse.jdt.internal.compiler.ast.BreakStatement;
0108: import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
0109: import org.eclipse.jdt.internal.compiler.ast.CastExpression;
0110: import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
0111: import org.eclipse.jdt.internal.compiler.ast.CompoundAssignment;
0112: import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
0113: import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
0114: import org.eclipse.jdt.internal.compiler.ast.ContinueStatement;
0115: import org.eclipse.jdt.internal.compiler.ast.DoStatement;
0116: import org.eclipse.jdt.internal.compiler.ast.EmptyStatement;
0117: import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
0118: import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
0119: import org.eclipse.jdt.internal.compiler.ast.Expression;
0120: import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
0121: import org.eclipse.jdt.internal.compiler.ast.FieldReference;
0122: import org.eclipse.jdt.internal.compiler.ast.ForStatement;
0123: import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
0124: import org.eclipse.jdt.internal.compiler.ast.IfStatement;
0125: import org.eclipse.jdt.internal.compiler.ast.Initializer;
0126: import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression;
0127: import org.eclipse.jdt.internal.compiler.ast.LabeledStatement;
0128: import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
0129: import org.eclipse.jdt.internal.compiler.ast.MessageSend;
0130: import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
0131: import org.eclipse.jdt.internal.compiler.ast.OR_OR_Expression;
0132: import org.eclipse.jdt.internal.compiler.ast.PostfixExpression;
0133: import org.eclipse.jdt.internal.compiler.ast.PrefixExpression;
0134: import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
0135: import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
0136: import org.eclipse.jdt.internal.compiler.ast.QualifiedSuperReference;
0137: import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference;
0138: import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
0139: import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
0140: import org.eclipse.jdt.internal.compiler.ast.Statement;
0141: import org.eclipse.jdt.internal.compiler.ast.SuperReference;
0142: import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
0143: import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement;
0144: import org.eclipse.jdt.internal.compiler.ast.ThisReference;
0145: import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
0146: import org.eclipse.jdt.internal.compiler.ast.TryStatement;
0147: import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
0148: import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
0149: import org.eclipse.jdt.internal.compiler.ast.WhileStatement;
0150: import org.eclipse.jdt.internal.compiler.impl.BooleanConstant;
0151: import org.eclipse.jdt.internal.compiler.impl.ByteConstant;
0152: import org.eclipse.jdt.internal.compiler.impl.CharConstant;
0153: import org.eclipse.jdt.internal.compiler.impl.Constant;
0154: import org.eclipse.jdt.internal.compiler.impl.DoubleConstant;
0155: import org.eclipse.jdt.internal.compiler.impl.FloatConstant;
0156: import org.eclipse.jdt.internal.compiler.impl.IntConstant;
0157: import org.eclipse.jdt.internal.compiler.impl.LongConstant;
0158: import org.eclipse.jdt.internal.compiler.impl.ShortConstant;
0159: import org.eclipse.jdt.internal.compiler.impl.StringConstant;
0160: import org.eclipse.jdt.internal.compiler.lookup.Binding;
0161: import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
0162: import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
0163: import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
0164: import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
0165: import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
0166: import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
0167: import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
0168: import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
0169: import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
0170: import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
0171: import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
0172: import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
0173: import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
0174: import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
0175: import org.eclipse.jdt.internal.compiler.problem.ProblemHandler;
0176: import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
0177:
0178: import java.lang.reflect.InvocationTargetException;
0179: import java.lang.reflect.Method;
0180: import java.util.ArrayList;
0181: import java.util.Collections;
0182: import java.util.HashMap;
0183: import java.util.IdentityHashMap;
0184: import java.util.Iterator;
0185: import java.util.LinkedList;
0186: import java.util.List;
0187: import java.util.Map;
0188:
0189: /**
0190: * This is the big kahuna where most of the nitty gritty of creating our AST
0191: * happens. BuildTypeMap must have already run so we have valid mappings between
0192: * JDT nodes and our already-created AST nodes.
0193: */
0194: public class GenerateJavaAST {
0195:
0196: /**
0197: * Visit the JDT AST and produce our own AST into the passed-in TypeMap's
0198: * JProgram. By the end of this pass, the produced AST should contain every
0199: * piece of information we'll ever need about the code. The JDT nodes should
0200: * never again be referenced after this.
0201: *
0202: * This is implemented as a reflective visitor for JDT's AST. The advantage of
0203: * doing it reflectively is that if we run into any JDT nodes we can't handle,
0204: * we'll automatically throw an exception. If we had subclassed
0205: * {@link org.eclipse.jdt.internal.compiler.ast.ASTNode} we'd have to override
0206: * every single method and explicitly throw an exception to get the same
0207: * behavior.
0208: *
0209: * NOTE ON JDT FORCED OPTIMIZATIONS - If JDT statically determines that a
0210: * section of code in unreachable, it won't fully resolve that section of
0211: * code. This invalid-state code causes us major problems. As a result, we
0212: * have to optimize out those dead blocks early and never try to translate
0213: * them to our AST.
0214: */
0215: private static class JavaASTGenerationVisitor {
0216:
0217: private class JsniRefResolver extends JsModVisitor {
0218: private final AbstractMethodDeclaration methodDecl;
0219: private final JsniMethodBody nativeMethodBody;
0220:
0221: private JsniRefResolver(
0222: AbstractMethodDeclaration methodDecl,
0223: JsniMethodBody nativeMethodBody) {
0224: this .methodDecl = methodDecl;
0225: this .nativeMethodBody = nativeMethodBody;
0226: }
0227:
0228: @Override
0229: public void endVisit(JsNameRef x,
0230: JsContext<JsExpression> ctx) {
0231: String ident = x.getIdent();
0232: if (ident.charAt(0) == '@') {
0233: processNameRef(x, ctx);
0234: }
0235: }
0236:
0237: private HasEnclosingType parseJsniRef(SourceInfo info,
0238: String ident) {
0239: String[] parts = ident.substring(1).split("::");
0240: assert (parts.length == 2);
0241: String className = parts[0];
0242: JReferenceType type = program.getFromTypeMap(className);
0243: if (type == null) {
0244: reportJsniError(info, methodDecl,
0245: "Unresolvable native reference to type '"
0246: + className + "'");
0247: return null;
0248: }
0249: String rhs = parts[1];
0250: int parenPos = rhs.indexOf('(');
0251: if (parenPos < 0) {
0252: // look for a field
0253: for (int i = 0; i < type.fields.size(); ++i) {
0254: JField field = type.fields.get(i);
0255: if (field.getName().equals(rhs)) {
0256: return field;
0257: }
0258: }
0259:
0260: reportJsniError(info, methodDecl,
0261: "Unresolvable native reference to field '"
0262: + rhs + "' in type '" + className
0263: + "'");
0264: } else {
0265: // look for a method
0266: String methodName = rhs.substring(0, parenPos);
0267: String almostMatches = null;
0268: for (int i = 0; i < type.methods.size(); ++i) {
0269: JMethod method = type.methods.get(i);
0270: if (method.getName().equals(methodName)) {
0271: String jsniSig = getJsniSig(method);
0272: if (jsniSig.equals(rhs)) {
0273: return method;
0274: } else if (almostMatches == null) {
0275: almostMatches = "'" + jsniSig + "'";
0276: } else {
0277: almostMatches += ", '" + jsniSig + "'";
0278: }
0279: }
0280: }
0281:
0282: if (almostMatches == null) {
0283: reportJsniError(info, methodDecl,
0284: "Unresolvable native reference to method '"
0285: + methodName + "' in type '"
0286: + className + "'");
0287: } else {
0288: reportJsniError(info, methodDecl,
0289: "Unresolvable native reference to method '"
0290: + methodName + "' in type '"
0291: + className
0292: + "' (did you mean "
0293: + almostMatches + "?)");
0294: }
0295: }
0296: return null;
0297: }
0298:
0299: private void processField(JsNameRef nameRef,
0300: SourceInfo info, JField field,
0301: JsContext<JsExpression> ctx) {
0302: if (field.isStatic() && nameRef.getQualifier() != null) {
0303: reportJsniError(info, methodDecl,
0304: "Cannot make a qualified reference to the static field "
0305: + field.getName());
0306: } else if (!field.isStatic()
0307: && nameRef.getQualifier() == null) {
0308: reportJsniError(info, methodDecl,
0309: "Cannot make an unqualified reference to the instance field "
0310: + field.getName());
0311: }
0312:
0313: /*
0314: * We must replace any compile-time constants with the constant value of
0315: * the field.
0316: */
0317: JLiteral initializer = field.constInitializer;
0318: if (field.isStatic() && field.isFinal()
0319: && initializer != null) {
0320: JType type = initializer.getType();
0321: if (type instanceof JPrimitiveType
0322: || type == program.getTypeJavaLangString()) {
0323: GenerateJavaScriptLiterals generator = new GenerateJavaScriptLiterals(
0324: jsProgram);
0325: generator.accept(initializer);
0326: JsExpression result = generator.peek();
0327: assert (result != null);
0328: ctx.replaceMe(result);
0329: return;
0330: }
0331: }
0332:
0333: // Normal: create a jsniRef.
0334: JsniFieldRef fieldRef = new JsniFieldRef(program, info,
0335: field, currentClass);
0336: nativeMethodBody.jsniFieldRefs.add(fieldRef);
0337: }
0338:
0339: private void processMethod(JsNameRef nameRef,
0340: SourceInfo info, JMethod method) {
0341: if (method.isStatic() && nameRef.getQualifier() != null) {
0342: reportJsniError(info, methodDecl,
0343: "Cannot make a qualified reference to the static method "
0344: + method.getName());
0345: } else if (!method.isStatic()
0346: && nameRef.getQualifier() == null) {
0347: reportJsniError(info, methodDecl,
0348: "Cannot make an unqualified reference to the instance method "
0349: + method.getName());
0350: }
0351:
0352: JsniMethodRef methodRef = new JsniMethodRef(program,
0353: info, method);
0354: nativeMethodBody.jsniMethodRefs.add(methodRef);
0355: }
0356:
0357: private void processNameRef(JsNameRef nameRef,
0358: JsContext<JsExpression> ctx) {
0359: SourceInfo info = nativeMethodBody.getSourceInfo();
0360: // TODO: make this tighter when we have real source info
0361: // JSourceInfo info = translateInfo(nameRef.getInfo());
0362: String ident = nameRef.getIdent();
0363: HasEnclosingType node = program.jsniMap.get(ident);
0364: if (node == null) {
0365: node = parseJsniRef(info, ident);
0366: if (node == null) {
0367: return; // already reported error
0368: }
0369: program.jsniMap.put(ident, node);
0370: }
0371:
0372: if (node instanceof JField) {
0373: processField(nameRef, info, (JField) node, ctx);
0374: } else if (node instanceof JMethod) {
0375: processMethod(nameRef, info, (JMethod) node);
0376: } else {
0377: throw new InternalCompilerException(
0378: (HasSourceInfo) node,
0379: "JSNI reference to something other than a field or method?",
0380: null);
0381: }
0382: }
0383: }
0384:
0385: private static String getJsniSig(JMethod method) {
0386: StringBuffer sb = new StringBuffer();
0387: sb.append(method.getName());
0388: sb.append("(");
0389: for (int i = 0; i < method.getOriginalParamTypes().size(); ++i) {
0390: JType type = method.getOriginalParamTypes().get(i);
0391: sb.append(type.getJsniSignatureName());
0392: }
0393: sb.append(")");
0394: return sb.toString();
0395: }
0396:
0397: private static InternalCompilerException translateException(
0398: JNode node, Throwable e) {
0399: InternalCompilerException ice;
0400: if (e instanceof InternalCompilerException) {
0401: ice = (InternalCompilerException) e;
0402: ice.addNode(node);
0403: } else {
0404: ice = new InternalCompilerException(node,
0405: "Error constructing Java AST", e);
0406: }
0407: return ice;
0408: }
0409:
0410: private JReferenceType currentClass;
0411:
0412: private ClassScope currentClassScope;
0413:
0414: private String currentFileName;
0415:
0416: private JMethod currentMethod;
0417:
0418: private JMethodBody currentMethodBody;
0419:
0420: private MethodScope currentMethodScope;
0421:
0422: private int[] currentSeparatorPositions;
0423:
0424: private final JsProgram jsProgram;
0425:
0426: private final Map<JMethod, Map<String, JLabel>> labelMap = new IdentityHashMap<JMethod, Map<String, JLabel>>();
0427:
0428: private final JProgram program;
0429:
0430: private final TypeMap typeMap;
0431:
0432: public JavaASTGenerationVisitor(TypeMap typeMap,
0433: JProgram program, JsProgram jsProgram) {
0434: this .typeMap = typeMap;
0435: this .program = program;
0436: this .jsProgram = jsProgram;
0437: }
0438:
0439: public void processEnumType(JEnumType type) {
0440: // Create a JSNI map for string-based lookup.
0441: JField mapField = createEnumValueMap(type);
0442:
0443: // Generate the synthetic values() and valueOf() methods.
0444: for (JMethod method : type.methods) {
0445: currentMethod = method;
0446: if ("values".equals(method.getName())) {
0447: if (method.params.size() != 0) {
0448: continue;
0449: }
0450: currentMethodBody = (JMethodBody) method.getBody();
0451: writeEnumValuesMethod(type);
0452: } else if ("valueOf".equals(method.getName())) {
0453: if (method.params.size() != 1) {
0454: continue;
0455: }
0456: if (method.params.get(0).getType() != program
0457: .getTypeJavaLangString()) {
0458: continue;
0459: }
0460: currentMethodBody = (JMethodBody) method.getBody();
0461: writeEnumValueOfMethod(type, mapField);
0462: }
0463: currentMethodBody = null;
0464: currentMethod = null;
0465: }
0466: }
0467:
0468: /**
0469: * We emulate static initializers and instance initializers as methods. As
0470: * in other cases, this gives us: simpler AST, easier to optimize, more like
0471: * output JavaScript.
0472: */
0473: public void processType(TypeDeclaration x) {
0474: if (x.binding.isAnnotationType()) {
0475: // Do not process.
0476: return;
0477: }
0478: currentClass = (JReferenceType) typeMap.get(x.binding);
0479: try {
0480: currentClassScope = x.scope;
0481: currentSeparatorPositions = x.compilationResult.lineSeparatorPositions;
0482: currentFileName = String
0483: .valueOf(x.compilationResult.fileName);
0484:
0485: /*
0486: * Make clinits chain to super class (JDT doesn't write code to do
0487: * this). Call super class $clinit; $clinit is always in position 0.
0488: */
0489: if (currentClass.extnds != null) {
0490: JMethod myClinit = currentClass.methods.get(0);
0491: JMethod super Clinit = currentClass.extnds.methods
0492: .get(0);
0493: JMethodCall super ClinitCall = new JMethodCall(
0494: program, myClinit.getSourceInfo(), null,
0495: super Clinit);
0496: JMethodBody body = (JMethodBody) myClinit.getBody();
0497: body.getStatements().add(0,
0498: super ClinitCall.makeStatement());
0499: }
0500:
0501: if (x.fields != null) {
0502: // Process fields
0503: for (int i = 0, n = x.fields.length; i < n; ++i) {
0504: FieldDeclaration fieldDeclaration = x.fields[i];
0505: if (fieldDeclaration.isStatic()) {
0506: // clinit
0507: currentMethod = currentClass.methods.get(0);
0508: currentMethodBody = (JMethodBody) currentMethod
0509: .getBody();
0510: currentMethodScope = x.staticInitializerScope;
0511: } else {
0512: // init
0513: currentMethod = currentClass.methods.get(1);
0514: currentMethodBody = (JMethodBody) currentMethod
0515: .getBody();
0516: currentMethodScope = x.initializerScope;
0517: }
0518:
0519: if (fieldDeclaration instanceof Initializer) {
0520: assert (currentClass instanceof JClassType);
0521: processInitializer((Initializer) fieldDeclaration);
0522: } else {
0523: processField(fieldDeclaration);
0524: }
0525: }
0526: }
0527:
0528: currentMethodScope = null;
0529: currentMethod = null;
0530:
0531: if (x.methods != null) {
0532: // Process methods
0533: for (int i = 0, n = x.methods.length; i < n; ++i) {
0534: if (x.methods[i].isConstructor()) {
0535: assert (currentClass instanceof JClassType);
0536: processConstructor((ConstructorDeclaration) x.methods[i]);
0537: } else if (x.methods[i].isClinit()) {
0538: // nothing to do
0539: } else {
0540: processMethod(x.methods[i]);
0541: }
0542: }
0543: }
0544:
0545: // Write the body of the getClass() override.
0546: if (currentClass instanceof JClassType
0547: && currentClass != program
0548: .getTypeJavaLangObject()
0549: && currentClass != program
0550: .getIndexedType("Array")) {
0551: JMethod method = currentClass.methods.get(2);
0552: assert ("getClass".equals(method.getName()));
0553:
0554: tryFindUpRefs(method);
0555:
0556: JMethodBody body = (JMethodBody) method.getBody();
0557: JClassLiteral classLit = program
0558: .getLiteralClass(currentClass);
0559: body.getStatements().add(
0560: new JReturnStatement(program, null,
0561: classLit));
0562: }
0563:
0564: if (currentClass instanceof JEnumType) {
0565: processEnumType((JEnumType) currentClass);
0566: }
0567:
0568: currentClassScope = null;
0569: currentClass = null;
0570: currentSeparatorPositions = null;
0571: currentFileName = null;
0572: } catch (Throwable e) {
0573: throw translateException(currentClass, e);
0574: }
0575: }
0576:
0577: /**
0578: * This is the guts of the "reflective" part of this visitor. Try to find a
0579: * "process" method that exactly matches the run-time type of the argument.
0580: */
0581: protected JNode dispatch(String name, Object child) {
0582: if (child == null) {
0583: return null;
0584: }
0585:
0586: try {
0587: Method method = getClass().getDeclaredMethod(name,
0588: child.getClass());
0589: return (JNode) method.invoke(this , child);
0590: } catch (Throwable e) {
0591: if (e instanceof InvocationTargetException) {
0592: e = ((InvocationTargetException) e)
0593: .getTargetException();
0594: }
0595: throw translateException(child, e);
0596: }
0597: }
0598:
0599: /**
0600: * Process an Expression type node reflectively; must return a JExpression.
0601: */
0602: protected JExpression dispProcessExpression(Expression x) {
0603: /*
0604: * Note that we always prefer a JDT-computed constant value to the actual
0605: * written expression. (Let's hope JDT is always right.) This means we
0606: * don't have to write processExpression methods for the numerous JDT
0607: * literal nodes because they ALWAYS have a constant value.
0608: */
0609: JExpression result = null;
0610: if (x != null && x.constant != null
0611: && x.constant != Constant.NotAConstant) {
0612: result = (JExpression) dispatch("processConstant",
0613: x.constant);
0614: }
0615:
0616: if (result == null) {
0617: // The expression was not a constant, so use the general logic.
0618: result = (JExpression) dispatch("processExpression", x);
0619: }
0620:
0621: // Check if we need to box the resulting expression.
0622: if (x != null) {
0623: if ((x.implicitConversion & TypeIds.BOXING) != 0) {
0624: /*
0625: * Beware! Passing in "primitiveTypeToBox" seems unnecessary, but it
0626: * isn't. You cannot determine the true type to box directly by
0627: * calling result.getType() in all cases because sometimes, such as
0628: * when the expression is originally a int literal prefixed with a
0629: * narrowing cast, the processExpression() calls returns JIntLiterals
0630: * even when the actual type should be byte or short. So, unless you
0631: * remember what the original expression type was, you'd incorrectly
0632: * box a byte as an Integer.
0633: */
0634: JType primitiveTypeToBox = (JType) typeMap
0635: .get(x.resolvedType);
0636:
0637: if (!(primitiveTypeToBox instanceof JPrimitiveType)) {
0638: throw new InternalCompilerException(result,
0639: "Attempt to box a non-primitive type: "
0640: + primitiveTypeToBox.getName(),
0641: null);
0642: }
0643:
0644: result = box(result,
0645: (JPrimitiveType) primitiveTypeToBox);
0646: } else if ((x.implicitConversion & TypeIds.UNBOXING) != 0) {
0647:
0648: JType typeToUnbox = (JType) typeMap
0649: .get(x.resolvedType);
0650: if (!(typeToUnbox instanceof JClassType)) {
0651: throw new InternalCompilerException(result,
0652: "Attempt to unbox a non-class type: "
0653: + typeToUnbox.getName(), null);
0654: }
0655:
0656: result = unbox(result, (JClassType) typeToUnbox);
0657: }
0658: }
0659: return result;
0660: }
0661:
0662: /**
0663: * Process an Statement type node reflectively; must return a JStatement.
0664: */
0665: protected JStatement dispProcessStatement(Statement x) {
0666: JStatement stmt;
0667: if (x instanceof Expression) {
0668: JExpression expr = dispProcessExpression((Expression) x);
0669: if (expr == null) {
0670: return null;
0671: }
0672: stmt = expr.makeStatement();
0673: } else {
0674: stmt = (JStatement) dispatch("processStatement", x);
0675: }
0676: return stmt;
0677: }
0678:
0679: JBooleanLiteral processConstant(BooleanConstant x) {
0680: return program.getLiteralBoolean(x.booleanValue());
0681: }
0682:
0683: JIntLiteral processConstant(ByteConstant x) {
0684: return program.getLiteralInt(x.byteValue());
0685: }
0686:
0687: JCharLiteral processConstant(CharConstant x) {
0688: return program.getLiteralChar(x.charValue());
0689: }
0690:
0691: JDoubleLiteral processConstant(DoubleConstant x) {
0692: return program.getLiteralDouble(x.doubleValue());
0693: }
0694:
0695: JFloatLiteral processConstant(FloatConstant x) {
0696: return program.getLiteralFloat(x.floatValue());
0697: }
0698:
0699: JIntLiteral processConstant(IntConstant x) {
0700: return program.getLiteralInt(x.intValue());
0701: }
0702:
0703: JLongLiteral processConstant(LongConstant x) {
0704: return program.getLiteralLong(x.longValue());
0705: }
0706:
0707: JIntLiteral processConstant(ShortConstant x) {
0708: return program.getLiteralInt(x.shortValue());
0709: }
0710:
0711: JStringLiteral processConstant(StringConstant x) {
0712: return program.getLiteralString(x.stringValue()
0713: .toCharArray());
0714: }
0715:
0716: /**
0717: * Weird: we used to have JConstructor (and JConstructorCall) in our AST,
0718: * but we got rid of them completely and instead model them as instance
0719: * methods whose qualifier is a naked no-argument new operation.
0720: *
0721: * There are several reasons we do it this way:
0722: *
0723: * 1) When spitting our AST back to Java code (for verification purposes),
0724: * we found it was impossible to correctly emulate nested classes as
0725: * non-nested classes using traditional constructor syntax. It boiled down
0726: * to the fact that you really HAVE to assign your synthetic arguments to
0727: * your synthetic fields BEFORE calling your superclass constructor (because
0728: * it might call you back polymorphically). And trying to do that in
0729: * straight Java is a semantic error, a super call must be the first
0730: * statement of your constructor.
0731: *
0732: * 2) It's a lot more like how we'll be generating JavaScript eventually.
0733: *
0734: * 3) It's a lot easier to optimize; the same optimizations work on our
0735: * synthetic fields as work on any user fields. In fact, once we're past AST
0736: * generation, we throw away all information about what's synthetic.
0737: *
0738: * The order of emulation is: - assign all synthetic fields from synthetic
0739: * args - call our super constructor emulation method - call our instance
0740: * initializer emulation method - run user code - return this
0741: */
0742: void processConstructor(ConstructorDeclaration x) {
0743: JMethod ctor = (JMethod) typeMap.get(x.binding);
0744: try {
0745: SourceInfo info = ctor.getSourceInfo();
0746:
0747: currentMethod = ctor;
0748: currentMethodBody = (JMethodBody) ctor.getBody();
0749: currentMethodScope = x.scope;
0750:
0751: JMethodCall super OrThisCall = null;
0752: ExplicitConstructorCall ctorCall = x.constructorCall;
0753: if (ctorCall != null) {
0754: super OrThisCall = (JMethodCall) dispatch(
0755: "processExpression", ctorCall);
0756: }
0757:
0758: /*
0759: * Determine if we have an explicit this call. The presence of an
0760: * explicit this call indicates we can skip certain initialization steps
0761: * (as the callee will perform those steps for us). These skippable
0762: * steps are 1) assigning synthetic args to fields and 2) running
0763: * initializers.
0764: */
0765: boolean hasExplicitThis = (ctorCall != null)
0766: && !ctorCall.isSuperAccess();
0767:
0768: JClassType enclosingType = (JClassType) ctor
0769: .getEnclosingType();
0770:
0771: // Call clinit; $clinit is always in position 0.
0772: JMethod clinitMethod = enclosingType.methods.get(0);
0773: JMethodCall clinitCall = new JMethodCall(program, info,
0774: null, clinitMethod);
0775: JMethodBody body = (JMethodBody) ctor.getBody();
0776: List<JStatement> statements = body.getStatements();
0777: statements.add(clinitCall.makeStatement());
0778:
0779: /*
0780: * All synthetic fields must be assigned, unless we have an explicit
0781: * this constructor call, in which case the callee will assign them for
0782: * us.
0783: */
0784: if (!hasExplicitThis) {
0785: ReferenceBinding declaringClass = x.binding.declaringClass;
0786: if (declaringClass instanceof NestedTypeBinding) {
0787: Iterator<JParameter> paramIt = getSyntheticsIterator(ctor);
0788: NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
0789: if (nestedBinding.enclosingInstances != null) {
0790: for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
0791: SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
0792: JParameter param = paramIt.next();
0793: if (arg.matchingField != null) {
0794: JField field = (JField) typeMap
0795: .get(arg);
0796: statements
0797: .add(program
0798: .createAssignmentStmt(
0799: info,
0800: createVariableRef(
0801: info,
0802: field),
0803: createVariableRef(
0804: info,
0805: param)));
0806: }
0807: }
0808: }
0809:
0810: if (nestedBinding.outerLocalVariables != null) {
0811: for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
0812: SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
0813: JParameter param = paramIt.next();
0814: JField field = (JField) typeMap
0815: .get(arg);
0816: statements.add(program
0817: .createAssignmentStmt(info,
0818: createVariableRef(info,
0819: field),
0820: createVariableRef(info,
0821: param)));
0822: }
0823: }
0824: }
0825: }
0826:
0827: // Enums: wire up synthetic name/ordinal params to the super method.
0828: if (enclosingType.isEnumOrSubclass() != null) {
0829: assert (super OrThisCall != null);
0830: JVariableRef enumNameRef = createVariableRef(
0831: super OrThisCall.getSourceInfo(),
0832: ctor.params.get(0));
0833: super OrThisCall.getArgs().add(0, enumNameRef);
0834: JVariableRef enumOrdinalRef = createVariableRef(
0835: super OrThisCall.getSourceInfo(),
0836: ctor.params.get(1));
0837: super OrThisCall.getArgs().add(1, enumOrdinalRef);
0838: }
0839:
0840: // optional this or super constructor call
0841: if (super OrThisCall != null) {
0842: statements.add(super OrThisCall.makeStatement());
0843: }
0844:
0845: JExpression this Ref = createThisRef(info, enclosingType);
0846:
0847: /*
0848: * Call the synthetic instance initializer method, unless we have an
0849: * explicit this constructor call, in which case the callee will.
0850: */
0851: if (!hasExplicitThis) {
0852: // $init is always in position 1 (clinit is in 0)
0853: JMethod initMethod = enclosingType.methods.get(1);
0854: JMethodCall initCall = new JMethodCall(program,
0855: info, this Ref, initMethod);
0856: statements.add(initCall.makeStatement());
0857: }
0858:
0859: // user code (finally!)
0860: if (x.statements != null) {
0861: for (int i = 0, n = x.statements.length; i < n; ++i) {
0862: Statement origStmt = x.statements[i];
0863: JStatement jstmt = dispProcessStatement(origStmt);
0864: if (jstmt != null) {
0865: statements.add(jstmt);
0866: }
0867: }
0868: }
0869:
0870: currentMethodScope = null;
0871: currentMethod = null;
0872:
0873: // synthesize a return statement to emulate returning the new object
0874: statements.add(new JReturnStatement(program, null,
0875: this Ref));
0876: } catch (Throwable e) {
0877: throw translateException(ctor, e);
0878: }
0879: }
0880:
0881: JExpression processExpression(AllocationExpression x) {
0882: SourceInfo info = makeSourceInfo(x);
0883: SourceTypeBinding typeBinding = erasure(x.resolvedType);
0884: if (typeBinding.constantPoolName() == null) {
0885: /*
0886: * Weird case: if JDT determines that this local class is totally
0887: * uninstantiable, it won't bother allocating a local name.
0888: */
0889: return program.getLiteralNull();
0890: }
0891: JClassType newType = (JClassType) typeMap.get(typeBinding);
0892: MethodBinding b = x.binding;
0893: JMethod ctor = (JMethod) typeMap.get(b);
0894: JMethodCall call;
0895: JClassType javaLangString = program.getTypeJavaLangString();
0896: if (newType == javaLangString) {
0897: /*
0898: * MAGIC: java.lang.String is implemented as a JavaScript String
0899: * primitive with a modified prototype. This requires funky handling of
0900: * constructor calls. We find a method named _String() whose signature
0901: * matches the requested constructor
0902: */
0903: int ctorArgc = ctor.params.size();
0904: JMethod targetMethod = null;
0905: outer: for (int j = 0; j < javaLangString.methods
0906: .size(); ++j) {
0907: JMethod method = javaLangString.methods.get(j);
0908: if (method.getName().equals("_String")
0909: && method.params.size() == ctorArgc) {
0910: for (int i = 0; i < ctorArgc; ++i) {
0911: JParameter mparam = method.params.get(i);
0912: JParameter cparam = ctor.params.get(i);
0913: if (mparam.getType() != cparam.getType()) {
0914: continue outer;
0915: }
0916: }
0917: targetMethod = method;
0918: break;
0919: }
0920: }
0921: if (targetMethod == null) {
0922: throw new InternalCompilerException(
0923: "String constructor error; no matching implementation.");
0924: }
0925: call = new JMethodCall(program, makeSourceInfo(x),
0926: null, targetMethod);
0927: } else {
0928: JNewInstance newInstance = new JNewInstance(program,
0929: info, newType);
0930: call = new JMethodCall(program, info, newInstance, ctor);
0931: }
0932:
0933: // Enums: hidden arguments for the name and id.
0934: if (x.enumConstant != null) {
0935: call.getArgs().add(
0936: program.getLiteralString(x.enumConstant.name));
0937: call.getArgs().add(
0938: program.getLiteralInt(x.enumConstant.binding
0939: .original().id));
0940: }
0941:
0942: // Plain old regular user arguments
0943: addCallArgs(x.arguments, call, b);
0944:
0945: // Synthetic args for inner classes
0946: ReferenceBinding targetBinding = b.declaringClass;
0947: if (targetBinding.isNestedType()
0948: && !targetBinding.isStatic()) {
0949: NestedTypeBinding nestedBinding = (NestedTypeBinding) erasure(targetBinding);
0950: // Synthetic this args for inner classes
0951: if (nestedBinding.enclosingInstances != null) {
0952: for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
0953: SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
0954: JClassType syntheticThisType = (JClassType) typeMap
0955: .get(arg.type);
0956: call.getArgs().add(
0957: createThisRef(info, syntheticThisType));
0958: }
0959: }
0960: // Synthetic locals for local classes
0961: if (nestedBinding.outerLocalVariables != null) {
0962: for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
0963: SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
0964: JVariable variable = (JVariable) typeMap
0965: .get(arg.actualOuterLocalVariable);
0966: call.getArgs().add(
0967: createVariableRef(info, variable,
0968: arg.actualOuterLocalVariable));
0969: }
0970: }
0971: }
0972:
0973: return call;
0974: }
0975:
0976: JExpression processExpression(AND_AND_Expression x) {
0977: JType type = (JType) typeMap.get(x.resolvedType);
0978: SourceInfo info = makeSourceInfo(x);
0979: return processBinaryOperation(info, JBinaryOperator.AND,
0980: type, x.left, x.right);
0981: }
0982:
0983: JExpression processExpression(ArrayAllocationExpression x) {
0984: SourceInfo info = makeSourceInfo(x);
0985: JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
0986: JNewArray newArray = new JNewArray(program, info, type);
0987:
0988: if (x.initializer != null) {
0989: newArray.initializers = new ArrayList<JExpression>();
0990: if (x.initializer.expressions != null) {
0991: for (Expression expression : x.initializer.expressions) {
0992: newArray.initializers
0993: .add(dispProcessExpression(expression));
0994: }
0995: }
0996: } else {
0997: newArray.dims = new ArrayList<JExpression>();
0998: for (Expression dimension : x.dimensions) {
0999: // can be null if index expression was empty
1000: if (dimension == null) {
1001: newArray.dims.add(program
1002: .getLiteralAbsentArrayDimension());
1003: } else {
1004: newArray.dims
1005: .add(dispProcessExpression(dimension));
1006: }
1007: }
1008: }
1009:
1010: return newArray;
1011: }
1012:
1013: JExpression processExpression(ArrayInitializer x) {
1014: SourceInfo info = makeSourceInfo(x);
1015: JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
1016: JNewArray newArray = new JNewArray(program, info, type);
1017:
1018: newArray.initializers = new ArrayList<JExpression>();
1019: if (x.expressions != null) {
1020: for (Expression expression : x.expressions) {
1021: newArray.initializers
1022: .add(dispProcessExpression(expression));
1023: }
1024: }
1025:
1026: return newArray;
1027: }
1028:
1029: JExpression processExpression(ArrayReference x) {
1030: SourceInfo info = makeSourceInfo(x);
1031: JArrayRef arrayRef = new JArrayRef(program, info,
1032: dispProcessExpression(x.receiver),
1033: dispProcessExpression(x.position));
1034: return arrayRef;
1035: }
1036:
1037: JExpression processExpression(Assignment x) {
1038: JType type = (JType) typeMap.get(x.resolvedType);
1039: SourceInfo info = makeSourceInfo(x);
1040: return processBinaryOperation(info, JBinaryOperator.ASG,
1041: type, x.lhs, x.expression);
1042: }
1043:
1044: JExpression processExpression(BinaryExpression x) {
1045: JBinaryOperator op;
1046:
1047: int binOp = (x.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
1048: switch (binOp) {
1049: case BinaryExpression.LEFT_SHIFT:
1050: op = JBinaryOperator.SHL;
1051: break;
1052: case BinaryExpression.RIGHT_SHIFT:
1053: op = JBinaryOperator.SHR;
1054: break;
1055: case BinaryExpression.UNSIGNED_RIGHT_SHIFT:
1056: op = JBinaryOperator.SHRU;
1057: break;
1058: case BinaryExpression.PLUS:
1059: op = JBinaryOperator.ADD;
1060: break;
1061: case BinaryExpression.MINUS:
1062: op = JBinaryOperator.SUB;
1063: break;
1064: case BinaryExpression.REMAINDER:
1065: op = JBinaryOperator.MOD;
1066: break;
1067: case BinaryExpression.XOR:
1068: op = JBinaryOperator.BIT_XOR;
1069: break;
1070: case BinaryExpression.AND:
1071: op = JBinaryOperator.BIT_AND;
1072: break;
1073: case BinaryExpression.MULTIPLY:
1074: op = JBinaryOperator.MUL;
1075: break;
1076: case BinaryExpression.OR:
1077: op = JBinaryOperator.BIT_OR;
1078: break;
1079: case BinaryExpression.DIVIDE:
1080: op = JBinaryOperator.DIV;
1081: break;
1082: case BinaryExpression.LESS_EQUAL:
1083: op = JBinaryOperator.LTE;
1084: break;
1085: case BinaryExpression.GREATER_EQUAL:
1086: op = JBinaryOperator.GTE;
1087: break;
1088: case BinaryExpression.GREATER:
1089: op = JBinaryOperator.GT;
1090: break;
1091: case BinaryExpression.LESS:
1092: op = JBinaryOperator.LT;
1093: break;
1094: default:
1095: throw new InternalCompilerException(
1096: "Unexpected operator for BinaryExpression");
1097: }
1098:
1099: JType type = (JType) typeMap.get(x.resolvedType);
1100: SourceInfo info = makeSourceInfo(x);
1101: return processBinaryOperation(info, op, type, x.left,
1102: x.right);
1103: }
1104:
1105: JExpression processExpression(CastExpression x) {
1106: SourceInfo info = makeSourceInfo(x);
1107: JType type = (JType) typeMap.get(x.resolvedType);
1108: JCastOperation cast = new JCastOperation(program, info,
1109: type, dispProcessExpression(x.expression));
1110: return cast;
1111: }
1112:
1113: JExpression processExpression(ClassLiteralAccess x) {
1114: JType type = (JType) typeMap.get(x.targetType);
1115: return program.getLiteralClass(type);
1116: }
1117:
1118: JExpression processExpression(CompoundAssignment x) {
1119: JBinaryOperator op;
1120:
1121: switch (x.operator) {
1122: case CompoundAssignment.PLUS:
1123: op = JBinaryOperator.ASG_ADD;
1124: break;
1125: case CompoundAssignment.MINUS:
1126: op = JBinaryOperator.ASG_SUB;
1127: break;
1128: case CompoundAssignment.MULTIPLY:
1129: op = JBinaryOperator.ASG_MUL;
1130: break;
1131: case CompoundAssignment.DIVIDE:
1132: op = JBinaryOperator.ASG_DIV;
1133: break;
1134: case CompoundAssignment.AND:
1135: op = JBinaryOperator.ASG_BIT_AND;
1136: break;
1137: case CompoundAssignment.OR:
1138: op = JBinaryOperator.ASG_BIT_OR;
1139: break;
1140: case CompoundAssignment.XOR:
1141: op = JBinaryOperator.ASG_BIT_XOR;
1142: break;
1143: case CompoundAssignment.REMAINDER:
1144: op = JBinaryOperator.ASG_MOD;
1145: break;
1146: case CompoundAssignment.LEFT_SHIFT:
1147: op = JBinaryOperator.ASG_SHL;
1148: break;
1149: case CompoundAssignment.RIGHT_SHIFT:
1150: op = JBinaryOperator.ASG_SHR;
1151: break;
1152: case CompoundAssignment.UNSIGNED_RIGHT_SHIFT:
1153: op = JBinaryOperator.ASG_SHRU;
1154: break;
1155: default:
1156: throw new InternalCompilerException(
1157: "Unexpected operator for CompoundAssignment");
1158: }
1159:
1160: JType type = (JType) typeMap.get(x.resolvedType);
1161: SourceInfo info = makeSourceInfo(x);
1162: return processBinaryOperation(info, op, type, x.lhs,
1163: x.expression);
1164: }
1165:
1166: JExpression processExpression(ConditionalExpression x) {
1167: SourceInfo info = makeSourceInfo(x);
1168: JType type = (JType) typeMap.get(x.resolvedType);
1169: JExpression ifTest = dispProcessExpression(x.condition);
1170: JExpression thenExpr = dispProcessExpression(x.valueIfTrue);
1171: JExpression elseExpr = dispProcessExpression(x.valueIfFalse);
1172: JConditional conditional = new JConditional(program, info,
1173: type, ifTest, thenExpr, elseExpr);
1174: return conditional;
1175: }
1176:
1177: JExpression processExpression(EqualExpression x) {
1178: JBinaryOperator op;
1179: switch ((x.bits & BinaryExpression.OperatorMASK) >> BinaryExpression.OperatorSHIFT) {
1180: case BinaryExpression.EQUAL_EQUAL:
1181: op = JBinaryOperator.EQ;
1182: break;
1183: case BinaryExpression.NOT_EQUAL:
1184: op = JBinaryOperator.NEQ;
1185: break;
1186: default:
1187: throw new InternalCompilerException(
1188: "Unexpected operator for EqualExpression");
1189: }
1190:
1191: JType type = (JType) typeMap.get(x.resolvedType);
1192: SourceInfo info = makeSourceInfo(x);
1193: return processBinaryOperation(info, op, type, x.left,
1194: x.right);
1195: }
1196:
1197: /**
1198: * How we have to treat super calls vs. this calls is so different, they may
1199: * as well have been two different JDT nodes.
1200: */
1201: JMethodCall processExpression(ExplicitConstructorCall x) {
1202: if (x.isSuperAccess()) {
1203: return processSuperConstructorCall(x);
1204: } else {
1205: return processThisConstructorCall(x);
1206: }
1207: }
1208:
1209: JExpression processExpression(FieldReference x) {
1210: SourceInfo info = makeSourceInfo(x);
1211: FieldBinding fieldBinding = x.binding;
1212: JField field;
1213: if (fieldBinding.declaringClass == null) {
1214: // probably array.length
1215: field = program.getIndexedField("Array.length");
1216: if (!field.getName().equals(
1217: String.valueOf(fieldBinding.name))) {
1218: throw new InternalCompilerException(
1219: "Error matching fieldBinding.");
1220: }
1221: } else {
1222: field = (JField) typeMap.get(fieldBinding);
1223: }
1224: JExpression instance = dispProcessExpression(x.receiver);
1225: JExpression fieldRef = new JFieldRef(program, info,
1226: instance, field, currentClass);
1227: return fieldRef;
1228: }
1229:
1230: JExpression processExpression(InstanceOfExpression x) {
1231: SourceInfo info = makeSourceInfo(x);
1232: JExpression expr = dispProcessExpression(x.expression);
1233: JReferenceType testType = (JReferenceType) typeMap
1234: .get(x.type.resolvedType);
1235: return new JInstanceOf(program, info, testType, expr);
1236: }
1237:
1238: JExpression processExpression(MessageSend x) {
1239: SourceInfo info = makeSourceInfo(x);
1240: JType type = (JType) typeMap.get(x.resolvedType);
1241: JMethod method = (JMethod) typeMap.get(x.binding);
1242:
1243: JExpression qualifier;
1244: if (x.receiver instanceof ThisReference) {
1245: if (method.isStatic()) {
1246: // don't bother qualifying it, it's a no-op
1247: qualifier = null;
1248: } else if (x.receiver instanceof QualifiedThisReference) {
1249: // use the supplied qualifier
1250: qualifier = dispProcessExpression(x.receiver);
1251: } else {
1252: /*
1253: * In cases where JDT had to synthesize a this ref for us, it could
1254: * actually be the wrong type, if the target method is in an enclosing
1255: * class. We have to synthesize our own ref of the correct type.
1256: */
1257: qualifier = createThisRef(info, method
1258: .getEnclosingType());
1259: }
1260: } else {
1261: qualifier = dispProcessExpression(x.receiver);
1262: }
1263:
1264: JMethodCall call = new JMethodCall(program, info,
1265: qualifier, method);
1266:
1267: // On a super ref, don't allow polymorphic dispatch. Oddly enough,
1268: // QualifiedSuperReference not derived from SuperReference!
1269: boolean isSuperRef = x.receiver instanceof SuperReference
1270: || x.receiver instanceof QualifiedSuperReference;
1271: if (isSuperRef) {
1272: call.setStaticDispatchOnly();
1273: }
1274:
1275: // The arguments come first...
1276: addCallArgs(x.arguments, call, x.binding);
1277:
1278: if (type != method.getType()) {
1279: // Must be a generic; insert a cast operation.
1280: JReferenceType toType = (JReferenceType) type;
1281: return new JCastOperation(program, info, toType, call);
1282: } else {
1283: return call;
1284: }
1285: }
1286:
1287: JExpression processExpression(NullLiteral x) {
1288: return program.getLiteralNull();
1289: }
1290:
1291: JExpression processExpression(OR_OR_Expression x) {
1292: JType type = (JType) typeMap.get(x.resolvedType);
1293: SourceInfo info = makeSourceInfo(x);
1294: return processBinaryOperation(info, JBinaryOperator.OR,
1295: type, x.left, x.right);
1296: }
1297:
1298: JExpression processExpression(PostfixExpression x) {
1299: SourceInfo info = makeSourceInfo(x);
1300: JUnaryOperator op;
1301:
1302: switch (x.operator) {
1303: case PostfixExpression.MINUS:
1304: op = JUnaryOperator.DEC;
1305: break;
1306:
1307: case PostfixExpression.PLUS:
1308: op = JUnaryOperator.INC;
1309: break;
1310:
1311: default:
1312: throw new InternalCompilerException(
1313: "Unexpected postfix operator");
1314: }
1315:
1316: JPostfixOperation postOp = new JPostfixOperation(program,
1317: info, op, dispProcessExpression(x.lhs));
1318: return postOp;
1319: }
1320:
1321: JExpression processExpression(PrefixExpression x) {
1322: SourceInfo info = makeSourceInfo(x);
1323: JUnaryOperator op;
1324:
1325: switch (x.operator) {
1326: case PrefixExpression.MINUS:
1327: op = JUnaryOperator.DEC;
1328: break;
1329:
1330: case PrefixExpression.PLUS:
1331: op = JUnaryOperator.INC;
1332: break;
1333:
1334: default:
1335: throw new InternalCompilerException(
1336: "Unexpected prefix operator");
1337: }
1338:
1339: JPrefixOperation preOp = new JPrefixOperation(program,
1340: info, op, dispProcessExpression(x.lhs));
1341: return preOp;
1342: }
1343:
1344: JExpression processExpression(QualifiedAllocationExpression x) {
1345: /*
1346: * Weird: sometimes JDT will create a QualifiedAllocationExpression with
1347: * no qualifier. I guess this is supposed to let us know that we need to
1348: * synthesize a synthetic this arg based on our own current "this"? But
1349: * plain old regular AllocationExpression also must be treated as if it
1350: * might be be implicitly qualified, so I'm not sure what the point is.
1351: * Let's just defer to the AllocationExpression logic if there's no
1352: * qualifier.
1353: */
1354: if (x.enclosingInstance() == null) {
1355: return processExpression((AllocationExpression) x);
1356: }
1357:
1358: SourceInfo info = makeSourceInfo(x);
1359: MethodBinding b = x.binding;
1360: JMethod ctor = (JMethod) typeMap.get(b);
1361: JClassType enclosingType = (JClassType) ctor
1362: .getEnclosingType();
1363: JNewInstance newInstance = new JNewInstance(program, info,
1364: enclosingType);
1365: JMethodCall call = new JMethodCall(program, info,
1366: newInstance, ctor);
1367: JExpression qualifier = dispProcessExpression(x.enclosingInstance);
1368: List<JExpression> qualList = new ArrayList<JExpression>();
1369: qualList.add(qualifier);
1370:
1371: /*
1372: * Really weird: Sometimes an allocation expression needs both its
1373: * explicit qualifier AND its implicit enclosing class! We add this second
1374: * because the explicit qualifier takes precedence.
1375: */
1376: if (!currentMethod.isStatic()) {
1377: JExpression implicitOuter = program.getExprThisRef(
1378: info, (JClassType) currentClass);
1379: qualList.add(implicitOuter);
1380: }
1381:
1382: // Plain old regular arguments
1383: addCallArgs(x.arguments, call, b);
1384:
1385: // Synthetic args for inner classes
1386: ReferenceBinding targetBinding = b.declaringClass;
1387: if (targetBinding.isNestedType()
1388: && !targetBinding.isStatic()) {
1389: NestedTypeBinding nestedBinding = (NestedTypeBinding) targetBinding;
1390: // Synthetic this args for inner classes
1391: if (nestedBinding.enclosingInstances != null) {
1392: for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
1393: SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
1394: JClassType syntheticThisType = (JClassType) typeMap
1395: .get(arg.type);
1396: call.getArgs().add(
1397: createThisRef(syntheticThisType,
1398: qualList));
1399: }
1400: }
1401: // Synthetic locals for local classes
1402: if (nestedBinding.outerLocalVariables != null) {
1403: for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
1404: SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
1405: JVariable variable = (JVariable) typeMap
1406: .get(arg.actualOuterLocalVariable);
1407: call.getArgs().add(
1408: createVariableRef(info, variable,
1409: arg.actualOuterLocalVariable));
1410: }
1411: }
1412: }
1413:
1414: return call;
1415: }
1416:
1417: JExpression processExpression(QualifiedNameReference x) {
1418: SourceInfo info = makeSourceInfo(x);
1419: Binding binding = x.binding;
1420: JNode node = typeMap.get(binding);
1421: if (!(node instanceof JVariable)) {
1422: return null;
1423: }
1424: JVariable variable = (JVariable) node;
1425:
1426: JExpression curRef = createVariableRef(info, variable,
1427: binding);
1428:
1429: /*
1430: * Wackiness: JDT represents multiple field access as an array of fields,
1431: * each qualified by everything to the left. So each subsequent item in
1432: * otherBindings takes the current expression as a qualifier.
1433: */
1434: if (x.otherBindings != null) {
1435: for (FieldBinding fieldBinding : x.otherBindings) {
1436: JField field;
1437: if (fieldBinding.declaringClass == null) {
1438: // probably array.length
1439: field = program.getIndexedField("Array.length");
1440: if (!field.getName().equals(
1441: String.valueOf(fieldBinding.name))) {
1442: throw new InternalCompilerException(
1443: "Error matching fieldBinding.");
1444: }
1445: } else {
1446: field = (JField) typeMap.get(fieldBinding);
1447: }
1448: curRef = new JFieldRef(program, info, curRef,
1449: field, currentClass);
1450: }
1451: }
1452:
1453: return curRef;
1454: }
1455:
1456: JExpression processExpression(QualifiedSuperReference x) {
1457: JClassType refType = (JClassType) typeMap
1458: .get(x.resolvedType);
1459: JClassType qualType = (JClassType) typeMap
1460: .get(x.qualification.resolvedType);
1461: assert (refType == qualType.extnds);
1462: // Oddly enough, super refs can be modeled as this refs, because whatever
1463: // expression they qualify has already been resolved.
1464: return processQualifiedThisOrSuperRef(x, qualType);
1465: }
1466:
1467: JExpression processExpression(QualifiedThisReference x) {
1468: JClassType refType = (JClassType) typeMap
1469: .get(x.resolvedType);
1470: JClassType qualType = (JClassType) typeMap
1471: .get(x.qualification.resolvedType);
1472: assert (refType == qualType);
1473: return processQualifiedThisOrSuperRef(x, qualType);
1474: }
1475:
1476: JExpression processExpression(SingleNameReference x) {
1477: SourceInfo info = makeSourceInfo(x);
1478: Binding binding = x.binding;
1479: Object target = typeMap.get(binding);
1480: if (!(target instanceof JVariable)) {
1481: return null;
1482: }
1483: JVariable variable = (JVariable) target;
1484:
1485: /*
1486: * Wackiness: if a field happens to have synthetic accessors (only fields
1487: * can have them, apparently), this is a ref to a field in an enclosing
1488: * instance. CreateThisRef should compute a "this" access of the
1489: * appropriate type, unless the field is static.
1490: */
1491: if (x.syntheticAccessors != null) {
1492: JField field = (JField) variable;
1493: if (!field.isStatic()) {
1494: JExpression instance = createThisRef(info, field
1495: .getEnclosingType());
1496: return new JFieldRef(program, info, instance,
1497: field, currentClass);
1498: }
1499: }
1500:
1501: return createVariableRef(info, variable, binding);
1502: }
1503:
1504: JExpression processExpression(SuperReference x) {
1505: JClassType type = (JClassType) typeMap.get(x.resolvedType);
1506: assert (type == currentClass.extnds);
1507: SourceInfo info = makeSourceInfo(x);
1508: // Oddly enough, super refs can be modeled as a this refs.
1509: JExpression super Ref = createThisRef(info, currentClass);
1510: return super Ref;
1511: }
1512:
1513: JExpression processExpression(ThisReference x) {
1514: JClassType type = (JClassType) typeMap.get(x.resolvedType);
1515: assert (type == currentClass);
1516: SourceInfo info = makeSourceInfo(x);
1517: JExpression this Ref = createThisRef(info, currentClass);
1518: return this Ref;
1519: }
1520:
1521: JExpression processExpression(UnaryExpression x) {
1522: SourceInfo info = makeSourceInfo(x);
1523: JUnaryOperator op;
1524: int operator = ((x.bits & UnaryExpression.OperatorMASK) >> UnaryExpression.OperatorSHIFT);
1525:
1526: switch (operator) {
1527: case UnaryExpression.MINUS:
1528: op = JUnaryOperator.NEG;
1529: break;
1530:
1531: case UnaryExpression.NOT:
1532: op = JUnaryOperator.NOT;
1533: break;
1534:
1535: case UnaryExpression.PLUS:
1536: // Odd case.. a useless + operator; just return the operand
1537: return dispProcessExpression(x.expression);
1538:
1539: case UnaryExpression.TWIDDLE:
1540: op = JUnaryOperator.BIT_NOT;
1541: break;
1542:
1543: default:
1544: throw new InternalCompilerException(
1545: "Unexpected operator for unary expression");
1546: }
1547:
1548: JPrefixOperation preOp = new JPrefixOperation(program,
1549: info, op, dispProcessExpression(x.expression));
1550: return preOp;
1551: }
1552:
1553: List<JExpressionStatement> processExpressionStatements(
1554: Statement[] statements) {
1555: List<JExpressionStatement> jstatements = new ArrayList<JExpressionStatement>();
1556: if (statements != null) {
1557: for (int i = 0, n = statements.length; i < n; ++i) {
1558: JStatement jstmt = dispProcessStatement(statements[i]);
1559: if (jstmt != null) {
1560: jstatements.add((JExpressionStatement) jstmt);
1561: }
1562: }
1563: }
1564: return jstatements;
1565: }
1566:
1567: void processField(FieldDeclaration declaration) {
1568: JField field = (JField) typeMap.tryGet(declaration.binding);
1569: if (field == null) {
1570: /*
1571: * When anonymous classes declare constant fields, the field declaration
1572: * is not visited by JDT. Just bail since any references to that field
1573: * are guaranteed to be replaced with literals.
1574: */
1575: return;
1576: }
1577: try {
1578: JExpression initializer = null;
1579: if (declaration.initialization != null) {
1580: initializer = dispProcessExpression(declaration.initialization);
1581: }
1582:
1583: if (field instanceof JEnumField) {
1584: // An enum field must be initialized!
1585: assert (initializer instanceof JMethodCall);
1586: }
1587:
1588: if (initializer instanceof JLiteral) {
1589: field.constInitializer = (JLiteral) initializer;
1590: } else if (initializer != null) {
1591: SourceInfo info = makeSourceInfo(declaration);
1592: JStatement assignStmt = program
1593: .createAssignmentStmt(info,
1594: createVariableRef(info, field),
1595: initializer);
1596:
1597: // will either be init or clinit
1598: currentMethodBody.getStatements().add(assignStmt);
1599: }
1600: } catch (Throwable e) {
1601: throw translateException(field, e);
1602: }
1603: }
1604:
1605: void processInitializer(Initializer initializer) {
1606: JBlock block = (JBlock) dispProcessStatement(initializer.block);
1607: try {
1608: // will either be init or clinit
1609: currentMethodBody.getStatements().add(block);
1610: } catch (Throwable e) {
1611: throw translateException(initializer, e);
1612: }
1613: }
1614:
1615: void processMethod(AbstractMethodDeclaration x) {
1616: MethodBinding b = x.binding;
1617: JMethod method = (JMethod) typeMap.get(b);
1618: try {
1619: if (b.isImplementing() || b.isOverriding()) {
1620: tryFindUpRefs(method, b);
1621: }
1622:
1623: if (x.isNative()) {
1624: processNativeMethod(x, (JsniMethodBody) method
1625: .getBody());
1626: return;
1627: }
1628:
1629: currentMethod = method;
1630: currentMethodBody = (JMethodBody) method.getBody();
1631: currentMethodScope = x.scope;
1632:
1633: if (x.statements != null) {
1634: for (int i = 0, n = x.statements.length; i < n; ++i) {
1635: Statement origStmt = x.statements[i];
1636: JStatement jstmt = dispProcessStatement(origStmt);
1637: if (jstmt != null) {
1638: currentMethodBody.getStatements()
1639: .add(jstmt);
1640: }
1641: }
1642: }
1643: currentMethodScope = null;
1644: currentMethodBody = null;
1645: currentMethod = null;
1646: } catch (Throwable e) {
1647: throw translateException(method, e);
1648: }
1649: }
1650:
1651: void processNativeMethod(AbstractMethodDeclaration x,
1652: JsniMethodBody nativeMethodBody) {
1653:
1654: JsFunction func = nativeMethodBody.getFunc();
1655: if (func == null) {
1656: return;
1657: }
1658:
1659: // resolve jsni refs
1660: new JsniRefResolver(x, nativeMethodBody).accept(func);
1661: }
1662:
1663: JStatement processStatement(AssertStatement x) {
1664: SourceInfo info = makeSourceInfo(x);
1665: JExpression expr = dispProcessExpression(x.assertExpression);
1666: JExpression arg = dispProcessExpression(x.exceptionArgument);
1667: return new JAssertStatement(program, info, expr, arg);
1668: }
1669:
1670: JBlock processStatement(Block x) {
1671: if (x == null) {
1672: return null;
1673: }
1674:
1675: SourceInfo info = makeSourceInfo(x);
1676: JBlock block = new JBlock(program, info);
1677: if (x.statements != null) {
1678: for (int i = 0, n = x.statements.length; i < n; ++i) {
1679: JStatement jstmt = dispProcessStatement(x.statements[i]);
1680: if (jstmt != null) {
1681: block.statements.add(jstmt);
1682: }
1683: }
1684: }
1685: return block;
1686: }
1687:
1688: JStatement processStatement(BreakStatement x) {
1689: SourceInfo info = makeSourceInfo(x);
1690: return new JBreakStatement(program, info, getOrCreateLabel(
1691: info, currentMethod, x.label));
1692: }
1693:
1694: JStatement processStatement(CaseStatement x) {
1695: SourceInfo info = makeSourceInfo(x);
1696: JExpression expression = dispProcessExpression(x.constantExpression);
1697: if (x.isEnumConstant) {
1698: // TODO: propagate enum information?
1699: JFieldRef fieldRef = (JFieldRef) expression;
1700: JEnumField field = (JEnumField) fieldRef.getField();
1701: return new JCaseStatement(program, info, program
1702: .getLiteralInt(field.ordinal()));
1703: } else {
1704: return new JCaseStatement(program, info,
1705: (JLiteral) expression);
1706: }
1707: }
1708:
1709: JStatement processStatement(ContinueStatement x) {
1710: SourceInfo info = makeSourceInfo(x);
1711: return new JContinueStatement(program, info,
1712: getOrCreateLabel(info, currentMethod, x.label));
1713: }
1714:
1715: JStatement processStatement(DoStatement x) {
1716: SourceInfo info = makeSourceInfo(x);
1717: JExpression loopTest = dispProcessExpression(x.condition);
1718: JStatement loopBody = dispProcessStatement(x.action);
1719: JDoStatement stmt = new JDoStatement(program, info,
1720: loopTest, loopBody);
1721: return stmt;
1722: }
1723:
1724: JStatement processStatement(EmptyStatement x) {
1725: return null;
1726: }
1727:
1728: JStatement processStatement(ForeachStatement x) {
1729: SourceInfo info = makeSourceInfo(x);
1730:
1731: JBlock body;
1732: JStatement action = dispProcessStatement(x.action);
1733: if (action instanceof JBlock) {
1734: body = (JBlock) action;
1735: } else {
1736: body = new JBlock(program, info);
1737: body.statements.add(action);
1738: }
1739:
1740: JLocal elementVar = (JLocal) typeMap
1741: .get(x.elementVariable.binding);
1742: String elementVarName = elementVar.getName();
1743:
1744: JLocalDeclarationStatement elementDecl = (JLocalDeclarationStatement) processStatement(x.elementVariable);
1745: assert (elementDecl.initializer == null);
1746:
1747: JForStatement result;
1748: if (x.collection.resolvedType.isArrayType()) {
1749: /**
1750: * <pre>
1751: * for (T[] i$array = collection, int i$index = 0, int i$max = i$array.length;
1752: * i$index < i$max; ++i$index) {
1753: * T elementVar = i$array[i$index];
1754: * // user action
1755: * }
1756: * </pre>
1757: */
1758: JLocal arrayVar = createSyntheticLocal(info,
1759: elementVarName + "$array", (JType) typeMap
1760: .get(x.collection.resolvedType));
1761: JLocal indexVar = createSyntheticLocal(info,
1762: elementVarName + "$index", program
1763: .getTypePrimitiveInt());
1764: JLocal maxVar = createSyntheticLocal(info,
1765: elementVarName + "$max", program
1766: .getTypePrimitiveInt());
1767:
1768: List<JStatement> initializers = new ArrayList<JStatement>(
1769: 3);
1770: // T[] i$array = arr
1771: initializers.add(createLocalDeclaration(info, arrayVar,
1772: dispProcessExpression(x.collection)));
1773: // int i$index = 0
1774: initializers.add(createLocalDeclaration(info, indexVar,
1775: program.getLiteralInt(0)));
1776: // int i$max = i$array.length
1777: initializers.add(createLocalDeclaration(info, maxVar,
1778: new JFieldRef(program, info, createVariableRef(
1779: info, arrayVar), program
1780: .getIndexedField("Array.length"),
1781: currentClass)));
1782:
1783: // i$index < i$max
1784: JExpression condition = new JBinaryOperation(program,
1785: info, program.getTypePrimitiveBoolean(),
1786: JBinaryOperator.LT, createVariableRef(info,
1787: indexVar), createVariableRef(info,
1788: maxVar));
1789:
1790: // ++i$index
1791: List<JExpressionStatement> increments = new ArrayList<JExpressionStatement>(
1792: 1);
1793: increments.add(new JPrefixOperation(program, info,
1794: JUnaryOperator.INC, createVariableRef(info,
1795: indexVar)).makeStatement());
1796:
1797: // T elementVar = i$array[i$index];
1798: elementDecl.initializer = new JArrayRef(program, info,
1799: createVariableRef(info, arrayVar),
1800: createVariableRef(info, indexVar));
1801: body.statements.add(0, elementDecl);
1802:
1803: result = new JForStatement(program, info, initializers,
1804: condition, increments, body);
1805: } else {
1806: /**
1807: * <pre>
1808: * for (Iterator<T> i$iterator = collection.iterator(); i$iterator.hasNext(); ) {
1809: * T elementVar = i$iterator.next();
1810: * // user action
1811: * }
1812: * </pre>
1813: */
1814: JLocal iteratorVar = createSyntheticLocal(info,
1815: elementVarName + "$iterator", (JType) typeMap
1816: .get(x.collection.resolvedType));
1817:
1818: List<JStatement> initializers = new ArrayList<JStatement>(
1819: 1);
1820: // Iterator<T> i$iterator = collection.iterator()
1821: initializers
1822: .add(createLocalDeclaration(
1823: info,
1824: iteratorVar,
1825: new JMethodCall(
1826: program,
1827: info,
1828: dispProcessExpression(x.collection),
1829: program
1830: .getIndexedMethod("Iterable.iterator"))));
1831:
1832: // i$iterator.hasNext()
1833: JExpression condition = new JMethodCall(program, info,
1834: createVariableRef(info, iteratorVar), program
1835: .getIndexedMethod("Iterator.hasNext"));
1836:
1837: // T elementVar = i$array[i$index];
1838: elementDecl.initializer = new JMethodCall(program,
1839: info, createVariableRef(info, iteratorVar),
1840: program.getIndexedMethod("Iterator.next"));
1841: body.statements.add(0, elementDecl);
1842:
1843: result = new JForStatement(program, info, initializers,
1844: condition, Collections
1845: .<JExpressionStatement> emptyList(),
1846: body);
1847: }
1848:
1849: // May need to box or unbox the assignment.
1850: if (x.elementVariableImplicitWidening != -1) {
1851: if ((x.elementVariableImplicitWidening & TypeIds.BOXING) != 0) {
1852: elementDecl.initializer = box(
1853: elementDecl.initializer,
1854: (JClassType) elementVar.getType());
1855: } else if ((x.elementVariableImplicitWidening & TypeIds.UNBOXING) != 0) {
1856: elementDecl.initializer = unbox(
1857: elementDecl.initializer,
1858: (JPrimitiveType) elementVar.getType());
1859: }
1860: }
1861: return result;
1862: }
1863:
1864: JStatement processStatement(ForStatement x) {
1865: SourceInfo info = makeSourceInfo(x);
1866: // SEE NOTE ON JDT FORCED OPTIMIZATIONS
1867: // If the condition is false, don't process the body
1868: boolean removeBody = isOptimizedFalse(x.condition);
1869:
1870: List<JStatement> init = processStatements(x.initializations);
1871: JExpression expr = dispProcessExpression(x.condition);
1872: List<JExpressionStatement> incr = processExpressionStatements(x.increments);
1873: JStatement body = removeBody ? null
1874: : dispProcessStatement(x.action);
1875: return new JForStatement(program, info, init, expr, incr,
1876: body);
1877: }
1878:
1879: JStatement processStatement(IfStatement x) {
1880: // SEE NOTE ON JDT FORCED OPTIMIZATIONS
1881: // If the condition is false, don't process the then statement
1882: // If the condition is false, don't process the else statement
1883: boolean removeThen = isOptimizedFalse(x.condition);
1884: boolean removeElse = isOptimizedTrue(x.condition);
1885:
1886: SourceInfo info = makeSourceInfo(x);
1887: JExpression expr = dispProcessExpression(x.condition);
1888: JStatement thenStmt = removeThen ? null
1889: : dispProcessStatement(x.thenStatement);
1890: JStatement elseStmt = removeElse ? null
1891: : dispProcessStatement(x.elseStatement);
1892: JIfStatement ifStmt = new JIfStatement(program, info, expr,
1893: thenStmt, elseStmt);
1894: return ifStmt;
1895: }
1896:
1897: JStatement processStatement(LabeledStatement x) {
1898: JStatement body = dispProcessStatement(x.statement);
1899: if (body == null) {
1900: return null;
1901: }
1902: SourceInfo info = makeSourceInfo(x);
1903: return new JLabeledStatement(program, info,
1904: getOrCreateLabel(info, currentMethod, x.label),
1905: body);
1906: }
1907:
1908: JStatement processStatement(LocalDeclaration x) {
1909: SourceInfo info = makeSourceInfo(x);
1910: JLocal local = (JLocal) typeMap.get(x.binding);
1911: JLocalRef localRef = new JLocalRef(program, info, local);
1912: JExpression initializer = dispProcessExpression(x.initialization);
1913: return new JLocalDeclarationStatement(program, info,
1914: localRef, initializer);
1915: }
1916:
1917: JStatement processStatement(ReturnStatement x) {
1918: SourceInfo info = makeSourceInfo(x);
1919: if (currentMethodScope.referenceContext instanceof ConstructorDeclaration) {
1920: /*
1921: * Special: constructors are implemented as instance methods that return
1922: * their this object, so any embedded return statements have to be fixed
1923: * up.
1924: */
1925: JClassType enclosingType = (JClassType) currentMethod
1926: .getEnclosingType();
1927: assert (x.expression == null);
1928: return new JReturnStatement(program, info,
1929: createThisRef(info, enclosingType));
1930: } else {
1931: return new JReturnStatement(program, info,
1932: dispProcessExpression(x.expression));
1933: }
1934: }
1935:
1936: JStatement processStatement(SwitchStatement x) {
1937: SourceInfo info = makeSourceInfo(x);
1938: JExpression expression = dispProcessExpression(x.expression);
1939: if (expression.getType() instanceof JClassType) {
1940: // Must be an enum; synthesize a call to ordinal().
1941: expression = new JMethodCall(program, info, expression,
1942: program.getIndexedMethod("Enum.ordinal"));
1943: }
1944: JBlock block = new JBlock(program, info);
1945: block.statements = processStatements(x.statements);
1946: return new JSwitchStatement(program, info, expression,
1947: block);
1948: }
1949:
1950: JStatement processStatement(SynchronizedStatement x) {
1951: JBlock block = (JBlock) dispProcessStatement(x.block);
1952: JExpression expr = dispProcessExpression(x.expression);
1953: block.statements.add(0, expr.makeStatement());
1954: return block;
1955: }
1956:
1957: JStatement processStatement(ThrowStatement x) {
1958: SourceInfo info = makeSourceInfo(x);
1959: JExpression toThrow = dispProcessExpression(x.exception);
1960: return new JThrowStatement(program, info, toThrow);
1961: }
1962:
1963: JStatement processStatement(TryStatement x) {
1964: SourceInfo info = makeSourceInfo(x);
1965: JBlock tryBlock = (JBlock) dispProcessStatement(x.tryBlock);
1966: List<JLocalRef> catchArgs = new ArrayList<JLocalRef>();
1967: List<JBlock> catchBlocks = new ArrayList<JBlock>();
1968: if (x.catchBlocks != null) {
1969: for (int i = 0, c = x.catchArguments.length; i < c; ++i) {
1970: JLocal local = (JLocal) typeMap
1971: .get(x.catchArguments[i].binding);
1972: catchArgs.add((JLocalRef) createVariableRef(info,
1973: local));
1974: }
1975: for (int i = 0, c = x.catchBlocks.length; i < c; ++i) {
1976: catchBlocks
1977: .add((JBlock) dispProcessStatement(x.catchBlocks[i]));
1978: }
1979: }
1980: JBlock finallyBlock = (JBlock) dispProcessStatement(x.finallyBlock);
1981: return new JTryStatement(program, info, tryBlock,
1982: catchArgs, catchBlocks, finallyBlock);
1983: }
1984:
1985: JStatement processStatement(TypeDeclaration x) {
1986: // do nothing -- the local class is treated at the program level
1987: return null;
1988: }
1989:
1990: JStatement processStatement(WhileStatement x) {
1991: // SEE NOTE ON JDT FORCED OPTIMIZATIONS
1992: // If the condition is false, don't process the body
1993: boolean removeBody = isOptimizedFalse(x.condition);
1994:
1995: SourceInfo info = makeSourceInfo(x);
1996: JExpression loopTest = dispProcessExpression(x.condition);
1997: JStatement loopBody = removeBody ? null
1998: : dispProcessStatement(x.action);
1999: JWhileStatement stmt = new JWhileStatement(program, info,
2000: loopTest, loopBody);
2001: return stmt;
2002: }
2003:
2004: List<JStatement> processStatements(Statement[] statements) {
2005: List<JStatement> jstatements = new ArrayList<JStatement>();
2006: if (statements != null) {
2007: for (int i = 0, n = statements.length; i < n; ++i) {
2008: JStatement jstmt = dispProcessStatement(statements[i]);
2009: if (jstmt != null) {
2010: jstatements.add(jstmt);
2011: }
2012: }
2013: }
2014: return jstatements;
2015: }
2016:
2017: JMethodCall processSuperConstructorCall(
2018: ExplicitConstructorCall x) {
2019: SourceInfo info = makeSourceInfo(x);
2020: JMethod ctor = (JMethod) typeMap.get(x.binding);
2021: JExpression trueQualifier = createThisRef(info,
2022: currentClass);
2023: JMethodCall call = new JMethodCall(program, info,
2024: trueQualifier, ctor);
2025:
2026: addCallArgs(x.arguments, call, x.binding);
2027:
2028: // We have to find and pass through any synthetics our supertype needs
2029: ReferenceBinding super Class = x.binding.declaringClass;
2030: if (super Class instanceof NestedTypeBinding
2031: && !super Class.isStatic()) {
2032: NestedTypeBinding myBinding = (NestedTypeBinding) currentClassScope
2033: .referenceType().binding;
2034: NestedTypeBinding super Binding = (NestedTypeBinding) super Class;
2035:
2036: // enclosing types
2037: if (super Binding.enclosingInstances != null) {
2038: JExpression qualifier = dispProcessExpression(x.qualification);
2039: for (int j = 0; j < super Binding.enclosingInstances.length; ++j) {
2040: SyntheticArgumentBinding arg = super Binding.enclosingInstances[j];
2041: JClassType classType = (JClassType) typeMap
2042: .get(arg.type);
2043: if (qualifier == null) {
2044: /*
2045: * Got to be one of my params; it would be illegal to use a this
2046: * ref at this moment-- we would most likely be passing in a
2047: * supertype field that HASN'T BEEN INITIALIZED YET.
2048: *
2049: * Unfortunately, my params might not work as-is, so we have to
2050: * check each one to see if any will make a suitable this ref.
2051: */
2052: List<JExpression> workList = new ArrayList<JExpression>();
2053: Iterator<JParameter> paramIt = getSyntheticsIterator(currentMethod);
2054: for (int i = 0; i < myBinding.enclosingInstances.length; ++i) {
2055: workList.add(createVariableRef(info,
2056: paramIt.next()));
2057: }
2058: call.getArgs().add(
2059: createThisRef(classType, workList));
2060: } else {
2061: call.getArgs()
2062: .add(
2063: createThisRef(classType,
2064: qualifier));
2065: }
2066: }
2067: }
2068:
2069: // outer locals
2070: if (super Binding.outerLocalVariables != null) {
2071: for (int j = 0; j < super Binding.outerLocalVariables.length; ++j) {
2072: SyntheticArgumentBinding arg = super Binding.outerLocalVariables[j];
2073: // Got to be one of my params
2074: JType varType = (JType) typeMap.get(arg.type);
2075: String varName = String.valueOf(arg.name);
2076: JParameter param = null;
2077: for (int i = 0; i < currentMethod.params.size(); ++i) {
2078: JParameter paramIt = currentMethod.params
2079: .get(i);
2080: if (varType == paramIt.getType()
2081: && varName
2082: .equals(paramIt.getName())) {
2083: param = paramIt;
2084: }
2085: }
2086: if (param == null) {
2087: throw new InternalCompilerException(
2088: "Could not find matching local arg for explicit super ctor call.");
2089: }
2090: call.getArgs().add(
2091: createVariableRef(info, param));
2092: }
2093: }
2094: }
2095:
2096: return call;
2097: }
2098:
2099: JMethodCall processThisConstructorCall(ExplicitConstructorCall x) {
2100: SourceInfo info = makeSourceInfo(x);
2101: JMethod ctor = (JMethod) typeMap.get(x.binding);
2102: JExpression trueQualifier = createThisRef(info,
2103: currentClass);
2104: JMethodCall call = new JMethodCall(program, info,
2105: trueQualifier, ctor);
2106:
2107: assert (x.qualification == null);
2108:
2109: addCallArgs(x.arguments, call, x.binding);
2110:
2111: // All synthetics must be passed through to the target ctor
2112: ReferenceBinding declaringClass = x.binding.declaringClass;
2113: if (declaringClass instanceof NestedTypeBinding) {
2114: Iterator<JParameter> paramIt = getSyntheticsIterator(currentMethod);
2115: NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
2116: if (nestedBinding.enclosingInstances != null) {
2117: for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
2118: call.getArgs()
2119: .add(
2120: createVariableRef(info, paramIt
2121: .next()));
2122: }
2123: }
2124: if (nestedBinding.outerLocalVariables != null) {
2125: for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
2126: call.getArgs()
2127: .add(
2128: createVariableRef(info, paramIt
2129: .next()));
2130: }
2131: }
2132: }
2133:
2134: return call;
2135: }
2136:
2137: private void addAllOuterThisRefs(List<? super JFieldRef> list,
2138: JExpression expr, JClassType classType) {
2139: if (classType.fields.size() > 0) {
2140: JField field = classType.fields.get(0);
2141: if (field.getName().startsWith("this$")) {
2142: list
2143: .add(new JFieldRef(program, expr
2144: .getSourceInfo(), expr, field,
2145: currentClass));
2146: }
2147: }
2148: }
2149:
2150: private void addAllOuterThisRefsPlusSuperChain(
2151: List<? super JFieldRef> workList, JExpression expr,
2152: JClassType classType) {
2153: for (; classType != null; classType = classType.extnds) {
2154: addAllOuterThisRefs(workList, expr, classType);
2155: }
2156: }
2157:
2158: private void addCallArgs(Expression[] args, JMethodCall call,
2159: MethodBinding binding) {
2160: if (args != null) {
2161: TypeBinding[] params = binding.parameters;
2162: int n = params.length;
2163:
2164: if (binding.isVarargs()) {
2165: // Do everything but the last arg.
2166: --n;
2167: }
2168:
2169: for (int i = 0; i < n; ++i) {
2170: call.getArgs().add(dispProcessExpression(args[i]));
2171: }
2172:
2173: if (binding.isVarargs()) {
2174: // Handle the last arg.
2175: JArrayType type = (JArrayType) typeMap
2176: .get(params[n]);
2177:
2178: // See if there is only one arg and it's an array of the correct dims.
2179: if (args.length == n + 1) {
2180: JType lastArgType = (JType) typeMap
2181: .get(args[n].resolvedType);
2182: if (lastArgType instanceof JArrayType) {
2183: JArrayType lastArgArrayType = (JArrayType) lastArgType;
2184: if (lastArgArrayType.getDims() == type
2185: .getDims()) {
2186: // Looks like it's already an array.
2187: call.getArgs().add(
2188: dispProcessExpression(args[n]));
2189: return;
2190: }
2191: }
2192: }
2193:
2194: // Fall through: must synthesize a new array allocation.
2195: SourceInfo info = makeSourceInfo(args[n]);
2196: JNewArray newArray = new JNewArray(program, info,
2197: type);
2198: newArray.initializers = new ArrayList<JExpression>();
2199: for (int i = n; i < args.length; ++i) {
2200: newArray.initializers
2201: .add(dispProcessExpression(args[i]));
2202: }
2203: call.getArgs().add(newArray);
2204: }
2205: }
2206: }
2207:
2208: private JExpression box(JExpression toBox,
2209: JClassType wrapperType) {
2210: JPrimitiveType primitiveType = getPrimitiveTypeForWrapperType(wrapperType);
2211: if (primitiveType == null) {
2212: throw new InternalCompilerException(toBox,
2213: "Failed to find primitive type for wrapper type '"
2214: + wrapperType.getName() + "'", null);
2215: }
2216:
2217: // Find the correct valueOf() method.
2218: JMethod valueOfMethod = null;
2219: for (JMethod method : wrapperType.methods) {
2220: if ("valueOf".equals(method.getName())) {
2221: if (method.params.size() == 1) {
2222: JParameter param = method.params.get(0);
2223: if (param.getType() == primitiveType) {
2224: // Found it.
2225: valueOfMethod = method;
2226: break;
2227: }
2228: }
2229: }
2230: }
2231:
2232: if (valueOfMethod == null || !valueOfMethod.isStatic()
2233: || valueOfMethod.getType() != wrapperType) {
2234: throw new InternalCompilerException(
2235: toBox,
2236: "Expected to find a method on '"
2237: + wrapperType.getName()
2238: + "' whose signature matches 'public static "
2239: + wrapperType.getName() + " valueOf("
2240: + primitiveType.getName() + ")'", null);
2241: }
2242:
2243: // Create the boxing call.
2244: JMethodCall call = new JMethodCall(program, toBox
2245: .getSourceInfo(), null, valueOfMethod);
2246: call.getArgs().add(toBox);
2247: return call;
2248: }
2249:
2250: private JExpression box(JExpression toBox,
2251: JPrimitiveType primitiveType) {
2252: // Find the wrapper type for this primitive type.
2253: String wrapperTypeName = primitiveType.getWrapperTypeName();
2254: JClassType wrapperType = (JClassType) program
2255: .getFromTypeMap(wrapperTypeName);
2256: if (wrapperType == null) {
2257: throw new InternalCompilerException(toBox,
2258: "Cannot find wrapper type '" + wrapperTypeName
2259: + "' associated with primitive type '"
2260: + primitiveType.getName() + "'", null);
2261: }
2262: return box(toBox, wrapperType);
2263: }
2264:
2265: private JField createEnumValueMap(JEnumType type) {
2266: JsonObject map = new JsonObject(program);
2267: for (JEnumField field : type.enumList) {
2268: // JSON maps require leading underscores to prevent collisions.
2269: JStringLiteral key = program.getLiteralString("_"
2270: + field.getName());
2271: JFieldRef value = new JFieldRef(program, null, null,
2272: field, type);
2273: map.propInits.add(new JsonObject.JsonPropInit(program,
2274: key, value));
2275: }
2276: JField mapField = program.createField(null, "enum$map"
2277: .toCharArray(), type, map.getType(), true, true,
2278: true);
2279:
2280: // Initialize in clinit.
2281: JMethodBody clinitBody = (JMethodBody) type.methods.get(0)
2282: .getBody();
2283: JExpressionStatement assignment = program
2284: .createAssignmentStmt(null, createVariableRef(null,
2285: mapField), map);
2286: clinitBody.getStatements().add(assignment);
2287: return mapField;
2288: }
2289:
2290: private JLocalDeclarationStatement createLocalDeclaration(
2291: SourceInfo info, JLocal arrayVar, JExpression value) {
2292: return new JLocalDeclarationStatement(program, info,
2293: new JLocalRef(program, info, arrayVar), value);
2294: }
2295:
2296: /**
2297: * Helper to create a qualified "this" ref (really a synthetic this field
2298: * access) of the appropriate type. Always use this method instead of
2299: * creating a naked JThisRef or you won't get the synthetic accesses right.
2300: */
2301: private JExpression createQualifiedThisRef(SourceInfo info,
2302: JClassType targetType) {
2303: assert (currentClass instanceof JClassType);
2304: JExpression expr = program.getExprThisRef(info,
2305: (JClassType) currentClass);
2306: List<JExpression> list = new ArrayList<JExpression>();
2307: addAllOuterThisRefsPlusSuperChain(list, expr,
2308: (JClassType) currentClass);
2309: return createThisRef(targetType, list);
2310: }
2311:
2312: private JLocal createSyntheticLocal(SourceInfo info,
2313: String name, JType type) {
2314: return program.createLocal(info, name.toCharArray(), type,
2315: false, currentMethodBody);
2316: }
2317:
2318: /**
2319: * Helper to create an expression of the target type, possibly by accessing
2320: * synthetic this fields on the passed-in expression. This is needed by a
2321: * QualifiedAllocationExpression, because the qualifier may not be the
2322: * correct type, and we may need use one of its fields.
2323: */
2324: private JExpression createThisRef(JReferenceType qualType,
2325: JExpression expr) {
2326: List<JExpression> list = new ArrayList<JExpression>();
2327: list.add(expr);
2328: return createThisRef(qualType, list);
2329: }
2330:
2331: /**
2332: * Helper to create an expression of the target type, possibly by accessing
2333: * synthetic this fields on ANY of several passed-in expressions. Why in the
2334: * world would we need to do this? It turns out that when making an
2335: * unqualified explicit super constructor call to something that needs a
2336: * synthetic outer this arg, the correct value to pass in can be one of
2337: * several of the calling constructor's own synthetic ags. The catch is,
2338: * it's possible none of the args are exactly the right type-- we have to
2339: * make one of them the right type by following each of their synthetic this
2340: * refs up an arbitrarily big tree of enclosing classes and
2341: * supertypes-with-enclosing-classes until we find something that's the
2342: * right type.
2343: *
2344: * We have this implemented as a Breadth-First Search to minimize the number
2345: * of derefs required, and this seems to be correct. Note that we explicitly
2346: * prefer the current expression as one of its supertypes over a synthetic
2347: * this ref rooted off the current expression that happens to be the correct
2348: * type. We have observed this to be consistent with how Java handles it.
2349: */
2350: private JExpression createThisRef(JReferenceType qualType,
2351: List<JExpression> list) {
2352: LinkedList<JExpression> workList = new LinkedList<JExpression>();
2353: workList.addAll(list);
2354: while (!workList.isEmpty()) {
2355: JExpression expr = workList.removeFirst();
2356: JClassType classType = (JClassType) expr.getType();
2357: for (; classType != null; classType = classType.extnds) {
2358: // prefer myself or myself-as-supertype over any of my this$ fields
2359: // that may have already been added to the work list
2360: if (program.typeOracle.canTriviallyCast(classType,
2361: qualType)) {
2362: return expr;
2363: }
2364: addAllOuterThisRefs(workList, expr, classType);
2365: }
2366: }
2367:
2368: throw new InternalCompilerException(
2369: "Cannot create a ThisRef of the appropriate type.");
2370: }
2371:
2372: /**
2373: * Helper to creates this ref (or maybe a synthetic this field access) of
2374: * the appropriate type. Always use this method instead of creating a naked
2375: * JThisRef or you won't get the synthetic accesses right.
2376: */
2377: private JExpression createThisRef(SourceInfo info,
2378: JReferenceType targetType) {
2379: assert (currentClass instanceof JClassType);
2380: return createThisRef(targetType, program.getExprThisRef(
2381: info, (JClassType) currentClass));
2382: }
2383:
2384: /**
2385: * Creates an appropriate JVariableRef for the polymorphic type of the
2386: * requested JVariable.
2387: */
2388: private JVariableRef createVariableRef(SourceInfo info,
2389: JVariable variable) {
2390: if (variable instanceof JLocal) {
2391: JLocal local = (JLocal) variable;
2392: if (local.getEnclosingMethod() != currentMethod) {
2393: throw new InternalCompilerException(
2394: "LocalRef referencing local in a different method.");
2395: }
2396: return new JLocalRef(program, info, local);
2397: } else if (variable instanceof JParameter) {
2398: JParameter parameter = (JParameter) variable;
2399: if (parameter.getEnclosingMethod() != currentMethod) {
2400: throw new InternalCompilerException(
2401: "ParameterRef referencing param in a different method.");
2402: }
2403: return new JParameterRef(program, info, parameter);
2404: } else if (variable instanceof JField) {
2405: JField field = (JField) variable;
2406: JExpression instance = null;
2407: if (!field.isStatic()) {
2408: JClassType fieldEnclosingType = (JClassType) field
2409: .getEnclosingType();
2410: instance = createThisRef(info, fieldEnclosingType);
2411: if (!program.typeOracle.canTriviallyCast(
2412: (JClassType) instance.getType(),
2413: fieldEnclosingType)) {
2414: throw new InternalCompilerException(
2415: "FieldRef referencing field in a different type.");
2416: }
2417: }
2418: return new JFieldRef(program, info, instance, field,
2419: currentClass);
2420: }
2421: throw new InternalCompilerException(
2422: "Unknown JVariable subclass.");
2423: }
2424:
2425: /**
2426: * Creates an appropriate JVariableRef for the polymorphic type of the
2427: * requested JVariable.
2428: */
2429: private JVariableRef createVariableRef(SourceInfo info,
2430: JVariable variable, Binding binding) {
2431: // Fix up the reference if it's to an outer local/param
2432: variable = possiblyReferenceOuterLocal(variable, binding);
2433: if (variable == null) {
2434: /*
2435: * Strange case: in certain circumstances, JDT will fail to provide an
2436: * emulation path to an outer local variable. In the case I know of, the
2437: * reference is a spurious qualifier to a static method call. Let's just
2438: * return null and ditch the expression.
2439: */
2440: return null;
2441: }
2442: return createVariableRef(info, variable);
2443: }
2444:
2445: private SourceTypeBinding erasure(TypeBinding typeBinding) {
2446: if (typeBinding instanceof ParameterizedTypeBinding) {
2447: typeBinding = ((ParameterizedTypeBinding) typeBinding)
2448: .erasure();
2449: }
2450: return (SourceTypeBinding) typeBinding;
2451: }
2452:
2453: /**
2454: * Get a new label of a particular name, or create a new one if it doesn't
2455: * exist already.
2456: */
2457: private JLabel getOrCreateLabel(SourceInfo info,
2458: JMethod enclosingMethod, char[] name) {
2459: if (name == null) {
2460: return null;
2461: }
2462: String sname = String.valueOf(name);
2463: Map<String, JLabel> lblMap = this .labelMap
2464: .get(enclosingMethod);
2465: if (lblMap == null) {
2466: lblMap = new HashMap<String, JLabel>();
2467: this .labelMap.put(enclosingMethod, lblMap);
2468: }
2469: JLabel jlabel = lblMap.get(sname);
2470: if (jlabel == null) {
2471: jlabel = new JLabel(program, info, sname);
2472: lblMap.put(sname, jlabel);
2473: }
2474: return jlabel;
2475: }
2476:
2477: private JPrimitiveType getPrimitiveTypeForWrapperType(
2478: JClassType wrapperType) {
2479: String wrapperTypeName = wrapperType.getName();
2480: if ("java.lang.Integer".equals(wrapperTypeName)) {
2481: return program.getTypePrimitiveInt();
2482: } else if ("java.lang.Boolean".equals(wrapperTypeName)) {
2483: return program.getTypePrimitiveBoolean();
2484: } else if ("java.lang.Character".equals(wrapperTypeName)) {
2485: return program.getTypePrimitiveChar();
2486: } else if ("java.lang.Long".equals(wrapperTypeName)) {
2487: return program.getTypePrimitiveLong();
2488: } else if ("java.lang.Short".equals(wrapperTypeName)) {
2489: return program.getTypePrimitiveShort();
2490: } else if ("java.lang.Byte".equals(wrapperTypeName)) {
2491: return program.getTypePrimitiveByte();
2492: } else if ("java.lang.Double".equals(wrapperTypeName)) {
2493: return program.getTypePrimitiveDouble();
2494: } else if ("java.lang.Float".equals(wrapperTypeName)) {
2495: return program.getTypePrimitiveFloat();
2496: } else {
2497: return null;
2498: }
2499: }
2500:
2501: private SourceInfo makeSourceInfo(Statement x) {
2502: int startLine = ProblemHandler.searchLineNumber(
2503: currentSeparatorPositions, x.sourceStart);
2504: return new SourceInfo(x.sourceStart, x.sourceEnd,
2505: startLine, currentFileName);
2506: }
2507:
2508: /**
2509: * Sometimes a variable reference can be to a local or parameter in an an
2510: * enclosing method. This is a tricky situation to detect. There's no
2511: * obvious way to tell, but the clue we can get from JDT is that the local's
2512: * containing method won't be the same as the method we're currently
2513: * processing.
2514: *
2515: * Once we have this clue, we can use getEmulationPath to compute the
2516: * current class's binding for that field.
2517: */
2518: private JVariable possiblyReferenceOuterLocal(
2519: JVariable variable, Binding binding) {
2520:
2521: if (variable instanceof JLocal
2522: || variable instanceof JParameter) {
2523: LocalVariableBinding localBinding = (LocalVariableBinding) binding;
2524: if (localBinding.declaringScope.methodScope() != currentMethodScope) {
2525: variable = null;
2526: VariableBinding[] vars = currentMethodScope
2527: .getEmulationPath(localBinding);
2528: if (vars == null) {
2529: return null;
2530: }
2531: assert (vars.length == 1);
2532: VariableBinding varBinding = vars[0];
2533:
2534: // See if there's an available parameter
2535: if (varBinding instanceof SyntheticArgumentBinding) {
2536: JType type = (JType) typeMap
2537: .get(varBinding.type);
2538: String name = String.valueOf(varBinding.name);
2539: for (int i = 0; i < currentMethod.params.size(); ++i) {
2540: JParameter param = currentMethod.params
2541: .get(i);
2542: if (type == param.getType()
2543: && name.equals(param.getName())) {
2544: variable = param;
2545: break;
2546: }
2547: }
2548: }
2549:
2550: // just use the field
2551: if (variable == null) {
2552: variable = (JField) typeMap.get(varBinding);
2553: }
2554:
2555: // now we have an updated variable that we can create our ref from
2556: }
2557: }
2558: return variable;
2559: }
2560:
2561: /**
2562: * Helper for creating all JBinaryOperation. Several different JDT nodes can
2563: * result in binary operations: AND_AND_Expression, Assignment,
2564: * BinaryExpresion, CompoundAssignment, EqualExpression, and
2565: * OR_OR_Expression. Hopefully the specific operators that can result in
2566: * each different JDT type won't change between releases, because we only
2567: * look for the specific operators that we think should match each JDT node,
2568: * and throw an error if there's a mismatch.
2569: */
2570: private JExpression processBinaryOperation(SourceInfo info,
2571: JBinaryOperator op, JType type, Expression arg1,
2572: Expression arg2) {
2573: JExpression exprArg1 = dispProcessExpression(arg1);
2574: JExpression exprArg2 = dispProcessExpression(arg2);
2575: JBinaryOperation binaryOperation = new JBinaryOperation(
2576: program, info, type, op, exprArg1, exprArg2);
2577: return binaryOperation;
2578: }
2579:
2580: private JExpression processQualifiedThisOrSuperRef(
2581: QualifiedThisReference x, JClassType qualType) {
2582: /*
2583: * WEIRD: If a thisref or superref is qualified with the EXACT type of the
2584: * innermost type (in other words, a needless qualifier), it must refer to
2585: * that innermost type, because a class can never be nested inside of
2586: * itself. In this case, we must treat it as if it were not qualified.
2587: *
2588: * In all other cases, the qualified thisref or superref cannot possibly
2589: * refer to the innermost type (even if the innermost type could be cast
2590: * to a compatible type), so we must create a reference to some outer
2591: * type.
2592: */
2593: SourceInfo info = makeSourceInfo(x);
2594: if (qualType == currentClass) {
2595: return createThisRef(info, qualType);
2596: } else {
2597: return createQualifiedThisRef(info, qualType);
2598: }
2599: }
2600:
2601: private InternalCompilerException translateException(
2602: Object node, Throwable e) {
2603: InternalCompilerException ice;
2604: if (e instanceof InternalCompilerException) {
2605: ice = (InternalCompilerException) e;
2606: } else {
2607: ice = new InternalCompilerException(
2608: "Error constructing Java AST", e);
2609: }
2610: String className = node.getClass().getName();
2611: String description = node.toString();
2612: SourceInfo sourceInfo = null;
2613: if (node instanceof Statement) {
2614: sourceInfo = makeSourceInfo((Statement) node);
2615: }
2616: ice.addNode(className, description, sourceInfo);
2617: return ice;
2618: }
2619:
2620: /**
2621: * For a given method, try to find all methods that it overrides/implements.
2622: * An experimental version that doesn't use JDT. Right now it's only used to
2623: * resolve upRefs for Object.getClass(), which is synthetic on everything
2624: * other than object.
2625: */
2626: private void tryFindUpRefs(JMethod method) {
2627: if (method.getEnclosingType() != null) {
2628: tryFindUpRefsRecursive(method, method
2629: .getEnclosingType());
2630: }
2631: }
2632:
2633: /**
2634: * For a given method(and method binding), try to find all methods that it
2635: * overrides/implements.
2636: */
2637: private void tryFindUpRefs(JMethod method, MethodBinding binding) {
2638: tryFindUpRefsRecursive(method, binding,
2639: binding.declaringClass);
2640: }
2641:
2642: /**
2643: * For a given method(and method binding), recursively try to find all
2644: * methods that it overrides/implements.
2645: */
2646: private void tryFindUpRefsRecursive(JMethod method,
2647: JReferenceType searchThisType) {
2648:
2649: // See if this class has any uprefs, unless this class is myself
2650: if (method.getEnclosingType() != searchThisType) {
2651: outer: for (JMethod upRef : searchThisType.methods) {
2652: if (upRef.isStatic()) {
2653: continue;
2654: }
2655: if (!upRef.getName().equals(method.getName())) {
2656: continue;
2657: }
2658: if (upRef.params.size() != method.params.size()) {
2659: continue;
2660: }
2661: for (int i = 0, c = upRef.params.size(); i < c; ++i) {
2662: if (upRef.params.get(i).getType() != method.params
2663: .get(i).getType()) {
2664: continue outer;
2665: }
2666: }
2667:
2668: if (!method.overrides.contains(upRef)) {
2669: method.overrides.add(upRef);
2670: break;
2671: }
2672: }
2673: }
2674:
2675: // recurse super class
2676: if (searchThisType.extnds != null) {
2677: tryFindUpRefsRecursive(method, searchThisType.extnds);
2678: }
2679:
2680: // recurse super interfaces
2681: for (JInterfaceType intf : searchThisType.implments) {
2682: tryFindUpRefsRecursive(method, intf);
2683: }
2684: }
2685:
2686: /**
2687: * For a given method(and method binding), recursively try to find all
2688: * methods that it overrides/implements.
2689: */
2690: private void tryFindUpRefsRecursive(JMethod method,
2691: MethodBinding binding, ReferenceBinding searchThisType) {
2692:
2693: // See if this class has any uprefs, unless this class is myself
2694: if (binding.declaringClass != searchThisType) {
2695: for (MethodBinding tryMethod : searchThisType
2696: .getMethods(binding.selector)) {
2697: if (binding.areParameterErasuresEqual(tryMethod)) {
2698: JMethod upRef = (JMethod) typeMap
2699: .get(tryMethod);
2700: if (!method.overrides.contains(upRef)) {
2701: method.overrides.add(upRef);
2702: break;
2703: }
2704: }
2705: }
2706: }
2707:
2708: // recurse super class
2709: if (searchThisType.super class() != null) {
2710: tryFindUpRefsRecursive(method, binding, searchThisType
2711: .super class());
2712: }
2713:
2714: // recurse super interfaces
2715: if (searchThisType.super Interfaces() != null) {
2716: for (int i = 0; i < searchThisType.super Interfaces().length; i++) {
2717: ReferenceBinding intf = searchThisType
2718: .super Interfaces()[i];
2719: tryFindUpRefsRecursive(method, binding, intf);
2720: }
2721: }
2722: }
2723:
2724: private JExpression unbox(JExpression toUnbox,
2725: JClassType wrapperType) {
2726: JPrimitiveType primitiveType = getPrimitiveTypeForWrapperType(wrapperType);
2727: if (primitiveType == null) {
2728: throw new InternalCompilerException(toUnbox,
2729: "Attempt to unbox unexpected type '"
2730: + wrapperType.getName() + "'", null);
2731: }
2732:
2733: String valueMethodName = primitiveType.getName() + "Value";
2734: JMethod valueMethod = null;
2735: for (Object element : wrapperType.methods) {
2736: JMethod method = (JMethod) element;
2737: if (method.getName().equals(valueMethodName)) {
2738: if (method.params.isEmpty()) {
2739: // It's a match!
2740: valueMethod = method;
2741: break;
2742: }
2743: }
2744: }
2745:
2746: if (valueMethod == null) {
2747: throw new InternalCompilerException(toUnbox,
2748: "Expected to find a method on '"
2749: + wrapperType.getName()
2750: + "' whose signature matches 'public "
2751: + primitiveType.getName() + " "
2752: + valueMethodName + "()'", null);
2753: }
2754:
2755: JMethodCall unboxCall = new JMethodCall(program, toUnbox
2756: .getSourceInfo(), toUnbox, valueMethod);
2757: return unboxCall;
2758: }
2759:
2760: private JExpression unbox(JExpression toUnbox,
2761: JPrimitiveType primitiveType) {
2762: String wrapperTypeName = primitiveType.getWrapperTypeName();
2763: JClassType wrapperType = (JClassType) program
2764: .getFromTypeMap(wrapperTypeName);
2765: if (wrapperType == null) {
2766: throw new InternalCompilerException(toUnbox,
2767: "Cannot find wrapper type '" + wrapperTypeName
2768: + "' associated with primitive type '"
2769: + primitiveType.getName() + "'", null);
2770: }
2771: return unbox(toUnbox, wrapperType);
2772: }
2773:
2774: private void writeEnumValueOfMethod(JEnumType type,
2775: JField mapField) {
2776: // return Enum.valueOf(map, name);
2777: JFieldRef mapRef = new JFieldRef(program, null, null,
2778: mapField, type);
2779: JVariableRef nameRef = createVariableRef(null,
2780: currentMethod.params.get(0));
2781: JMethod delegateTo = program
2782: .getIndexedMethod("Enum.valueOf");
2783: JMethodCall call = new JMethodCall(program, null, null,
2784: delegateTo);
2785: call.getArgs().add(mapRef);
2786: call.getArgs().add(nameRef);
2787: currentMethodBody.getStatements().add(
2788: new JReturnStatement(program, null, call));
2789: }
2790:
2791: private void writeEnumValuesMethod(JEnumType type) {
2792: // return new E[]{A,B,C};
2793: JNewArray newExpr = new JNewArray(program, null, program
2794: .getTypeArray(type, 1));
2795: newExpr.initializers = new ArrayList<JExpression>();
2796: for (JEnumField field : type.enumList) {
2797: JFieldRef fieldRef = new JFieldRef(program, null, null,
2798: field, type);
2799: newExpr.initializers.add(fieldRef);
2800: }
2801: currentMethodBody.getStatements().add(
2802: new JReturnStatement(program, null, newExpr));
2803: }
2804: }
2805:
2806: /**
2807: * Combines the information from the JDT type nodes and the type map to create
2808: * a JProgram structure.
2809: */
2810: public static void exec(TypeDeclaration[] types, TypeMap typeMap,
2811: JProgram jprogram, JsProgram jsProgram) {
2812: JavaASTGenerationVisitor v = new JavaASTGenerationVisitor(
2813: typeMap, jprogram, jsProgram);
2814: for (int i = 0; i < types.length; ++i) {
2815: v.processType(types[i]);
2816: }
2817: Collections
2818: .sort(jprogram.getDeclaredTypes(), new HasNameSort());
2819: }
2820:
2821: public static void reportJsniError(SourceInfo info,
2822: AbstractMethodDeclaration methodDeclaration, String message) {
2823: CompilationResult compResult = methodDeclaration
2824: .compilationResult();
2825: DefaultProblem problem = new DefaultProblem(info.getFileName()
2826: .toCharArray(), message, IProblem.Unclassified, null,
2827: ProblemSeverities.Error, info.getStartPos(), info
2828: .getEndPos(), info.getStartLine());
2829: compResult.record(problem, methodDeclaration);
2830: }
2831:
2832: public static SourceInfo translateInfo(JsSourceInfo info) {
2833: // TODO implement this
2834: return null;
2835: }
2836:
2837: /**
2838: * Gets a JParameter iterator for a constructor method over its synthetic
2839: * parameters.
2840: */
2841: private static Iterator<JParameter> getSyntheticsIterator(
2842: JMethod method) {
2843: Iterator<JParameter> it = method.params.iterator();
2844: for (int i = 0, c = method.getOriginalParamTypes().size(); i < c; ++i) {
2845: it.next();
2846: }
2847: return it;
2848: }
2849:
2850: /**
2851: * Returns <code>true</code> if JDT optimized the condition to
2852: * <code>false</code>.
2853: */
2854: private static boolean isOptimizedFalse(Expression condition) {
2855: if (condition != null) {
2856: Constant cst = condition.optimizedBooleanConstant();
2857: if (cst != Constant.NotAConstant) {
2858: if (cst.booleanValue() == false) {
2859: return true;
2860: }
2861: }
2862: }
2863: return false;
2864: }
2865:
2866: /**
2867: * Returns <code>true</code> if JDT optimized the condition to
2868: * <code>true</code>.
2869: */
2870: private static boolean isOptimizedTrue(Expression condition) {
2871: if (condition != null) {
2872: Constant cst = condition.optimizedBooleanConstant();
2873: if (cst != Constant.NotAConstant) {
2874: if (cst.booleanValue() == true) {
2875: return true;
2876: }
2877: }
2878: }
2879: return false;
2880: }
2881:
2882: }
|