| /* |
| * 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<T> 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; |
| } |
| } |
| } |