blob: 8fad08b23374d44acff2e3d512f1a5a995c05039 [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.dev.javac.JSORestrictionsChecker;
import com.google.gwt.dev.javac.JsniMethod;
import com.google.gwt.dev.jdt.SafeASTVisitor;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.SourceOrigin;
import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
import com.google.gwt.dev.jjs.ast.JArrayLength;
import com.google.gwt.dev.jjs.ast.JArrayRef;
import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JAssertStatement;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
import com.google.gwt.dev.jjs.ast.JBlock;
import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
import com.google.gwt.dev.jjs.ast.JBreakStatement;
import com.google.gwt.dev.jjs.ast.JCaseStatement;
import com.google.gwt.dev.jjs.ast.JCastOperation;
import com.google.gwt.dev.jjs.ast.JCharLiteral;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JConditional;
import com.google.gwt.dev.jjs.ast.JConstructor;
import com.google.gwt.dev.jjs.ast.JContinueStatement;
import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JDoStatement;
import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
import com.google.gwt.dev.jjs.ast.JEnumField;
import com.google.gwt.dev.jjs.ast.JEnumType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JExpressionStatement;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JField.Disposition;
import com.google.gwt.dev.jjs.ast.JFieldRef;
import com.google.gwt.dev.jjs.ast.JFloatLiteral;
import com.google.gwt.dev.jjs.ast.JForStatement;
import com.google.gwt.dev.jjs.ast.JIfStatement;
import com.google.gwt.dev.jjs.ast.JInstanceOf;
import com.google.gwt.dev.jjs.ast.JIntLiteral;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLabel;
import com.google.gwt.dev.jjs.ast.JLabeledStatement;
import com.google.gwt.dev.jjs.ast.JLiteral;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JLocalRef;
import com.google.gwt.dev.jjs.ast.JLongLiteral;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JNewArray;
import com.google.gwt.dev.jjs.ast.JNewInstance;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JNullLiteral;
import com.google.gwt.dev.jjs.ast.JNullType;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JStatement;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JSwitchStatement;
import com.google.gwt.dev.jjs.ast.JThisRef;
import com.google.gwt.dev.jjs.ast.JThrowStatement;
import com.google.gwt.dev.jjs.ast.JTryStatement;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JUnaryOperator;
import com.google.gwt.dev.jjs.ast.JVariable;
import com.google.gwt.dev.jjs.ast.JWhileStatement;
import com.google.gwt.dev.jjs.ast.js.JsniClassLiteral;
import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
import com.google.gwt.dev.js.JsAbstractSymbolResolver;
import com.google.gwt.dev.js.ast.JsContext;
import com.google.gwt.dev.js.ast.JsExpression;
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsModVisitor;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsNameRef;
import com.google.gwt.dev.js.ast.JsNode;
import com.google.gwt.dev.js.ast.JsParameter;
import com.google.gwt.dev.util.StringInterner;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayReference;
import org.eclipse.jdt.internal.compiler.ast.AssertStatement;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.BreakStatement;
import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.CharLiteral;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.Clinit;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.CompoundAssignment;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ContinueStatement;
import org.eclipse.jdt.internal.compiler.ast.DoStatement;
import org.eclipse.jdt.internal.compiler.ast.DoubleLiteral;
import org.eclipse.jdt.internal.compiler.ast.EmptyStatement;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ExtendedStringLiteral;
import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.FloatLiteral;
import org.eclipse.jdt.internal.compiler.ast.ForStatement;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression;
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
import org.eclipse.jdt.internal.compiler.ast.LabeledStatement;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LongLiteral;
import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.NameReference;
import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.eclipse.jdt.internal.compiler.ast.OR_OR_Expression;
import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
import org.eclipse.jdt.internal.compiler.ast.PostfixExpression;
import org.eclipse.jdt.internal.compiler.ast.PrefixExpression;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedSuperReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedThisReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
import org.eclipse.jdt.internal.compiler.ast.StringLiteralConcatenation;
import org.eclipse.jdt.internal.compiler.ast.SuperReference;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
import org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
import org.eclipse.jdt.internal.compiler.ast.TrueLiteral;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
import org.eclipse.jdt.internal.compiler.ast.WhileStatement;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.NestedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.util.Util;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* Constructs a GWT Java AST from a single isolated compilation unit. The AST is
* not associated with any {@link com.google.gwt.dev.jjs.ast.JProgram} and will
* contain unresolved references.
*/
public class GwtAstBuilder {
/**
* Visit the JDT AST and produce our own AST. By the end of this pass, the
* produced AST should contain every piece of information we'll ever need
* about the code. The JDT nodes should never again be referenced after this.
*
* NOTE ON JDT FORCED OPTIMIZATIONS - If JDT statically determines that a
* section of code in unreachable, it won't fully resolve that section of
* code. This invalid-state code causes us major problems. As a result, we
* have to optimize out those dead blocks early and never try to translate
* them to our AST.
*/
class AstVisitor extends SafeASTVisitor {
/**
* Resolves local references to function parameters, and JSNI references.
*/
private class JsniResolver extends JsModVisitor {
private final GenerateJavaScriptLiterals generator = new GenerateJavaScriptLiterals();
private final JsniMethodBody nativeMethodBody;
private JsniResolver(JsniMethodBody nativeMethodBody) {
this.nativeMethodBody = nativeMethodBody;
}
@Override
public void endVisit(JsNameRef x, JsContext ctx) {
String ident = x.getIdent();
if (ident.charAt(0) == '@') {
Binding binding = jsniRefs.get(ident);
SourceInfo info = x.getSourceInfo();
if (binding == null) {
assert ident.startsWith("@null::");
if ("@null::nullMethod()".equals(ident)) {
processMethod(x, info, JMethod.NULL_METHOD);
} else {
assert "@null::nullField".equals(ident);
processField(x, info, JField.NULL_FIELD, ctx);
}
} else if (binding instanceof TypeBinding) {
JType type = typeMap.get((TypeBinding) binding);
processClassLiteral(x, info, type, ctx);
} else if (binding instanceof FieldBinding) {
FieldBinding fieldBinding = (FieldBinding) binding;
/*
* We must replace any compile-time constants with the constant
* value of the field.
*/
if (isCompileTimeConstant(fieldBinding)) {
assert !ctx.isLvalue();
JExpression constant = getConstant(info, fieldBinding.constant());
generator.accept(constant);
JsExpression result = generator.pop();
assert (result != null);
ctx.replaceMe(result);
} else {
// Normal: create a jsniRef.
JField field = typeMap.get(fieldBinding);
processField(x, info, field, ctx);
}
} else {
JMethod method = typeMap.get((MethodBinding) binding);
processMethod(x, info, method);
}
}
}
private void processClassLiteral(JsNameRef nameRef, SourceInfo info, JType type, JsContext ctx) {
assert !ctx.isLvalue();
JsniClassLiteral classLiteral = new JsniClassLiteral(info, nameRef.getIdent(), type);
nativeMethodBody.addClassRef(classLiteral);
}
private void processField(JsNameRef nameRef, SourceInfo info, JField field, JsContext ctx) {
JsniFieldRef fieldRef =
new JsniFieldRef(info, nameRef.getIdent(), field, curClass.type, ctx.isLvalue());
nativeMethodBody.addJsniRef(fieldRef);
}
private void processMethod(JsNameRef nameRef, SourceInfo info, JMethod method) {
JsniMethodRef methodRef =
new JsniMethodRef(info, nameRef.getIdent(), method, javaLangObject);
nativeMethodBody.addJsniRef(methodRef);
}
}
/**
* Resolves the scope of JS identifiers solely within the scope of a method.
*/
private class JsParameterResolver extends JsAbstractSymbolResolver {
private final JsFunction jsFunction;
public JsParameterResolver(JsFunction jsFunction) {
this.jsFunction = jsFunction;
}
@Override
public void resolve(JsNameRef x) {
// Only resolve unqualified names
if (x.getQualifier() == null) {
JsName name = getScope().findExistingName(x.getIdent());
// Ensure that we're resolving a name from the function's parameters
JsNode node = name == null ? null : name.getStaticRef();
if (node instanceof JsParameter) {
JsParameter param = (JsParameter) node;
if (jsFunction.getParameters().contains(param)) {
x.resolve(name);
}
}
}
}
}
private final Stack<ClassInfo> classStack = new Stack<ClassInfo>();
private ClassInfo curClass = null;
private MethodInfo curMethod = null;
private final Stack<MethodInfo> methodStack = new Stack<MethodInfo>();
private final ArrayList<JNode> nodeStack = new ArrayList<JNode>();
@Override
public void endVisit(AllocationExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
List<JExpression> arguments = popCallArgs(info, x.arguments, x.binding);
pushNewExpression(info, x, null, arguments, scope);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(AND_AND_Expression x, BlockScope scope) {
pushBinaryOp(x, JBinaryOperator.AND);
}
@Override
public void endVisit(AnnotationMethodDeclaration x, ClassScope classScope) {
endVisit((MethodDeclaration) x, classScope);
}
@Override
public void endVisit(ArrayAllocationExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
if (x.initializer != null) {
// handled by ArrayInitializer.
} else {
// Annoyingly, JDT only visits non-null dims, so we can't popList().
List<JExpression> dims = new ArrayList<JExpression>();
for (int i = x.dimensions.length - 1; i >= 0; --i) {
JExpression dimension = pop(x.dimensions[i]);
// can be null if index expression was empty
if (dimension == null) {
dimension = JAbsentArrayDimension.INSTANCE;
}
dims.add(dimension);
}
// Undo the stack reversal.
Collections.reverse(dims);
push(JNewArray.createDims(info, type, dims));
}
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ArrayInitializer x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JArrayType type = (JArrayType) typeMap.get(x.resolvedType);
List<JExpression> expressions = pop(x.expressions);
push(JNewArray.createInitializers(info, type, expressions));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ArrayReference x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JExpression position = pop(x.position);
JExpression receiver = pop(x.receiver);
push(new JArrayRef(info, receiver, position));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(AssertStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JExpression exceptionArgument = pop(x.exceptionArgument);
JExpression assertExpression = pop(x.assertExpression);
push(new JAssertStatement(info, assertExpression, exceptionArgument));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(Assignment x, BlockScope scope) {
pushBinaryOp(x, JBinaryOperator.ASG);
}
@Override
public void endVisit(BinaryExpression x, BlockScope scope) {
JBinaryOperator op;
int binOp = (x.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
switch (binOp) {
case OperatorIds.LEFT_SHIFT:
op = JBinaryOperator.SHL;
break;
case OperatorIds.RIGHT_SHIFT:
op = JBinaryOperator.SHR;
break;
case OperatorIds.UNSIGNED_RIGHT_SHIFT:
op = JBinaryOperator.SHRU;
break;
case OperatorIds.PLUS:
if (javaLangString == typeMap.get(x.resolvedType)) {
op = JBinaryOperator.CONCAT;
} else {
op = JBinaryOperator.ADD;
}
break;
case OperatorIds.MINUS:
op = JBinaryOperator.SUB;
break;
case OperatorIds.REMAINDER:
op = JBinaryOperator.MOD;
break;
case OperatorIds.XOR:
op = JBinaryOperator.BIT_XOR;
break;
case OperatorIds.AND:
op = JBinaryOperator.BIT_AND;
break;
case OperatorIds.MULTIPLY:
op = JBinaryOperator.MUL;
break;
case OperatorIds.OR:
op = JBinaryOperator.BIT_OR;
break;
case OperatorIds.DIVIDE:
op = JBinaryOperator.DIV;
break;
case OperatorIds.LESS_EQUAL:
op = JBinaryOperator.LTE;
break;
case OperatorIds.GREATER_EQUAL:
op = JBinaryOperator.GTE;
break;
case OperatorIds.GREATER:
op = JBinaryOperator.GT;
break;
case OperatorIds.LESS:
op = JBinaryOperator.LT;
break;
default:
throw translateException(x, new InternalCompilerException(
"Unexpected operator for BinaryExpression"));
}
pushBinaryOp(x, op);
}
@Override
public void endVisit(Block x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JBlock block = popBlock(info, x.statements);
push(block);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(BreakStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
push(new JBreakStatement(info, getOrCreateLabel(info, x.label)));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(CaseStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JExpression constantExpression = pop(x.constantExpression);
JLiteral caseLiteral;
if (constantExpression == null) {
caseLiteral = null;
} else if (constantExpression instanceof JLiteral) {
caseLiteral = (JLiteral) constantExpression;
} else {
// Adapted from CaseStatement.resolveCase().
assert x.constantExpression.resolvedType.isEnum();
NameReference reference = (NameReference) x.constantExpression;
FieldBinding field = reference.fieldBinding();
caseLiteral = JIntLiteral.get(field.original().id);
}
push(new JCaseStatement(info, caseLiteral));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(CastExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JType type = typeMap.get(x.resolvedType);
JExpression expression = pop(x.expression);
if (x.type instanceof NameReference) {
pop(x.type);
}
push(new JCastOperation(info, type, expression));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(CharLiteral x, BlockScope scope) {
try {
push(JCharLiteral.get(x.constant.charValue()));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ClassLiteralAccess x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JType type = typeMap.get(x.targetType);
push(new JClassLiteral(info, type));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(CompoundAssignment x, BlockScope scope) {
JBinaryOperator op;
switch (x.operator) {
case OperatorIds.PLUS:
if (javaLangString == typeMap.get(x.resolvedType)) {
op = JBinaryOperator.ASG_CONCAT;
} else {
op = JBinaryOperator.ASG_ADD;
}
break;
case OperatorIds.MINUS:
op = JBinaryOperator.ASG_SUB;
break;
case OperatorIds.MULTIPLY:
op = JBinaryOperator.ASG_MUL;
break;
case OperatorIds.DIVIDE:
op = JBinaryOperator.ASG_DIV;
break;
case OperatorIds.AND:
op = JBinaryOperator.ASG_BIT_AND;
break;
case OperatorIds.OR:
op = JBinaryOperator.ASG_BIT_OR;
break;
case OperatorIds.XOR:
op = JBinaryOperator.ASG_BIT_XOR;
break;
case OperatorIds.REMAINDER:
op = JBinaryOperator.ASG_MOD;
break;
case OperatorIds.LEFT_SHIFT:
op = JBinaryOperator.ASG_SHL;
break;
case OperatorIds.RIGHT_SHIFT:
op = JBinaryOperator.ASG_SHR;
break;
case OperatorIds.UNSIGNED_RIGHT_SHIFT:
op = JBinaryOperator.ASG_SHRU;
break;
default:
throw translateException(x, new InternalCompilerException(
"Unexpected operator for CompoundAssignment"));
}
pushBinaryOp(x, op);
}
@Override
public void endVisit(ConditionalExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JType type = typeMap.get(x.resolvedType);
JExpression valueIfFalse = pop(x.valueIfFalse);
JExpression valueIfTrue = pop(x.valueIfTrue);
JExpression condition = pop(x.condition);
push(new JConditional(info, type, condition, valueIfTrue, valueIfFalse));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ConstructorDeclaration x, ClassScope scope) {
try {
List<JStatement> statements = pop(x.statements);
JStatement constructorCall = pop(x.constructorCall);
JBlock block = curMethod.body.getBlock();
SourceInfo info = curMethod.method.getSourceInfo();
/*
* Determine if we have an explicit this call. The presence of an
* explicit this call indicates we can skip certain initialization steps
* (as the callee will perform those steps for us). These skippable
* steps are 1) assigning synthetic args to fields and 2) running
* initializers.
*/
boolean hasExplicitThis = (x.constructorCall != null) && !x.constructorCall.isSuperAccess();
/*
* All synthetic fields must be assigned, unless we have an explicit
* this constructor call, in which case the callee will assign them for
* us.
*/
if (!hasExplicitThis) {
ReferenceBinding declaringClass = (ReferenceBinding) x.binding.declaringClass.erasure();
if (isNested(declaringClass)) {
NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
if (nestedBinding.enclosingInstances != null) {
for (SyntheticArgumentBinding arg : nestedBinding.enclosingInstances) {
JBinaryOperation asg = assignSyntheticField(info, arg);
block.addStmt(asg.makeStatement());
}
}
if (nestedBinding.outerLocalVariables != null) {
for (SyntheticArgumentBinding arg : nestedBinding.outerLocalVariables) {
JBinaryOperation asg = assignSyntheticField(info, arg);
block.addStmt(asg.makeStatement());
}
}
}
}
if (constructorCall != null) {
block.addStmt(constructorCall);
}
/*
* Call the synthetic instance initializer method, unless we have an
* explicit this constructor call, in which case the callee will.
*/
if (!hasExplicitThis) {
// $init is always in position 1 (clinit is in 0)
JMethod initMethod = curClass.type.getMethods().get(1);
JMethodCall initCall = new JMethodCall(info, makeThisRef(info), initMethod);
block.addStmt(initCall.makeStatement());
}
// user code (finally!)
block.addStmts(statements);
popMethodInfo();
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ContinueStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
push(new JContinueStatement(info, getOrCreateLabel(info, x.label)));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(DoStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JExpression condition = pop(x.condition);
JStatement action = pop(x.action);
push(new JDoStatement(info, condition, action));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(DoubleLiteral x, BlockScope scope) {
try {
push(JDoubleLiteral.get(x.constant.doubleValue()));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(EmptyStatement x, BlockScope scope) {
push(null);
}
@Override
public void endVisit(EqualExpression x, BlockScope scope) {
JBinaryOperator op;
switch ((x.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) {
case OperatorIds.EQUAL_EQUAL:
op = JBinaryOperator.EQ;
break;
case OperatorIds.NOT_EQUAL:
op = JBinaryOperator.NEQ;
break;
default:
throw translateException(x, new InternalCompilerException(
"Unexpected operator for EqualExpression"));
}
pushBinaryOp(x, op);
}
@Override
public void endVisit(ExplicitConstructorCall x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JConstructor ctor = (JConstructor) typeMap.get(x.binding);
JExpression trueQualifier = makeThisRef(info);
JMethodCall call = new JMethodCall(info, trueQualifier, ctor);
List<JExpression> callArgs = popCallArgs(info, x.arguments, x.binding);
if (curClass.classType.isEnumOrSubclass() != null) {
// Enums: wire up synthetic name/ordinal params to the super method.
JParameterRef enumNameRef = new JParameterRef(info, curMethod.method.getParams().get(0));
call.addArg(enumNameRef);
JParameterRef enumOrdinalRef =
new JParameterRef(info, curMethod.method.getParams().get(1));
call.addArg(enumOrdinalRef);
}
if (x.isSuperAccess()) {
JExpression qualifier = pop(x.qualification);
ReferenceBinding superClass = x.binding.declaringClass;
boolean nestedSuper = isNested(superClass);
if (nestedSuper) {
processSuperCallThisArgs(superClass, call, qualifier, x.qualification);
}
call.addArgs(callArgs);
if (nestedSuper) {
processSuperCallLocalArgs(superClass, call);
}
} else {
assert (x.qualification == null);
ReferenceBinding declaringClass = x.binding.declaringClass;
boolean nested = isNested(declaringClass);
if (nested) {
processThisCallThisArgs(declaringClass, call);
}
call.addArgs(callArgs);
if (nested) {
processThisCallLocalArgs(declaringClass, call);
}
}
call.setStaticDispatchOnly();
push(call.makeStatement());
} catch (Throwable e) {
throw translateException(x, e);
} finally {
scope.methodScope().isConstructorCall = false;
}
}
@Override
public void endVisit(ExtendedStringLiteral x, BlockScope scope) {
endVisit((StringLiteral) x, scope);
}
@Override
public void endVisit(FalseLiteral x, BlockScope scope) {
push(JBooleanLiteral.FALSE);
}
@Override
public void endVisit(FieldDeclaration x, MethodScope scope) {
try {
JExpression initialization = pop(x.initialization);
JField field = typeMap.get(x.binding);
if (field instanceof JEnumField) {
// An enum field must be initialized!
assert (initialization instanceof JNewInstance);
}
if (initialization != null) {
SourceInfo info = makeSourceInfo(x);
JExpression instance = null;
if (!x.isStatic()) {
instance = makeThisRef(info);
}
// JDeclarationStatement's ctor sets up the field's initializer.
JStatement decl =
new JDeclarationStatement(info, new JFieldRef(info, instance, field, curClass.type),
initialization);
// will either be init or clinit
curMethod.body.getBlock().addStmt(decl);
}
popMethodInfo();
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(FieldReference x, BlockScope scope) {
try {
FieldBinding fieldBinding = x.binding;
SourceInfo info = makeSourceInfo(x);
JExpression instance = pop(x.receiver);
JExpression expr;
if (fieldBinding.declaringClass == null) {
if (!ARRAY_LENGTH_FIELD.equals(String.valueOf(fieldBinding.name))) {
throw new InternalCompilerException("Expected [array].length.");
}
expr = new JArrayLength(info, instance);
} else {
JField field = typeMap.get(fieldBinding);
expr = new JFieldRef(info, instance, field, curClass.type);
}
if (x.genericCast != null) {
JType castType = typeMap.get(x.genericCast);
/*
* Note, this may result in an invalid AST due to an LHS cast
* operation. We fix this up in FixAssignmentToUnbox.
*/
expr = maybeCast(castType, expr);
}
push(expr);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(FloatLiteral x, BlockScope scope) {
try {
push(JFloatLiteral.get(x.constant.floatValue()));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ForeachStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JBlock body = popBlock(info, x.action);
JExpression collection = pop(x.collection);
JDeclarationStatement elementDecl = pop(x.elementVariable);
assert (elementDecl.initializer == null);
JLocal elementVar = (JLocal) curMethod.locals.get(x.elementVariable.binding);
String elementVarName = elementVar.getName();
JForStatement result;
if (x.collectionVariable != null) {
/**
* <pre>
* for (final T[] i$array = collection,
* int i$index = 0,
* final int i$max = i$array.length;
* i$index < i$max; ++i$index) {
* T elementVar = i$array[i$index];
* // user action
* }
* </pre>
*/
JLocal arrayVar =
JProgram.createLocal(info, elementVarName + "$array", collection.getType(), true,
curMethod.body);
JLocal indexVar =
JProgram.createLocal(info, elementVarName + "$index", JPrimitiveType.INT, false,
curMethod.body);
JLocal maxVar =
JProgram.createLocal(info, elementVarName + "$max", JPrimitiveType.INT, true,
curMethod.body);
List<JStatement> initializers = new ArrayList<JStatement>(3);
// T[] i$array = arr
initializers.add(makeDeclaration(info, arrayVar, collection));
// int i$index = 0
initializers.add(makeDeclaration(info, indexVar, JIntLiteral.get(0)));
// int i$max = i$array.length
initializers.add(makeDeclaration(info, maxVar, new JArrayLength(info, new JLocalRef(info,
arrayVar))));
// i$index < i$max
JExpression condition =
new JBinaryOperation(info, JPrimitiveType.BOOLEAN, JBinaryOperator.LT, new JLocalRef(
info, indexVar), new JLocalRef(info, maxVar));
// ++i$index
List<JExpressionStatement> increments = new ArrayList<JExpressionStatement>(1);
increments.add(new JPrefixOperation(info, JUnaryOperator.INC, new JLocalRef(info,
indexVar)).makeStatement());
// T elementVar = i$array[i$index];
elementDecl.initializer =
new JArrayRef(info, new JLocalRef(info, arrayVar), new JLocalRef(info, indexVar));
body.addStmt(0, elementDecl);
result = new JForStatement(info, initializers, condition, increments, body);
} else {
/**
* <pre>
* for (Iterator&lt;T&gt; i$iterator = collection.iterator(); i$iterator.hasNext();) {
* T elementVar = i$iterator.next();
* // user action
* }
* </pre>
*/
CompilationUnitScope cudScope = scope.compilationUnitScope();
ReferenceBinding javaUtilIterator = scope.getJavaUtilIterator();
ReferenceBinding javaLangIterable = scope.getJavaLangIterable();
MethodBinding iterator = javaLangIterable.getExactMethod(ITERATOR, NO_TYPES, cudScope);
MethodBinding hasNext = javaUtilIterator.getExactMethod(HAS_NEXT, NO_TYPES, cudScope);
MethodBinding next = javaUtilIterator.getExactMethod(NEXT, NO_TYPES, cudScope);
JLocal iteratorVar =
JProgram.createLocal(info, (elementVarName + "$iterator"), typeMap
.get(javaUtilIterator), false, curMethod.body);
List<JStatement> initializers = new ArrayList<JStatement>(1);
// Iterator<T> i$iterator = collection.iterator()
initializers.add(makeDeclaration(info, iteratorVar, new JMethodCall(info, collection,
typeMap.get(iterator))));
// i$iterator.hasNext()
JExpression condition =
new JMethodCall(info, new JLocalRef(info, iteratorVar), typeMap.get(hasNext));
// T elementVar = (T) i$iterator.next();
elementDecl.initializer =
new JMethodCall(info, new JLocalRef(info, iteratorVar), typeMap.get(next));
// Perform any implicit reference type casts (due to generics).
// Note this occurs before potential unboxing.
if (elementVar.getType() != javaLangObject) {
TypeBinding collectionElementType = (TypeBinding) collectionElementTypeField.get(x);
JType toType = typeMap.get(collectionElementType);
assert (toType instanceof JReferenceType);
elementDecl.initializer = maybeCast(toType, elementDecl.initializer);
}
body.addStmt(0, elementDecl);
result =
new JForStatement(info, initializers, condition, Collections
.<JExpressionStatement> emptyList(), body);
}
// May need to box or unbox the element assignment.
elementDecl.initializer =
maybeBoxOrUnbox(elementDecl.initializer, x.elementVariableImplicitWidening);
push(result);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ForStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JStatement action = pop(x.action);
List<JExpressionStatement> increments = pop(x.increments);
JExpression condition = pop(x.condition);
List<JStatement> initializations = pop(x.initializations);
push(new JForStatement(info, initializations, condition, increments, action));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(IfStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JStatement elseStatement = pop(x.elseStatement);
JStatement thenStatement = pop(x.thenStatement);
JExpression condition = pop(x.condition);
push(new JIfStatement(info, condition, thenStatement, elseStatement));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(Initializer x, MethodScope scope) {
try {
JBlock block = pop(x.block);
if (block != null) {
curMethod.body.getBlock().addStmt(block);
}
popMethodInfo();
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(InstanceOfExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JExpression expr = pop(x.expression);
JReferenceType testType = (JReferenceType) typeMap.get(x.type.resolvedType);
push(new JInstanceOf(info, testType, expr));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(IntLiteral x, BlockScope scope) {
try {
push(JIntLiteral.get(x.constant.intValue()));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(LabeledStatement x, BlockScope scope) {
try {
JStatement statement = pop(x.statement);
if (statement == null) {
push(null);
return;
}
SourceInfo info = makeSourceInfo(x);
push(new JLabeledStatement(info, getOrCreateLabel(info, x.label), statement));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(LocalDeclaration x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JLocal local = (JLocal) curMethod.locals.get(x.binding);
assert local != null;
JLocalRef localRef = new JLocalRef(info, local);
JExpression initialization = pop(x.initialization);
push(new JDeclarationStatement(info, localRef, initialization));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(LongLiteral x, BlockScope scope) {
try {
push(JLongLiteral.get(x.constant.longValue()));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(MessageSend x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JMethod method = typeMap.get(x.binding);
List<JExpression> arguments = popCallArgs(info, x.arguments, x.binding);
JExpression receiver = pop(x.receiver);
if (x.receiver instanceof ThisReference) {
if (method.isStatic()) {
// don't bother qualifying it, it's a no-op
receiver = null;
} else if ((x.bits & ASTNode.DepthMASK) != 0) {
// outer method can be reached through emulation if implicit access
ReferenceBinding targetType =
scope.enclosingSourceType().enclosingTypeAt(
(x.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT);
receiver = makeThisReference(info, targetType, true, scope);
}
}
JMethodCall call = new JMethodCall(info, receiver, method);
// On a super ref, don't allow polymorphic dispatch. Oddly enough,
// QualifiedSuperReference not derived from SuperReference!
boolean isSuperRef =
x.receiver instanceof SuperReference || x.receiver instanceof QualifiedSuperReference;
if (isSuperRef) {
call.setStaticDispatchOnly();
}
// The arguments come first...
call.addArgs(arguments);
if (x.valueCast != null) {
JType castType = typeMap.get(x.valueCast);
push(maybeCast(castType, call));
} else {
push(call);
}
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(MethodDeclaration x, ClassScope scope) {
try {
if (x.isNative()) {
processNativeMethod(x);
} else {
List<JStatement> statements = pop(x.statements);
curMethod.body.getBlock().addStmts(statements);
}
popMethodInfo();
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(NullLiteral x, BlockScope scope) {
push(JNullLiteral.INSTANCE);
}
@Override
public void endVisit(OR_OR_Expression x, BlockScope scope) {
pushBinaryOp(x, JBinaryOperator.OR);
}
@Override
public void endVisit(PostfixExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JUnaryOperator op;
switch (x.operator) {
case OperatorIds.MINUS:
op = JUnaryOperator.DEC;
break;
case OperatorIds.PLUS:
op = JUnaryOperator.INC;
break;
default:
throw new InternalCompilerException("Unexpected postfix operator");
}
JExpression lhs = pop(x.lhs);
push(new JPostfixOperation(info, op, lhs));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(PrefixExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JUnaryOperator op;
switch (x.operator) {
case OperatorIds.MINUS:
op = JUnaryOperator.DEC;
break;
case OperatorIds.PLUS:
op = JUnaryOperator.INC;
break;
default:
throw new InternalCompilerException("Unexpected prefix operator");
}
JExpression lhs = pop(x.lhs);
push(new JPrefixOperation(info, op, lhs));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(QualifiedAllocationExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
List<JExpression> arguments = popCallArgs(info, x.arguments, x.binding);
pushNewExpression(info, x, x.enclosingInstance(), arguments, scope);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(QualifiedNameReference x, BlockScope scope) {
try {
JExpression curRef = resolveNameReference(x, scope);
if (curRef == null) {
push(null);
return;
}
if (x.genericCast != null) {
JType castType = typeMap.get(x.genericCast);
curRef = maybeCast(castType, curRef);
}
SourceInfo info = curRef.getSourceInfo();
/*
* JDT represents multiple field access as an array of fields, each
* qualified by everything to the left. So each subsequent item in
* otherBindings takes the current expression as a qualifier.
*/
if (x.otherBindings != null) {
for (int i = 0; i < x.otherBindings.length; ++i) {
FieldBinding fieldBinding = x.otherBindings[i];
if (fieldBinding.declaringClass == null) {
// probably array.length
if (!ARRAY_LENGTH_FIELD.equals(String.valueOf(fieldBinding.name))) {
throw new InternalCompilerException("Expected [array].length.");
}
curRef = new JArrayLength(info, curRef);
} else {
JField field = typeMap.get(fieldBinding);
curRef = new JFieldRef(info, curRef, field, curClass.type);
}
if (x.otherGenericCasts != null && x.otherGenericCasts[i] != null) {
JType castType = typeMap.get(x.otherGenericCasts[i]);
curRef = maybeCast(castType, curRef);
}
}
}
push(curRef);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(QualifiedSuperReference x, BlockScope scope) {
try {
// Oddly enough, super refs can be modeled as this refs, because
// whatever expression they qualify has already been resolved.
endVisit((QualifiedThisReference) x, scope);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(QualifiedThisReference x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
ReferenceBinding targetType = (ReferenceBinding) x.qualification.resolvedType;
push(makeThisReference(info, targetType, true, scope));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ReturnStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JExpression expression = pop(x.expression);
push(new JReturnStatement(info, expression));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(SingleNameReference x, BlockScope scope) {
try {
JExpression result = resolveNameReference(x, scope);
if (result == null) {
push(null);
return;
}
if (x.genericCast != null) {
JType castType = typeMap.get(x.genericCast);
result = maybeCast(castType, result);
}
push(result);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(StringLiteral x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
push(getStringLiteral(info, x.constant.stringValue()));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(StringLiteralConcatenation x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
push(getStringLiteral(info, x.constant.stringValue()));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(SuperReference x, BlockScope scope) {
try {
assert (typeMap.get(x.resolvedType) == curClass.classType.getSuperClass());
// Super refs can be modeled as a this ref.
push(makeThisRef(makeSourceInfo(x)));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(SwitchStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JBlock block = popBlock(info, x.statements);
JExpression expression = pop(x.expression);
if (x.expression.resolvedType.isEnum()) {
// synthesize a call to ordinal().
ReferenceBinding javaLangEnum = scope.getJavaLangEnum();
MethodBinding ordinal = javaLangEnum.getMethods(ORDINAL)[0];
expression = new JMethodCall(info, expression, typeMap.get(ordinal));
}
push(new JSwitchStatement(info, expression, block));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(SynchronizedStatement x, BlockScope scope) {
try {
JBlock block = pop(x.block);
JExpression expression = pop(x.expression);
block.addStmt(0, expression.makeStatement());
push(block);
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ThisReference x, BlockScope scope) {
try {
assert (typeMap.get(x.resolvedType) == curClass.classType);
push(makeThisRef(makeSourceInfo(x)));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(ThrowStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JExpression exception = pop(x.exception);
push(new JThrowStatement(info, exception));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(TrueLiteral x, BlockScope scope) {
push(JBooleanLiteral.TRUE);
}
@Override
public void endVisit(TryStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JBlock finallyBlock = pop(x.finallyBlock);
List<JBlock> catchBlocks = pop(x.catchBlocks);
JBlock tryBlock = pop(x.tryBlock);
List<JLocalRef> catchArgs = new ArrayList<JLocalRef>();
if (x.catchBlocks != null) {
for (Argument argument : x.catchArguments) {
JLocal local = (JLocal) curMethod.locals.get(argument.binding);
catchArgs.add(new JLocalRef(info, local));
}
}
push(new JTryStatement(info, tryBlock, catchArgs, catchBlocks, finallyBlock));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(TypeDeclaration x, ClassScope scope) {
endVisit(x);
}
@Override
public void endVisit(TypeDeclaration x, CompilationUnitScope scope) {
endVisit(x);
}
@Override
public void endVisit(UnaryExpression x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JUnaryOperator op;
int operator = ((x.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT);
switch (operator) {
case OperatorIds.MINUS:
op = JUnaryOperator.NEG;
break;
case OperatorIds.NOT:
op = JUnaryOperator.NOT;
break;
case OperatorIds.PLUS:
// Odd case.. useless + operator; just leave the operand on the
// stack.
return;
case OperatorIds.TWIDDLE:
op = JUnaryOperator.BIT_NOT;
break;
default:
throw new InternalCompilerException("Unexpected operator for unary expression");
}
JExpression expression = pop(x.expression);
push(new JPrefixOperation(info, op, expression));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisit(WhileStatement x, BlockScope scope) {
try {
SourceInfo info = makeSourceInfo(x);
JStatement action = pop(x.action);
JExpression condition = pop(x.condition);
push(new JWhileStatement(info, condition, action));
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public void endVisitValid(TypeDeclaration x, BlockScope scope) {
endVisit(x);
if (!x.binding.isAnonymousType()) {
// Class declaration as a statement; insert a dummy statement.
push(null);
}
}
public boolean isJavaScriptObject(JClassType type) {
if (type == null) {
return false;
}
if (JProgram.JAVASCRIPTOBJECT.equals(type.getName())) {
return true;
}
return isJavaScriptObject(type.getSuperClass());
}
@Override
public boolean visit(AnnotationMethodDeclaration x, ClassScope classScope) {
return visit((MethodDeclaration) x, classScope);
}
@Override
public boolean visit(Argument x, BlockScope scope) {
// handled by parents
return true;
}
@Override
public boolean visit(Block x, BlockScope scope) {
x.statements = reduceToReachable(x.statements);
return true;
}
@Override
public boolean visit(ConstructorDeclaration x, ClassScope scope) {
try {
JConstructor method = (JConstructor) typeMap.get(x.binding);
assert !method.isExternal();
JMethodBody body = new JMethodBody(method.getSourceInfo());
method.setBody(body);
pushMethodInfo(new MethodInfo(method, body, x.scope));
// Map all arguments.
Iterator<JParameter> it = method.getParams().iterator();
// Enum arguments have no mapping.
if (curClass.classType.isEnumOrSubclass() != null) {
// Skip past name and ordinal.
it.next();
it.next();
}
// Map synthetic arguments for outer this.
ReferenceBinding declaringClass = (ReferenceBinding) x.binding.declaringClass.erasure();
boolean isNested = isNested(declaringClass);
if (isNested) {
NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
if (nestedBinding.enclosingInstances != null) {
for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
curMethod.locals.put(arg, it.next());
}
}
}
// Map user arguments.
if (x.arguments != null) {
for (Argument argument : x.arguments) {
curMethod.locals.put(argument.binding, it.next());
}
}
// Map synthetic arguments for locals.
if (isNested) {
// add synthetic args for locals
NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
// add synthetic args for outer this and locals
if (nestedBinding.outerLocalVariables != null) {
for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
curMethod.locals.put(arg, it.next());
}
}
}
x.statements = reduceToReachable(x.statements);
return true;
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public boolean visit(ExplicitConstructorCall explicitConstructor, BlockScope scope) {
scope.methodScope().isConstructorCall = true;
return true;
}
@Override
public boolean visit(FieldDeclaration x, MethodScope scope) {
try {
assert !typeMap.get(x.binding).isExternal();
pushInitializerMethodInfo(x, scope);
return true;
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public boolean visit(ForStatement x, BlockScope scope) {
// SEE NOTE ON JDT FORCED OPTIMIZATIONS
if (isOptimizedFalse(x.condition)) {
x.action = null;
}
return true;
}
@Override
public boolean visit(IfStatement x, BlockScope scope) {
// SEE NOTE ON JDT FORCED OPTIMIZATIONS
if (isOptimizedFalse(x.condition)) {
x.thenStatement = null;
} else if (isOptimizedTrue(x.condition)) {
x.elseStatement = null;
}
return true;
}
@Override
public boolean visit(Initializer x, MethodScope scope) {
try {
pushInitializerMethodInfo(x, scope);
return true;
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public boolean visit(LocalDeclaration x, BlockScope scope) {
try {
createLocal(x);
return true;
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public boolean visit(MarkerAnnotation annotation, BlockScope scope) {
return false;
}
@Override
public boolean visit(MethodDeclaration x, ClassScope scope) {
try {
JMethod method = typeMap.get(x.binding);
assert !method.isExternal();
JMethodBody body = null;
if (!method.isNative()) {
body = new JMethodBody(method.getSourceInfo());
method.setBody(body);
}
pushMethodInfo(new MethodInfo(method, body, x.scope));
// Map user arguments.
Iterator<JParameter> it = method.getParams().iterator();
if (x.arguments != null) {
for (Argument argument : x.arguments) {
curMethod.locals.put(argument.binding, it.next());
}
}
x.statements = reduceToReachable(x.statements);
return true;
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public boolean visit(NormalAnnotation annotation, BlockScope scope) {
return false;
}
@Override
public boolean visit(SingleMemberAnnotation annotation, BlockScope scope) {
return false;
}
@Override
public boolean visit(SwitchStatement x, BlockScope scope) {
x.statements = reduceToReachable(x.statements);
return true;
}
@Override
public boolean visit(TryStatement x, BlockScope scope) {
try {
if (x.catchBlocks != null) {
for (Argument argument : x.catchArguments) {
createLocal(argument);
}
}
return true;
} catch (Throwable e) {
throw translateException(x, e);
}
}
@Override
public boolean visit(TypeDeclaration x, ClassScope scope) {
return visit(x);
}
@Override
public boolean visit(TypeDeclaration x, CompilationUnitScope scope) {
return visit(x);
}
@Override
public boolean visit(WhileStatement x, BlockScope scope) {
// SEE NOTE ON JDT FORCED OPTIMIZATIONS
if (isOptimizedFalse(x.condition)) {
x.action = null;
}
return true;
}
@Override
public boolean visitValid(TypeDeclaration x, BlockScope scope) {
// Local types actually need to be created now.
createTypes(x);
resolveTypeRefs(x);
createMembers(x);
return visit(x);
}
protected void endVisit(TypeDeclaration x) {
JDeclaredType type = curClass.type;
/*
* Make clinits chain to super class (JDT doesn't write code to do this).
* Call super class $clinit; $clinit is always in position 0.
*/
if (type.getSuperClass() != null) {
JMethod myClinit = type.getMethods().get(0);
JMethod superClinit = type.getSuperClass().getMethods().get(0);
JMethodCall superClinitCall = new JMethodCall(myClinit.getSourceInfo(), null, superClinit);
JMethodBody body = (JMethodBody) myClinit.getBody();
body.getBlock().addStmt(0, superClinitCall.makeStatement());
}
// Implement getClass() implementation for all non-Object classes.
if (type.getSuperClass() != null && !JSORestrictionsChecker.isJsoSubclass(x.binding)) {
implementGetClass(type);
}
if (type instanceof JEnumType) {
processEnumType((JEnumType) type);
}
if (type instanceof JClassType) {
addBridgeMethods(x.binding);
}
Binding[] rescues = artificialRescues.get(x);
if (rescues != null) {
for (Binding rescue : rescues) {
if (rescue instanceof TypeBinding) {
type.addArtificialRescue(typeMap.get((TypeBinding) rescue));
} else if (rescue instanceof FieldBinding) {
type.addArtificialRescue(typeMap.get((FieldBinding) rescue));
} else if (rescue instanceof MethodBinding) {
type.addArtificialRescue(typeMap.get((MethodBinding) rescue));
} else {
throw new InternalCompilerException("Unknown artifical rescue binding type.");
}
}
}
curClass = classStack.pop();
}
protected JBlock pop(Block x) {
return (x == null) ? null : (JBlock) pop();
}
protected JExpression pop(Expression x) {
if (x == null) {
return null;
}
JExpression result = (JExpression) pop();
if (result == null) {
assert x instanceof NameReference;
return null;
}
result = simplify(result, x);
return result;
}
@SuppressWarnings("unchecked")
protected <T extends JExpression> List<T> pop(Expression[] expressions) {
if (expressions == null) {
return Collections.emptyList();
}
List<T> result = (List<T>) popList(expressions.length);
for (int i = 0; i < expressions.length; ++i) {
result.set(i, (T) simplify(result.get(i), expressions[i]));
}
return result;
}
protected JDeclarationStatement pop(LocalDeclaration decl) {
return (decl == null) ? null : (JDeclarationStatement) pop();
}
protected JStatement pop(Statement x) {
JNode pop = (x == null) ? null : pop();
if (x instanceof Expression) {
return simplify((JExpression) pop, (Expression) x).makeStatement();
}
return (JStatement) pop;
}
@SuppressWarnings("unchecked")
protected <T extends JStatement> List<T> pop(Statement[] statements) {
if (statements == null) {
return Collections.emptyList();
}
List<T> result = (List<T>) popList(statements.length);
int i = 0;
for (ListIterator<T> it = result.listIterator(); it.hasNext(); ++i) {
Object element = it.next();
if (element == null) {
it.remove();
} else if (element instanceof JExpression) {
it.set((T) simplify((JExpression) element, (Expression) statements[i]).makeStatement());
}
}
return result;
}
protected JBlock popBlock(SourceInfo info, Statement statement) {
JStatement stmt = pop(statement);
if (stmt instanceof JBlock) {
return (JBlock) stmt;
}
JBlock block = new JBlock(info);
if (stmt != null) {
block.addStmt(stmt);
}
return block;
}
protected JBlock popBlock(SourceInfo info, Statement[] statements) {
List<JStatement> stmts = pop(statements);
JBlock block = new JBlock(info);
block.addStmts(stmts);
return block;
}
protected void pushBinaryOp(Assignment x, JBinaryOperator op) {
pushBinaryOp(x, op, x.lhs, x.expression);
}
protected void pushBinaryOp(BinaryExpression x, JBinaryOperator op) {
pushBinaryOp(x, op, x.left, x.right);
}
protected boolean visit(TypeDeclaration x) {
JDeclaredType type = (JDeclaredType) typeMap.get(x.binding);
assert !type.isExternal();
classStack.push(curClass);
curClass = new ClassInfo(type, x);
/*
* It's okay to defer creation of synthetic fields, they can't be
* referenced until we analyze the code.
*/
int index = 0;
SourceTypeBinding binding = x.binding;
if (isNested(binding)) {
// add synthetic fields for outer this and locals
assert (type instanceof JClassType);
NestedTypeBinding nestedBinding = (NestedTypeBinding) binding;
if (nestedBinding.enclosingInstances != null) {
for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
createSyntheticField(arg, type, index++, Disposition.THIS_REF);
}
}
if (nestedBinding.outerLocalVariables != null) {
for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
// See InnerClassTest.testOuterThisFromSuperCall().
boolean isReallyThisRef = false;
if (arg.actualOuterLocalVariable instanceof SyntheticArgumentBinding) {
SyntheticArgumentBinding outer =
(SyntheticArgumentBinding) arg.actualOuterLocalVariable;
if (outer.matchingField != null) {
JField field = typeMap.get(outer.matchingField);
if (field.isThisRef()) {
isReallyThisRef = true;
}
}
}
createSyntheticField(arg, type, index++, isReallyThisRef ? Disposition.THIS_REF
: Disposition.FINAL);
}
}
}
return true;
}
/**
* <p>
* Add a bridge method to <code>clazzBinding</code> for any method it
* inherits that implements an interface method but that has a different
* erased signature from the interface method.
* </p>
*
* <p>
* The need for these bridges was pointed out in issue 3064. The goal is
* that virtual method calls through an interface type are translated to
* JavaScript that will function correctly. If the interface signature
* matches the signature of the implementing method, then nothing special
* needs to be done. If they are different, due to the use of generics, then
* GenerateJavaScriptAST is careful to do the right thing. There is a
* remaining case, though, that GenerateJavaScriptAST is not in a good
* position to fix: a method could be inherited from a superclass, used to
* implement an interface method that has a different type signature, and
* does not have the interface method in its list of overrides. In that
* case, a bridge method should be added that overrides the interface method
* and then calls the implementation method.
* </p>
*
* <p>
* This method should only be called once all regular, non-bridge methods
* have been installed on the GWT types.
* </p>
*/
private void addBridgeMethods(SourceTypeBinding clazzBinding) {
/*
* JDT adds bridge methods in all the places GWT needs them. Use JDT's
* bridge methods.
*/
if (clazzBinding.syntheticMethods() != null) {
for (SyntheticMethodBinding synthmeth : clazzBinding.syntheticMethods()) {
if (synthmeth.purpose == SyntheticMethodBinding.BridgeMethod && !synthmeth.isStatic()) {
createBridgeMethod(synthmeth);
}
}
}
}
private JBinaryOperation assignSyntheticField(SourceInfo info, SyntheticArgumentBinding arg) {
JParameter param = (JParameter) curMethod.locals.get(arg);
assert param != null;
JField field = curClass.syntheticFields.get(arg);
assert field != null;
JFieldRef lhs = makeInstanceFieldRef(info, field);
JParameterRef rhs = new JParameterRef(info, param);
JBinaryOperation asg =
new JBinaryOperation(info, lhs.getType(), JBinaryOperator.ASG, lhs, rhs);
return asg;
}
private JExpression box(JExpression original, int implicitConversion) {
int typeId = (implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
ClassScope scope = curClass.scope;
BaseTypeBinding primitiveType = (BaseTypeBinding) TypeBinding.wellKnownType(scope, typeId);
ReferenceBinding boxType = (ReferenceBinding) scope.boxing(primitiveType);
MethodBinding valueOfMethod =
boxType.getExactMethod(VALUE_OF, new TypeBinding[]{primitiveType}, scope
.compilationUnitScope());
assert valueOfMethod != null;
// Add a cast to the correct primitive type if needed.
JType targetPrimitiveType = typeMap.get(primitiveType);
if (original.getType() != targetPrimitiveType) {
original = new JCastOperation(original.getSourceInfo(), targetPrimitiveType, original);
}
JMethod boxMethod = typeMap.get(valueOfMethod);
JMethodCall call = new JMethodCall(original.getSourceInfo(), null, boxMethod);
call.addArg(original);
return call;
}
/**
* Create a bridge method. It calls a same-named method with the same
* arguments, but with a different type signature.
*/
private void createBridgeMethod(SyntheticMethodBinding jdtBridgeMethod) {
JMethod implMethod = typeMap.get(jdtBridgeMethod.targetMethod);
SourceInfo info = implMethod.getSourceInfo();
String[] paramNames = null;
List<JParameter> implParams = implMethod.getParams();
if (jdtBridgeMethod.parameters != null) {
int paramCount = implParams.size();
assert paramCount == jdtBridgeMethod.parameters.length;
paramNames = new String[paramCount];
for (int i = 0; i < paramCount; ++i) {
paramNames[i] = implParams.get(i).getName();
}
}
JMethod bridgeMethod = createSynthicMethodFromBinding(info, jdtBridgeMethod, paramNames);
if (implMethod.isFinal()) {
bridgeMethod.setFinal();
}
// create a call and pass all arguments through, casting if necessary
JMethodCall call = new JMethodCall(info, makeThisRef(info), implMethod);
for (int i = 0; i < bridgeMethod.getParams().size(); i++) {
JParameter param = bridgeMethod.getParams().get(i);
JParameterRef paramRef = new JParameterRef(info, param);
call.addArg(maybeCast(implParams.get(i).getType(), paramRef));
}
JMethodBody body = (JMethodBody) bridgeMethod.getBody();
if (bridgeMethod.getType() == JPrimitiveType.VOID) {
body.getBlock().addStmt(call.makeStatement());
} else {
body.getBlock().addStmt(new JReturnStatement(info, call));
}
}
private JField createEnumValuesField(JEnumType type) {
// $VALUES = new E[]{A,B,B};
JArrayType enumArrayType = new JArrayType(type);
JField valuesField =
new JField(type.getSourceInfo(), "$VALUES", type, enumArrayType, true, Disposition.FINAL);
type.addField(valuesField);
SourceInfo info = type.getSourceInfo();
List<JExpression> initializers = new ArrayList<JExpression>();
for (JEnumField field : type.getEnumList()) {
JFieldRef fieldRef = new JFieldRef(info, null, field, type);
initializers.add(fieldRef);
}
JNewArray newExpr = JNewArray.createInitializers(info, enumArrayType, initializers);
JFieldRef valuesRef = new JFieldRef(info, null, valuesField, type);
JDeclarationStatement declStmt = new JDeclarationStatement(info, valuesRef, newExpr);
JBlock clinitBlock = ((JMethodBody) type.getMethods().get(0).getBody()).getBlock();
/*
* HACKY: the $VALUES array must be initialized immediately after all of
* the enum fields, but before any user initialization (which might rely
* on $VALUES). The "1 + " is the statement containing the call to
* Enum.$clinit().
*/
int insertionPoint = 1 + type.getEnumList().size();
assert clinitBlock.getStatements().size() >= initializers.size() + 1;
clinitBlock.addStmt(insertionPoint, declStmt);
return valuesField;
}
private JLocal createLocal(LocalDeclaration x) {
LocalVariableBinding b = x.binding;
TypeBinding resolvedType = x.type.resolvedType;
JType localType;
if (resolvedType.constantPoolName() != null) {
localType = typeMap.get(resolvedType);
} else {
// Special case, a statically unreachable local type.
localType = JNullType.INSTANCE;
}
SourceInfo info = makeSourceInfo(x);
JLocal newLocal =
JProgram.createLocal(info, intern(x.name), localType, b.isFinal(), curMethod.body);
curMethod.locals.put(b, newLocal);
return newLocal;
}
private JField createSyntheticField(SyntheticArgumentBinding arg, JDeclaredType enclosingType,
int index, Disposition disposition) {
JType type = typeMap.get(arg.type);
SourceInfo info = enclosingType.getSourceInfo();
JField field = new JField(info, intern(arg.name), enclosingType, type, false, disposition);
// TODO: remove me, source identical for now!
enclosingType.addField(index, field);
// enclosingType.addField(field);
curClass.syntheticFields.put(arg, field);
if (arg.matchingField != null) {
typeMap.setField(arg.matchingField, field);
}
return field;
}
private JExpression getConstant(SourceInfo info, Constant constant) {
switch (constant.typeID()) {
case TypeIds.T_int:
return JIntLiteral.get(constant.intValue());
case TypeIds.T_byte:
return JIntLiteral.get(constant.byteValue());
case TypeIds.T_short:
return JIntLiteral.get(constant.shortValue());
case TypeIds.T_char:
return JCharLiteral.get(constant.charValue());
case TypeIds.T_float:
return JFloatLiteral.get(constant.floatValue());
case TypeIds.T_double:
return JDoubleLiteral.get(constant.doubleValue());
case Constant.T_boolean:
return JBooleanLiteral.get(constant.booleanValue());
case Constant.T_long:
return JLongLiteral.get(constant.longValue());
case Constant.T_JavaLangString:
return getStringLiteral(info, constant.stringValue());
case Constant.T_null:
return JNullLiteral.INSTANCE;
default:
throw new InternalCompilerException("Unknown Constant type: " + constant.typeID());
}
}
/**
* Get a new label of a particular name, or create a new one if it doesn't
* exist already.
*/
private JLabel getOrCreateLabel(SourceInfo info, char[] name) {
if (name == null) {
return null;
}
String sname = intern(name);
JLabel jlabel = curMethod.labels.get(sname);
if (jlabel == null) {
jlabel = new JLabel(info, sname);
curMethod.labels.put(sname, jlabel);
}
return jlabel;
}
private JStringLiteral getStringLiteral(SourceInfo info, char[] chars) {
return new JStringLiteral(info, intern(chars), javaLangString);
}
private JStringLiteral getStringLiteral(SourceInfo info, String string) {
return new JStringLiteral(info, intern(string), javaLangString);
}
/**
* TODO(scottb): move to UnifyAst and only for non-abstract classes.
*/
private void implementGetClass(JDeclaredType type) {
JMethod method = type.getMethods().get(2);
assert ("getClass".equals(method.getName()));
SourceInfo info = method.getSourceInfo();
if ("com.google.gwt.lang.Array".equals(type.getName())) {
// Special implementation: return this.arrayClass
JField arrayClassField = null;
for (JField field : type.getFields()) {
if ("arrayClass".equals(field.getName())) {
arrayClassField = field;
}
}
assert arrayClassField != null;
implementMethod(method, new JFieldRef(info, makeThisRef(info), arrayClassField, type));
} else {
implementMethod(method, new JClassLiteral(info, type));
}
}
private void implementMethod(JMethod method, JExpression returnValue) {
JMethodBody body = (JMethodBody) method.getBody();
JBlock block = body.getBlock();
SourceInfo info;
if (block.getStatements().size() > 0) {
info = block.getStatements().get(0).getSourceInfo();
} else {
info = method.getSourceInfo();
}
block.clear();
block.addStmt(new JReturnStatement(info, returnValue));
}
private JDeclarationStatement makeDeclaration(SourceInfo info, JLocal local, JExpression value) {
return new JDeclarationStatement(info, new JLocalRef(info, local), value);
}
private JFieldRef makeInstanceFieldRef(SourceInfo info, JField field) {
return new JFieldRef(info, makeThisRef(info), field, curClass.classType);
}
private JExpression makeLocalRef(SourceInfo info, LocalVariableBinding b) {
JVariable variable = curMethod.locals.get(b);
assert variable != null;
if (variable instanceof JLocal) {
return new JLocalRef(info, (JLocal) variable);
} else {
return new JParameterRef(info, (JParameter) variable);
}
}
private JThisRef makeThisRef(SourceInfo info) {
return new JThisRef(info, curClass.classType);
}
private JExpression makeThisReference(SourceInfo info, ReferenceBinding targetType,
boolean exactMatch, BlockScope scope) {
targetType = (ReferenceBinding) targetType.erasure();
Object[] path = scope.getEmulationPath(targetType, exactMatch, false);
if (path == null) {
throw new InternalCompilerException("No emulation path.");
}
if (path == BlockScope.EmulationPathToImplicitThis) {
return makeThisRef(info);
}
JExpression ref;
ReferenceBinding type;
if (curMethod.scope.isInsideInitializer() && path[0] instanceof SyntheticArgumentBinding) {
SyntheticArgumentBinding b = (SyntheticArgumentBinding) path[0];
JField field = curClass.syntheticFields.get(b);
assert field != null;
ref = makeInstanceFieldRef(info, field);
type = (ReferenceBinding) b.type.erasure();
} else if (path[0] instanceof SyntheticArgumentBinding) {
SyntheticArgumentBinding b = (SyntheticArgumentBinding) path[0];
JParameter param = (JParameter) curMethod.locals.get(b);
assert param != null;
ref = new JParameterRef(info, param);
type = (ReferenceBinding) b.type.erasure();
} else if (path[0] instanceof FieldBinding) {
FieldBinding b = (FieldBinding) path[0];
JField field = typeMap.get(b);
assert field != null;
ref = makeInstanceFieldRef(info, field);
type = (ReferenceBinding) b.type.erasure();
} else {
throw new InternalCompilerException("Unknown emulation path.");
}
for (int i = 1; i < path.length; ++i) {
SyntheticMethodBinding b = (SyntheticMethodBinding) path[i];
assert type == b.declaringClass.erasure();
FieldBinding fieldBinding = b.targetReadField;
JField field = typeMap.get(fieldBinding);
assert field != null;
ref = new JFieldRef(info, ref, field, curClass.classType);
type = (ReferenceBinding) fieldBinding.type.erasure();
}
return ref;
}
private JExpression maybeBoxOrUnbox(JExpression original, int implicitConversion) {
if (implicitConversion != -1) {
if ((implicitConversion & TypeIds.BOXING) != 0) {
return box(original, implicitConversion);
} else if ((implicitConversion & TypeIds.UNBOXING) != 0) {
return unbox(original, implicitConversion);
}
}
return original;
}
private JExpression maybeCast(JType expected, JExpression expression) {
if (expected != expression.getType()) {
// Must be a generic; insert a cast operation.
JReferenceType toType = (JReferenceType) expected;
return new JCastOperation(expression.getSourceInfo(), toType, expression);
} else {
return expression;
}
}
private JNode pop() {
return nodeStack.remove(nodeStack.size() - 1);
}
private List<JExpression> popCallArgs(SourceInfo info, Expression[] jdtArgs,
MethodBinding binding) {
List<JExpression> args = pop(jdtArgs);
if (!binding.isVarargs()) {
return args;
}
// Handle the odd var-arg case.
if (jdtArgs == null) {
// Get writable collection (args is currently Collections.emptyList()).
args = new ArrayList<JExpression>(1);
}
TypeBinding[] params = binding.parameters;
int varArg = params.length - 1;
// See if there's a single varArg which is already an array.
if (args.size() == params.length) {
if (jdtArgs[varArg].resolvedType.isCompatibleWith(params[varArg])) {
// Already the correct array type.
return args;
}
}
// Need to synthesize an appropriately-typed array.
List<JExpression> tail = args.subList(varArg, args.size());
ArrayList<JExpression> initializers = new ArrayList<JExpression>(tail);
tail.clear();
JArrayType lastParamType = (JArrayType) typeMap.get(params[varArg]);
JNewArray newArray = JNewArray.createInitializers(info, lastParamType, initializers);
args.add(newArray);
return args;
}
private List<? extends JNode> popList(int count) {
List<JNode> tail = nodeStack.subList(nodeStack.size() - count, nodeStack.size());
// Make a copy.
List<JNode> result = new ArrayList<JNode>(tail);
// Causes the tail to be removed.
tail.clear();
return result;
}
private void popMethodInfo() {
curMethod = methodStack.pop();
}
private void processEnumType(JEnumType type) {
JField valuesField = createEnumValuesField(type);
// $clinit, $init, getClass, valueOf, values
{
JMethod valueOfMethod = type.getMethods().get(3);
assert "valueOf".equals(valueOfMethod.getName());
writeEnumValueOfMethod(type, valueOfMethod, valuesField);
}
{
JMethod valuesMethod = type.getMethods().get(4);
assert "values".equals(valuesMethod.getName());
writeEnumValuesMethod(type, valuesMethod, valuesField);
}
}
private void processNativeMethod(MethodDeclaration x) {
JMethod method = curMethod.method;
JsniMethod jsniMethod = jsniMethods.get(x);
assert jsniMethod != null;
SourceInfo info = method.getSourceInfo();
JsFunction jsFunction = jsniMethod.function();
JsniMethodBody body = new JsniMethodBody(info);
method.setBody(body);
jsFunction.setFromJava(true);
body.setFunc(jsFunction);
// Resolve locals, params, and JSNI.
JsParameterResolver localResolver = new JsParameterResolver(jsFunction);
localResolver.accept(jsFunction);
JsniResolver jsniResolver = new JsniResolver(body);
jsniResolver.accept(jsFunction);
}
private void processSuperCallLocalArgs(ReferenceBinding superClass, JMethodCall call) {
if (superClass.syntheticOuterLocalVariables() != null) {
for (SyntheticArgumentBinding arg : superClass.syntheticOuterLocalVariables()) {
// TODO: use emulation path here.
// Got to be one of my params
JType varType = typeMap.get(arg.type);
String varName = intern(arg.name);
JParameter param = null;
for (JParameter paramIt : curMethod.method.getParams()) {
if (varType == paramIt.getType() && varName.equals(paramIt.getName())) {
param = paramIt;
}
}
if (param == null) {
throw new InternalCompilerException(
"Could not find matching local arg for explicit super ctor call.");
}
call.addArg(new JParameterRef(call.getSourceInfo(), param));
}
}
}
private void processSuperCallThisArgs(ReferenceBinding superClass, JMethodCall call,
JExpression qualifier, Expression qualification) {
if (superClass.syntheticEnclosingInstanceTypes() != null) {
for (ReferenceBinding targetType : superClass.syntheticEnclosingInstanceTypes()) {
if (qualification != null && superClass.enclosingType() == targetType) {
assert qualification.resolvedType.erasure().isCompatibleWith(targetType);
call.addArg(qualifier);
} else {
call.addArg(makeThisReference(call.getSourceInfo(), targetType, false, curMethod.scope));
}
}
}
}
private void processThisCallLocalArgs(ReferenceBinding binding, JMethodCall call) {
if (binding.syntheticOuterLocalVariables() != null) {
for (SyntheticArgumentBinding arg : binding.syntheticOuterLocalVariables()) {
JParameter param = (JParameter) curMethod.locals.get(arg);
assert param != null;
call.addArg(new JParameterRef(call.getSourceInfo(), param));
}
}
}
private void processThisCallThisArgs(ReferenceBinding binding, JMethodCall call) {
if (binding.syntheticEnclosingInstanceTypes() != null) {
Iterator<JParameter> paramIt = curMethod.method.getParams().iterator();
if (curClass.classType.isEnumOrSubclass() != null) {
// Skip past the enum args.
paramIt.next();
paramIt.next();
}
for (@SuppressWarnings("unused")
ReferenceBinding argType : binding.syntheticEnclosingInstanceTypes()) {
JParameter param = paramIt.next();
call.addArg(new JParameterRef(call.getSourceInfo(), param));
}
}
}
private void push(JNode node) {
nodeStack.add(node);
}
private void pushBinaryOp(Expression x, JBinaryOperator op, Expression lhs, Expression rhs) {
try {
JType type = typeMap.get(x.resolvedType);
SourceInfo info = makeSourceInfo(x);
JExpression exprArg2 = pop(rhs);
JExpression exprArg1 = pop(lhs);
push(new JBinaryOperation(info, type, op, exprArg1, exprArg2));
} catch (Throwable e) {
throw translateException(x, e);
}
}
private void pushInitializerMethodInfo(FieldDeclaration x, MethodScope scope) {
JMethod initMeth;
if (x.isStatic()) {
initMeth = curClass.type.getMethods().get(0);
} else {
initMeth = curClass.type.getMethods().get(1);
}
pushMethodInfo(new MethodInfo(initMeth, (JMethodBody) initMeth.getBody(), scope));
}
private void pushMethodInfo(MethodInfo newInfo) {
methodStack.push(curMethod);
curMethod = newInfo;
}
private void pushNewExpression(SourceInfo info, AllocationExpression x, Expression qualifier,
List<JExpression> arguments, BlockScope scope) {
TypeBinding typeBinding = x.resolvedType;
if (typeBinding.constantPoolName() == null) {
/*
* Weird case: if JDT determines that this local class is totally
* uninstantiable, it won't bother allocating a local name.
*/
push(JNullLiteral.INSTANCE);
return;
}
assert typeBinding.isClass() || typeBinding.isEnum();
MethodBinding b = x.binding;
assert b.isConstructor();
JConstructor ctor = (JConstructor) typeMap.get(b);
JMethodCall call = new JNewInstance(info, ctor, curClass.type);
JExpression qualExpr = pop(qualifier);
// Enums: hidden arguments for the name and id.
if (x.enumConstant != null) {
call.addArgs(getStringLiteral(info, x.enumConstant.name), JIntLiteral
.get(x.enumConstant.binding.original().id));
}
// Synthetic args for inner classes
ReferenceBinding targetBinding = (ReferenceBinding) b.declaringClass.erasure();
boolean isNested = isNested(targetBinding);
if (isNested) {
// Synthetic this args for inner classes
if (targetBinding.syntheticEnclosingInstanceTypes() != null) {
ReferenceBinding checkedTargetType =
targetBinding.isAnonymousType() ? (ReferenceBinding) targetBinding.superclass()
.erasure() : targetBinding;
ReferenceBinding targetEnclosingType = checkedTargetType.enclosingType();
for (ReferenceBinding argType : targetBinding.syntheticEnclosingInstanceTypes()) {
argType = (ReferenceBinding) argType.erasure();
if (qualifier != null && argType == targetEnclosingType) {
call.addArg(qualExpr);
} else {
JExpression thisRef = makeThisReference(info, argType, false, scope);
call.addArg(thisRef);
}
}
}
}
// Plain old regular user arguments
call.addArgs(arguments);
// Synthetic args for inner classes
if (isNested) {
// Synthetic locals for local classes
if (targetBinding.syntheticOuterLocalVariables() != null) {
for (SyntheticArgumentBinding arg : targetBinding.syntheticOuterLocalVariables()) {
LocalVariableBinding targetVariable = arg.actualOuterLocalVariable;
VariableBinding[] path = scope.getEmulationPath(targetVariable);
assert path.length == 1;
if (curMethod.scope.isInsideInitializer()
&& path[0] instanceof SyntheticArgumentBinding) {
SyntheticArgumentBinding sb = (SyntheticArgumentBinding) path[0];
JField field = curClass.syntheticFields.get(sb);
assert field != null;
call.addArg(makeInstanceFieldRef(info, field));
} else if (path[0] instanceof LocalVariableBinding) {
JExpression localRef = makeLocalRef(info, (LocalVariableBinding) path[0]);
call.addArg(localRef);
} else if (path[0] instanceof FieldBinding) {
JField field = typeMap.get((FieldBinding) path[0]);
assert field != null;
call.addArg(makeInstanceFieldRef(info, field));
} else {
throw new InternalCompilerException("Unknown emulation path.");
}
}
}
}
if (ctor.getEnclosingType() == javaLangString) {
/*
* MAGIC: java.lang.String is implemented as a JavaScript String
* primitive with a modified prototype. This requires funky handling of
* constructor calls. We find a method named _String() whose signature
* matches the requested constructor
*
* TODO(scottb): consider moving this to a later pass.
*/
MethodBinding staticBinding =
targetBinding.getExactMethod(_STRING, b.parameters, curCud.scope);
assert staticBinding.isStatic();
JMethod staticMethod = typeMap.get(staticBinding);
JMethodCall newCall = new JMethodCall(info, null, staticMethod);
newCall.addArgs(call.getArgs());
call = newCall;
}
push(call);
}
/**
* Don't process unreachable statements, because JDT doesn't always fully
* resolve them, which can crash us.
*/
private Statement[] reduceToReachable(Statement[] statements) {
if (statements == null) {
return null;
}
int reachableCount = 0;
for (Statement statement : statements) {
if ((statement.bits & ASTNode.IsReachable) != 0) {
++reachableCount;
}
}
if (reachableCount == statements.length) {
return statements;
}
Statement[] newStatments = new Statement[reachableCount];
int index = 0;
for (Statement statement : statements) {
if ((statement.bits & ASTNode.IsReachable) != 0) {
newStatments[index++] = statement;
}
}
return newStatments;
}
private JExpression resolveNameReference(NameReference x, BlockScope scope) {
SourceInfo info = makeSourceInfo(x);
if (x.constant != Constant.NotAConstant) {
return getConstant(info, x.constant);
}
Binding binding = x.binding;
JExpression result = null;
if (binding instanceof LocalVariableBinding) {
LocalVariableBinding b = (LocalVariableBinding) binding;
if ((x.bits & ASTNode.DepthMASK) != 0) {
VariableBinding[] path = scope.getEmulationPath(b);
if (path == null) {
/*
* Don't like this, but in rare cases (e.g. the variable is only
* ever used as an unnecessary qualifier) JDT provides no emulation
* to the desired variable.
*/
// throw new InternalCompilerException("No emulation path.");
return null;
}
assert path.length == 1;
if (curMethod.scope.isInsideInitializer() && path[0] instanceof SyntheticArgumentBinding) {
SyntheticArgumentBinding sb = (SyntheticArgumentBinding) path[0];
JField field = curClass.syntheticFields.get(sb);
assert field != null;
result = makeInstanceFieldRef(info, field);
} else if (path[0] instanceof LocalVariableBinding) {
result = makeLocalRef(info, (LocalVariableBinding) path[0]);
} else if (path[0] instanceof FieldBinding) {
FieldBinding fb = (FieldBinding) path[0];
assert curClass.typeDecl.binding.isCompatibleWith(x.actualReceiverType.erasure());
JField field = typeMap.get(fb);
assert field != null;
result = makeInstanceFieldRef(info, field);
} else {
throw new InternalCompilerException("Unknown emulation path.");
}
} else {
result = makeLocalRef(info, b);
}
} else if (binding instanceof FieldBinding) {
FieldBinding b = ((FieldBinding) x.binding).original();
JField field = typeMap.get(b);
assert field != null;
JExpression thisRef = null;
if (!b.isStatic()) {
thisRef = makeThisReference(info, (ReferenceBinding) x.actualReceiverType, false, scope);
}
result = new JFieldRef(info, thisRef, field, curClass.type);
} else {
return null;
}
assert result != null;
return result;
}
private JExpression simplify(JExpression result, Expression x) {
if (x.constant != null && x.constant != Constant.NotAConstant) {
// Prefer JDT-computed constant value to the actual written expression.
result = getConstant(result.getSourceInfo(), x.constant);
}
return maybeBoxOrUnbox(result, x.implicitConversion);
}
private JExpression unbox(JExpression original, int implicitConversion) {
int typeId = implicitConversion & TypeIds.COMPILE_TYPE_MASK;
ClassScope scope = curClass.scope;
BaseTypeBinding primitiveType = (BaseTypeBinding) TypeBinding.wellKnownType(scope, typeId);
ReferenceBinding boxType = (ReferenceBinding) scope.boxing(primitiveType);
char[] selector = CharOperation.concat(primitiveType.simpleName, VALUE);
MethodBinding valueMethod =
boxType.getExactMethod(selector, NO_TYPES, scope.compilationUnitScope());
assert valueMethod != null;
JMethod unboxMethod = typeMap.get(valueMethod);
JMethodCall call = new JMethodCall(original.getSourceInfo(), original, unboxMethod);
return call;
}
private void writeEnumValueOfMethod(JEnumType type, JMethod method, JField valuesField) {
JField mapField;
TypeBinding mapType;
ReferenceBinding enumType = curCud.scope.getJavaLangEnum();
{
/*
* Make an inner class to hold a lazy-init name-value map. We use a
* class to take advantage of its clinit.
*
* class Map { $MAP = Enum.createValueOfMap($VALUES); }
*/
SourceInfo info = type.getSourceInfo();
JClassType mapClass = new JClassType(info, intern(type.getName() + "$Map"), false, true);
mapClass.setSuperClass(javaLangObject);
mapClass.setEnclosingType(type);
newTypes.add(mapClass);
MethodBinding[] createValueOfMapBindings = enumType.getMethods(CREATE_VALUE_OF_MAP);
assert createValueOfMapBindings.length == 1;
MethodBinding createValueOfMapBinding = createValueOfMapBindings[0];
mapType = createValueOfMapBinding.returnType;
mapField =
new JField(info, "$MAP", mapClass, typeMap.get(mapType), true, Disposition.FINAL);
mapClass.addField(mapField);
JMethodCall call = new JMethodCall(info, null, typeMap.get(createValueOfMapBinding));
call.addArg(new JFieldRef(info, null, valuesField, mapClass));
JFieldRef mapRef = new JFieldRef(info, null, mapField, mapClass);
JDeclarationStatement declStmt = new JDeclarationStatement(info, mapRef, call);
JMethod clinit =
createSyntheticMethod(info, "$clinit", mapClass, JPrimitiveType.VOID, false, true,
true, true);
JBlock clinitBlock = ((JMethodBody) clinit.getBody()).getBlock();
clinitBlock.addStmt(declStmt);
}
/*
* return Enum.valueOf(Enum$Map.Map.$MAP, name);
*/
{
SourceInfo info = method.getSourceInfo();
MethodBinding valueOfBinding =
enumType.getExactMethod(VALUE_OF, new TypeBinding[]{
mapType, curCud.scope.getJavaLangString()}, curCud.scope);
assert valueOfBinding != null;
JFieldRef mapRef = new JFieldRef(info, null, mapField, type);
JParameterRef nameRef = new JParameterRef(info, method.getParams().get(0));
JMethodCall call = new JMethodCall(info, null, typeMap.get(valueOfBinding));
call.addArgs(mapRef, nameRef);
implementMethod(method, call);
}
}
private void writeEnumValuesMethod(JEnumType type, JMethod method, JField valuesField) {
// return $VALUES;
JFieldRef valuesRef = new JFieldRef(method.getSourceInfo(), null, valuesField, type);
implementMethod(method, valuesRef);
}
}
static class ClassInfo {
public final JClassType classType;
public final ClassScope scope;
public final Map<SyntheticArgumentBinding, JField> syntheticFields =
new IdentityHashMap<SyntheticArgumentBinding, JField>();
public final JDeclaredType type;
public final TypeDeclaration typeDecl;
public ClassInfo(JDeclaredType type, TypeDeclaration x) {
this.type = type;
this.classType = (type instanceof JClassType) ? (JClassType) type : null;
this.typeDecl = x;
this.scope = x.scope;
}
}
static class CudInfo {
public final String fileName;
public final CompilationUnitScope scope;
public final int[] separatorPositions;
public CudInfo(CompilationUnitDeclaration cud) {
fileName = intern(cud.getFileName());
separatorPositions = cud.compilationResult().getLineSeparatorPositions();
scope = cud.scope;
}
}
static class MethodInfo {
public final JMethodBody body;
public final Map<String, JLabel> labels = new HashMap<String, JLabel>();
public final Map<LocalVariableBinding, JVariable> locals =
new IdentityHashMap<LocalVariableBinding, JVariable>();
public final JMethod method;
public final MethodScope scope;
public MethodInfo(JMethod method, JMethodBody methodBody, MethodScope methodScope) {
this.method = method;
this.body = methodBody;
this.scope = methodScope;
}
}
public static boolean ENABLED = System.getProperties().containsKey("x.gwt.astBuilder");
/**
* Manually tracked version count.
*
* TODO(zundel): something much more awesome?
*/
private static final long AST_VERSION = 1;
private static final char[] _STRING = "_String".toCharArray();
private static final String ARRAY_LENGTH_FIELD = "length";
/**
* Reflective access to {@link ForeachStatement#collectionElementType}.
*/
private static final Field collectionElementTypeField;
private static final char[] CREATE_VALUE_OF_MAP = "createValueOfMap".toCharArray();
private static final char[] HAS_NEXT = "hasNext".toCharArray();
private static final char[] ITERATOR = "iterator".toCharArray();
private static final char[] NEXT = "next".toCharArray();
private static final TypeBinding[] NO_TYPES = new TypeBinding[0];
private static final char[] ORDINAL = "ordinal".toCharArray();
private static final StringInterner stringInterner = StringInterner.get();
private static final char[] VALUE = "Value".toCharArray();
private static final char[] VALUE_OF = "valueOf".toCharArray();
private static final char[] VALUES = "values".toCharArray();
static {
InternalCompilerException.preload();
try {
collectionElementTypeField = ForeachStatement.class.getDeclaredField("collectionElementType");
collectionElementTypeField.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(
"Unexpectedly unable to access ForeachStatement.collectionElementType via reflection", e);
}
}
/**
* Returns a serialization version number. Used to determine if the AST
* contained within cached compilation units is compatible with the current
* version of GWT.
*/
public static long getSerializationVersion() {
// TODO(zundel): something much awesomer.
return ENABLED ? AST_VERSION : 0L;
}
static String dotify(char[][] name) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < name.length; ++i) {
if (i > 0) {
result.append('.');
}
result.append(name[i]);
}
return result.toString();
}
static Disposition getFieldDisposition(FieldBinding binding) {
Disposition disposition;
if (isCompileTimeConstant(binding)) {
disposition = Disposition.COMPILE_TIME_CONSTANT;
} else if (binding.isFinal()) {
disposition = Disposition.FINAL;
} else if (binding.isVolatile()) {
disposition = Disposition.VOLATILE;
} else {
disposition = Disposition.NONE;
}
return disposition;
}
static String intern(char[] cs) {
return intern(String.valueOf(cs));
}
static String intern(String s) {
return stringInterner.intern(s);
}
static boolean isNested(ReferenceBinding binding) {
return binding.isNestedType() && !binding.isStatic();
}
private static boolean isCompileTimeConstant(FieldBinding binding) {
assert !binding.isFinal() || !binding.isVolatile();
boolean isCompileTimeConstant =
binding.isStatic() && binding.isFinal() && (binding.constant() != Constant.NotAConstant);
if (isCompileTimeConstant) {
assert binding.type.isBaseType() || (binding.type.id == TypeIds.T_JavaLangString);
}
return isCompileTimeConstant;
}
/**
* Returns <code>true</code> if JDT optimized the condition to
* <code>false</code>.
*/
private static boolean isOptimizedFalse(Expression condition) {
if (condition != null) {
Constant cst = condition.optimizedBooleanConstant();
if (cst != Constant.NotAConstant) {
if (cst.booleanValue() == false) {
return true;
}
}
}
return false;
}
/**
* Returns <code>true</code> if JDT optimized the condition to
* <code>true</code>.
*/
private static boolean isOptimizedTrue(Expression condition) {
if (condition != null) {
Constant cst = condition.optimizedBooleanConstant();
if (cst != Constant.NotAConstant) {
if (cst.booleanValue()) {
return true;
}
}
}
return false;
}
Map<TypeDeclaration, Binding[]> artificialRescues;
CudInfo curCud = null;
JClassType javaLangClass = null;
JClassType javaLangObject = null;
JClassType javaLangString = null;
Map<MethodDeclaration, JsniMethod> jsniMethods;
Map<String, Binding> jsniRefs;
final ReferenceMapper typeMap = new ReferenceMapper();
private final AstVisitor astVisitor = new AstVisitor();
private List<JDeclaredType> newTypes;
public List<JDeclaredType> process(CompilationUnitDeclaration cud,
Map<TypeDeclaration, Binding[]> artificialRescues,
Map<MethodDeclaration, JsniMethod> jsniMethods, Map<String, Binding> jsniRefs) {
if (cud.types == null) {
return Collections.emptyList();
}
this.artificialRescues = artificialRescues;
this.jsniRefs = jsniRefs;
this.jsniMethods = jsniMethods;
newTypes = new ArrayList<JDeclaredType>();
curCud = new CudInfo(cud);
for (TypeDeclaration typeDecl : cud.types) {
createTypes(typeDecl);
}
// Now that types exist, cache Object, String, etc.
javaLangObject = (JClassType) typeMap.get(cud.scope.getJavaLangObject());
javaLangString = (JClassType) typeMap.get(cud.scope.getJavaLangString());
javaLangClass = (JClassType) typeMap.get(cud.scope.getJavaLangClass());
for (TypeDeclaration typeDecl : cud.types) {
// Resolve super type / interface relationships.
resolveTypeRefs(typeDecl);
}
for (TypeDeclaration typeDecl : cud.types) {
// Create fields and empty methods.
createMembers(typeDecl);
}
for (TypeDeclaration typeDecl : cud.types) {
// Build the code.
typeDecl.traverse(astVisitor, cud.scope);
}
List<JDeclaredType> result = newTypes;
// Clean up.
typeMap.clearSource();
this.jsniRefs = jsniRefs;
this.jsniMethods = jsniMethods;
newTypes = null;
curCud = null;
javaLangObject = null;
javaLangString = null;
javaLangClass = null;
return result;
}
SourceInfo makeSourceInfo(AbstractMethodDeclaration x) {
int startLine =
Util.getLineNumber(x.sourceStart, curCud.separatorPositions, 0,
curCud.separatorPositions.length - 1);
return SourceOrigin.create(x.sourceStart, x.bodyEnd, startLine, curCud.fileName);
}
SourceInfo makeSourceInfo(ASTNode x) {
int startLine =
Util.getLineNumber(x.sourceStart, curCud.separatorPositions, 0,
curCud.separatorPositions.length - 1);
return SourceOrigin.create(x.sourceStart, x.sourceEnd, startLine, curCud.fileName);
}
InternalCompilerException translateException(ASTNode node, Throwable e) {
if (e instanceof VirtualMachineError) {
// Always rethrow VM errors (an attempt to wrap may fail).
throw (VirtualMachineError) e;
}
InternalCompilerException ice;
if (e instanceof InternalCompilerException) {
ice = (InternalCompilerException) e;
} else {
ice = new InternalCompilerException("Error constructing Java AST", e);
}
if (node != null) {
ice.addNode(node.getClass().getName(), node.toString(), makeSourceInfo(node));
}
return ice;
}
private void createField(FieldDeclaration x) {
if (x instanceof Initializer) {
return;
}
SourceInfo info = makeSourceInfo(x);
FieldBinding binding = x.binding;
JType type = typeMap.get(binding.type);
JDeclaredType enclosingType = (JDeclaredType) typeMap.get(binding.declaringClass);
JField field;
if (x.initialization != null && x.initialization instanceof AllocationExpression
&& ((AllocationExpression) x.initialization).enumConstant != null) {
field =
new JEnumField(info, intern(binding.name), binding.original().id,
(JEnumType) enclosingType, (JClassType) type);
} else {
field =
new JField(info, intern(binding.name), enclosingType, type, binding.isStatic(),
getFieldDisposition(binding));
}
enclosingType.addField(field);
typeMap.setField(binding, field);
}
private void createMembers(TypeDeclaration x) {
SourceTypeBinding binding = x.binding;
JDeclaredType type = (JDeclaredType) typeMap.get(binding);
SourceInfo info = type.getSourceInfo();
try {
/**
* We emulate static initializers and instance initializers as methods. As
* in other cases, this gives us: simpler AST, easier to optimize, more
* like output JavaScript. Clinit is always in slot 0, init (if it exists)
* is always in slot 1.
*/
assert type.getMethods().size() == 0;
createSyntheticMethod(info, "$clinit", type, JPrimitiveType.VOID, false, true, true, true);
if (type instanceof JClassType) {
assert type.getMethods().size() == 1;
createSyntheticMethod(info, "$init", type, JPrimitiveType.VOID, false, false, true, true);
// Add a getClass() implementation for all non-Object classes.
if (type != javaLangObject && !JSORestrictionsChecker.isJsoSubclass(binding)) {
assert type.getMethods().size() == 2;
createSyntheticMethod(info, "getClass", type, javaLangClass, false, false, false, false);
}
}
if (type instanceof JEnumType) {
{
assert type.getMethods().size() == 3;
MethodBinding valueOfBinding =
binding.getExactMethod(VALUE_OF, new TypeBinding[]{x.scope.getJavaLangString()},
curCud.scope);
assert valueOfBinding != null;
createSynthicMethodFromBinding(info, valueOfBinding, new String[]{"name"});
}
{
assert type.getMethods().size() == 4;
MethodBinding valuesBinding = binding.getExactMethod(VALUES, NO_TYPES, curCud.scope);
assert valuesBinding != null;
createSynthicMethodFromBinding(info, valuesBinding, null);
}
}
if (x.fields != null) {
for (FieldDeclaration field : x.fields) {
if (x.binding.isLocalType() && field.isStatic()) {
/*
* Source compatibility with genJavaAst; static fields in local
* types don't get visited.
*/
} else {
createField(field);
}
}
}
if (x.methods != null) {
for (AbstractMethodDeclaration method : x.methods) {
createMethod(method);
}
}
if (x.memberTypes != null) {
for (TypeDeclaration memberType : x.memberTypes) {
createMembers(memberType);
}
}
} catch (Throwable e) {
InternalCompilerException ice = translateException(null, e);
StringBuffer sb = new StringBuffer();
x.printHeader(0, sb);
ice.addNode(x.getClass().getName(), sb.toString(), type.getSourceInfo());
throw ice;
}
}
private void createMethod(AbstractMethodDeclaration x) {
if (x instanceof Clinit) {
return;
}
SourceInfo info = makeSourceInfo(x);
MethodBinding b = x.binding;
ReferenceBinding declaringClass = (ReferenceBinding) b.declaringClass.erasure();
Set<String> alreadyNamedVariables = new HashSet<String>();
JDeclaredType enclosingType = (JDeclaredType) typeMap.get(declaringClass);
assert !enclosingType.isExternal();
JMethod method;
boolean isNested = isNested(declaringClass);
if (x.isConstructor()) {
method = new JConstructor(info, (JClassType) enclosingType);
if (x.binding.declaringClass.isEnum()) {
// Enums have hidden arguments for name and value
method.addParam(new JParameter(info, "enum$name", typeMap.get(x.scope.getJavaLangString()),
true, false, method));
method.addParam(new JParameter(info, "enum$ordinal", JPrimitiveType.INT, true, false,
method));
}
// add synthetic args for outer this
if (isNested) {
NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
if (nestedBinding.enclosingInstances != null) {
for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
String argName = String.valueOf(arg.name);
if (alreadyNamedVariables.contains(argName)) {
argName += "_" + i;
}
createParameter(info, arg, argName, method);
alreadyNamedVariables.add(argName);
}
}
}
} else {
method =
new JMethod(info, intern(b.selector), enclosingType, typeMap.get(b.returnType), b
.isAbstract(), b.isStatic(), b.isFinal(), b.isPrivate());
}
// User args.
createParameters(method, x);
if (x.isConstructor()) {
if (isNested) {
// add synthetic args for locals
NestedTypeBinding nestedBinding = (NestedTypeBinding) declaringClass;
// add synthetic args for outer this and locals
if (nestedBinding.outerLocalVariables != null) {
for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
String argName = String.valueOf(arg.name);
if (alreadyNamedVariables.contains(argName)) {
argName += "_" + i;
}
createParameter(info, arg, argName, method);
alreadyNamedVariables.add(argName);
}
}
}
}
mapExceptions(method, b);
if (b.isSynthetic()) {
method.setSynthetic();
}
enclosingType.addMethod(method);
typeMap.setMethod(b, method);
}
private void createParameter(SourceInfo info, LocalVariableBinding binding, JMethod method) {
createParameter(info, binding, intern(binding.name), method);
}
private void createParameter(SourceInfo info, LocalVariableBinding binding, String name,
JMethod method) {
JParameter param =
new JParameter(info, name, typeMap.get(binding.type), binding.isFinal(), false, method);
method.addParam(param);
}
private void createParameters(JMethod method, AbstractMethodDeclaration x) {
if (x.arguments != null) {
for (Argument argument : x.arguments) {
SourceInfo info = makeSourceInfo(argument);
LocalVariableBinding binding = argument.binding;
createParameter(info, binding, method);
}
}
method.freezeParamTypes();
}
private JMethod createSyntheticMethod(SourceInfo info, String name, JDeclaredType enclosingType,
JType returnType, boolean isAbstract, boolean isStatic, boolean isFinal, boolean isPrivate) {
JMethod method =
new JMethod(info, name, enclosingType, returnType, isAbstract, isStatic, isFinal, isPrivate);
method.freezeParamTypes();
method.setSynthetic();
method.setBody(new JMethodBody(info));
enclosingType.addMethod(method);
return method;
}
private JMethod createSynthicMethodFromBinding(SourceInfo info, MethodBinding binding,
String[] paramNames) {
JMethod method = typeMap.createMethod(info, binding, paramNames);
assert !method.isExternal();
method.setBody(new JMethodBody(info));
typeMap.setMethod(binding, method);
return method;
}
private void createTypes(TypeDeclaration x) {
SourceInfo info = makeSourceInfo(x);
try {
SourceTypeBinding binding = x.binding;
String name;
if (binding instanceof LocalTypeBinding) {
char[] localName = binding.constantPoolName();
name = new String(localName).replace('/', '.');
} else {
name = dotify(binding.compoundName);
}
name = intern(name);
JDeclaredType type;
if (binding.isClass()) {
type = new JClassType(info, name, binding.isAbstract(), binding.isFinal());
} else if (binding.isInterface() || binding.isAnnotationType()) {
type = new JInterfaceType(info, name);
} else if (binding.isEnum()) {
if (binding.isAnonymousType()) {
// Don't model an enum subclass as a JEnumType.
type = new JClassType(info, name, false, true);
} else {
type = new JEnumType(info, name, binding.isAbstract());
}
} else {
throw new InternalCompilerException("ReferenceBinding is not a class, interface, or enum.");
}
typeMap.setSourceType(binding, type);
newTypes.add(type);
if (x.memberTypes != null) {
for (TypeDeclaration memberType : x.memberTypes) {
createTypes(memberType);
}
}
} catch (Throwable e) {
InternalCompilerException ice = translateException(null, e);
StringBuffer sb = new StringBuffer();
x.printHeader(0, sb);
ice.addNode(x.getClass().getName(), sb.toString(), info);
throw ice;
}
}
private void mapExceptions(JMethod method, MethodBinding binding) {
for (ReferenceBinding thrownBinding : binding.thrownExceptions) {
JClassType type = (JClassType) typeMap.get(thrownBinding);
method.addThrownException(type);
}
}
private void resolveTypeRefs(TypeDeclaration x) {
SourceTypeBinding binding = x.binding;
JDeclaredType type = (JDeclaredType) typeMap.get(binding);
try {
ReferenceBinding superClassBinding = binding.superclass();
if (type instanceof JClassType && superClassBinding != null) {
assert (binding.superclass().isClass() || binding.superclass().isEnum());
JClassType superClass = (JClassType) typeMap.get(superClassBinding);
((JClassType) type).setSuperClass(superClass);
}
ReferenceBinding[] superInterfaces = binding.superInterfaces();
for (ReferenceBinding superInterfaceBinding : superInterfaces) {
assert (superInterfaceBinding.isInterface());
JInterfaceType superInterface = (JInterfaceType) typeMap.get(superInterfaceBinding);
type.addImplements(superInterface);
}
ReferenceBinding enclosingBinding = binding.enclosingType();
if (enclosingBinding != null) {
type.setEnclosingType((JDeclaredType) typeMap.get(enclosingBinding));
}
if (x.memberTypes != null) {
for (TypeDeclaration memberType : x.memberTypes) {
resolveTypeRefs(memberType);
}
}
} catch (Throwable e) {
InternalCompilerException ice = translateException(null, e);
StringBuffer sb = new StringBuffer();
x.printHeader(0, sb);
ice.addNode(x.getClass().getName(), sb.toString(), type.getSourceInfo());
throw ice;
}
}
}