CompileModule / GwtAstBuilder
Introduces a new CompileModule action, which triggers the production of decoupled ASTs during CompilationState build. At present, these ASTs are unusable, but follow-on work to stitch together decoupled ASTs should allow us to eliminate the traditional WebModeCompilerFrontEnd / BuildTypeMap / GenerateJavaAST path to building a GWT AST.
http://gwt-code-reviews.appspot.com/1373805/
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9844 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/CompileModule.java b/dev/core/src/com/google/gwt/dev/CompileModule.java
new file mode 100644
index 0000000..d584dca
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/CompileModule.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2011 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;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.CompileTaskRunner.CompileTask;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.CompilationUnit;
+import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.jdt.RebindPermutationOracle;
+import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
+import com.google.gwt.dev.jjs.CorrelationFactory;
+import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory;
+import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.jjs.InternalCompilerException.NodeInfo;
+import com.google.gwt.dev.jjs.JJSOptionsImpl;
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.impl.BuildTypeMap;
+import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
+import com.google.gwt.dev.jjs.impl.GwtAstBuilder;
+import com.google.gwt.dev.jjs.impl.TypeLinker;
+import com.google.gwt.dev.jjs.impl.TypeMap;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.util.Memory;
+import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
+import com.google.gwt.dev.util.arg.ArgHandlerModuleName;
+import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
+import com.google.gwt.dev.util.arg.OptionOutDir;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
+
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Compiles a GWT module.
+ */
+public class CompileModule {
+
+ static class ArgProcessor extends ArgProcessorBase {
+ public ArgProcessor(CompileModuleOptions options) {
+ registerHandler(new ArgHandlerLogLevel(options));
+ registerHandler(new ArgHandlerOutDir(options) {
+ @Override
+ public String[] getDefaultArgs() {
+ return new String[]{getTag(), "bin"};
+ }
+ });
+ registerHandler(new ArgHandlerModuleName(options));
+ }
+
+ @Override
+ protected String getName() {
+ return CompileModule.class.getName();
+ }
+ }
+
+ interface CompileModuleOptions extends CompileTaskOptions, OptionOutDir {
+ }
+
+ static class CompileModuleOptionsImpl extends CompileTaskOptionsImpl implements
+ CompileModuleOptions {
+
+ private File outDir;
+
+ public CompileModuleOptionsImpl() {
+ }
+
+ public CompileModuleOptionsImpl(CompileModuleOptions other) {
+ copyFrom(other);
+ }
+
+ public void copyFrom(CompileModuleOptions other) {
+ super.copyFrom(other);
+ setOutDir(other.getOutDir());
+ }
+
+ public File getOutDir() {
+ return outDir;
+ }
+
+ public void setOutDir(File outDir) {
+ this.outDir = outDir;
+ }
+ }
+
+ public static JProgram buildGenerateJavaAst(final TreeLogger logger, ModuleDef module,
+ final CompilationState compilationState) throws UnableToCompleteException {
+ final StandardGeneratorContext genCtx =
+ new StandardGeneratorContext(compilationState, module, null, new ArtifactSet(), true);
+ RebindPermutationOracle rpo = new RebindPermutationOracle() {
+ public void clear() {
+ }
+
+ public String[] getAllPossibleRebindAnswers(TreeLogger logger, String sourceTypeName)
+ throws UnableToCompleteException {
+ return new String[0];
+ }
+
+ public CompilationState getCompilationState() {
+ return compilationState;
+ }
+
+ public StandardGeneratorContext getGeneratorContext() {
+ return genCtx;
+ }
+ };
+
+ List<String> allRootTypes = new ArrayList<String>();
+ for (CompilationUnit unit : compilationState.getCompilationUnits()) {
+ allRootTypes.add(unit.getTypeName());
+ }
+ CompilationUnitDeclaration[] goldenCuds =
+ WebModeCompilerFrontEnd.getCompilationUnitDeclarations(logger, allRootTypes
+ .toArray(new String[allRootTypes.size()]), rpo, TypeLinker.NULL_TYPE_LINKER).compiledUnits;
+
+ CorrelationFactory correlator = DummyCorrelationFactory.INSTANCE;
+ JsProgram jsProgram = new JsProgram(correlator);
+ JProgram jprogram = new JProgram(correlator);
+ TypeMap typeMap = new TypeMap(jprogram);
+ TypeDeclaration[] allTypeDeclarations = BuildTypeMap.exec(typeMap, goldenCuds, jsProgram);
+ // BuildTypeMap can uncover syntactic JSNI errors; report & abort
+ checkForErrors(logger, goldenCuds);
+
+ // Compute all super type/sub type info
+ jprogram.typeOracle.computeBeforeAST();
+
+ // (2) Create our own Java AST from the JDT AST.
+ GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, new JJSOptionsImpl());
+
+ // GenerateJavaAST can uncover semantic JSNI errors; report & abort
+ checkForErrors(logger, goldenCuds);
+ return jprogram;
+ }
+
+ public static CompilationState buildGwtAst(TreeLogger logger, ModuleDef module)
+ throws UnableToCompleteException {
+ boolean gwtAstWasEnabled = GwtAstBuilder.ENABLED;
+ try {
+ GwtAstBuilder.ENABLED = true;
+ long start = System.currentTimeMillis();
+ final CompilationState compilationState = module.getCompilationState(logger);
+ logger.log(TreeLogger.INFO, (System.currentTimeMillis() - start)
+ + " time to get compilation state");
+ return compilationState;
+ } finally {
+ GwtAstBuilder.ENABLED = gwtAstWasEnabled;
+ }
+ }
+
+ public static void main(String[] args) {
+ Memory.initialize();
+ if (System.getProperty("gwt.jjs.dumpAst") != null) {
+ System.out.println("Will dump AST to: " + System.getProperty("gwt.jjs.dumpAst"));
+ }
+
+ SpeedTracerLogger.init();
+
+ /*
+ * NOTE: main always exits with a call to System.exit to terminate any
+ * non-daemon threads that were started in Generators. Typically, this is to
+ * shutdown AWT related threads, since the contract for their termination is
+ * still implementation-dependent.
+ */
+ final CompileModuleOptions options = new CompileModuleOptionsImpl();
+ if (new ArgProcessor(options).processArgs(args)) {
+ CompileTask task = new CompileTask() {
+ public boolean run(TreeLogger logger) throws UnableToCompleteException {
+ // TODO: updates?
+ return new CompileModule(options).run(logger);
+ }
+ };
+ if (CompileTaskRunner.runWithAppropriateLogger(options, task)) {
+ // Exit w/ success code.
+ System.exit(0);
+ }
+ }
+ // Exit w/ non-success code.
+ System.exit(1);
+ }
+
+ static UnableToCompleteException logAndTranslateException(TreeLogger logger, Throwable e) {
+ if (e instanceof UnableToCompleteException) {
+ // just rethrow
+ return (UnableToCompleteException) e;
+ } else if (e instanceof InternalCompilerException) {
+ TreeLogger topBranch =
+ logger.branch(TreeLogger.ERROR, "An internal compiler exception occurred", e);
+ List<NodeInfo> nodeTrace = ((InternalCompilerException) e).getNodeTrace();
+ for (NodeInfo nodeInfo : nodeTrace) {
+ SourceInfo info = nodeInfo.getSourceInfo();
+ String msg;
+ if (info != null) {
+ String fileName = info.getFileName();
+ fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
+ fileName = fileName.substring(fileName.lastIndexOf('\\') + 1);
+ msg = "at " + fileName + "(" + info.getStartLine() + "): ";
+ } else {
+ msg = "<no source info>: ";
+ }
+
+ String description = nodeInfo.getDescription();
+ if (description != null) {
+ msg += description;
+ } else {
+ msg += "<no description available>";
+ }
+ TreeLogger nodeBranch = topBranch.branch(TreeLogger.ERROR, msg, null);
+ String className = nodeInfo.getClassName();
+ if (className != null) {
+ nodeBranch.log(TreeLogger.INFO, className, null);
+ }
+ }
+ return new UnableToCompleteException();
+ } else if (e instanceof VirtualMachineError) {
+ // Always rethrow VM errors (an attempt to wrap may fail).
+ throw (VirtualMachineError) e;
+ } else {
+ logger.log(TreeLogger.ERROR, "Unexpected internal compiler error", e);
+ return new UnableToCompleteException();
+ }
+ }
+
+ private static void checkForErrors(TreeLogger logger, CompilationUnitDeclaration[] goldenCuds)
+ throws UnableToCompleteException {
+ for (CompilationUnitDeclaration cud : goldenCuds) {
+ CompilationResult result = cud.compilationResult();
+ if (result.hasErrors()) {
+ logger.log(TreeLogger.ERROR, "Aborting on '" + String.valueOf(cud.getFileName()) + "'");
+ throw new UnableToCompleteException();
+ }
+ }
+ }
+
+ private final CompileModuleOptionsImpl options;
+
+ public CompileModule(CompileModuleOptions options) {
+ this.options = new CompileModuleOptionsImpl(options);
+ }
+
+ public boolean run(final TreeLogger logger) {
+ try {
+ ModuleDef module = ModuleDefLoader.loadFromClassPath(logger, options.getModuleNames().get(0));
+ final CompilationState compilationState = buildGwtAst(logger, module);
+
+ long start = System.currentTimeMillis();
+ Map<String, JDeclaredType> compStateTypes = new HashMap<String, JDeclaredType>();
+ for (CompilationUnit unit : compilationState.getCompilationUnits()) {
+ for (JDeclaredType type : unit.getTypes()) {
+ compStateTypes.put(type.getName(), type);
+ }
+ }
+ logger.log(TreeLogger.INFO, (System.currentTimeMillis() - start) + " time to get all types");
+
+ start = System.currentTimeMillis();
+ JProgram jprogram = buildGenerateJavaAst(logger, module, compilationState);
+ logger.log(TreeLogger.INFO, (System.currentTimeMillis() - start) + " time to build old AST");
+
+ for (JDeclaredType genJavaAstType : jprogram.getDeclaredTypes()) {
+ String typeName = genJavaAstType.getName();
+ if ("com.google.gwt.core.client.JavaScriptObject".equals(typeName)) {
+ // Known mismatch; genJavaAst version implements all JSO interfaces.
+ continue;
+ }
+ JDeclaredType compStateType = compStateTypes.get(typeName);
+ if (compStateType == null) {
+ System.out.println("No matching prebuilt type for '" + typeName + "'");
+ } else {
+ String oldSource = genJavaAstType.toSource();
+ String newSource = compStateType.toSource();
+ if (!oldSource.equals(newSource)) {
+ System.out.println("Mismatched output for '" + typeName + "'");
+ System.out.println("GenerateJavaAST:");
+ System.out.println(oldSource);
+ System.out.println("GwtAstBuilder:");
+ System.out.println(newSource);
+ }
+ }
+ }
+
+ return !compilationState.hasErrors();
+ } catch (Throwable e) {
+ logAndTranslateException(logger, e);
+ return false;
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
index 788194b..66e8c52 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CachedCompilationUnit.java
@@ -15,6 +15,9 @@
*/
package com.google.gwt.dev.javac;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.util.collect.Lists;
+
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import java.io.IOException;
@@ -107,6 +110,12 @@
}
@Override
+ public List<JDeclaredType> getTypes() {
+ // TODO(scottb): implement.
+ return Lists.create();
+ }
+
+ @Override
public boolean isError() {
return isError;
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
index b7991d7..8d18de3 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
@@ -21,6 +21,8 @@
import com.google.gwt.dev.javac.JdtCompiler.AdditionalTypeProviderDelegate;
import com.google.gwt.dev.javac.JdtCompiler.UnitProcessor;
import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.impl.GwtAstBuilder;
import com.google.gwt.dev.js.ast.JsRootScope;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.StringInterner;
@@ -111,9 +113,17 @@
Dependencies dependencies =
new Dependencies(packageName, unresolvedQualified, unresolvedSimple, apiRefs);
+ List<JDeclaredType> types = Collections.emptyList();
+ if (GwtAstBuilder.ENABLED) {
+ if (!cud.compilationResult().hasErrors()) {
+ // Make a GWT AST.
+ types = astBuilder.process(cud, jsniMethods, jsniRefs);
+ }
+ }
+
CompilationUnit unit =
- builder.build(compiledClasses, dependencies, jsniMethods.values(), methodArgs, cud
- .compilationResult().getProblems());
+ builder.build(compiledClasses, types, dependencies, jsniMethods.values(), methodArgs,
+ cud.compilationResult().getProblems());
addValidUnit(unit);
// Cache the valid unit for future compiles.
ContentId contentId = builder.getContentId();
@@ -140,6 +150,8 @@
*/
private final Map<String, CompiledClass> allValidClasses = new HashMap<String, CompiledClass>();
+ private final GwtAstBuilder astBuilder = new GwtAstBuilder();
+
/**
* The JDT compiler.
*/
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
index 44c73e3..ee23e0d 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
@@ -19,6 +19,7 @@
import com.google.gwt.dev.asm.ClassReader;
import com.google.gwt.dev.asm.Opcodes;
import com.google.gwt.dev.asm.commons.EmptyVisitor;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.util.DiskCache;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.collect.HashMap;
@@ -291,6 +292,11 @@
*/
public abstract String getTypeName();
+ /**
+ * Returns the GWT AST types in this unit.
+ */
+ public abstract List<JDeclaredType> getTypes();
+
@Deprecated
public final boolean hasAnonymousClasses() {
for (CompiledClass cc : getCompiledClasses()) {
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
index 8b701cb..0b72e1b 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.dev.javac;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.Util;
@@ -60,11 +61,11 @@
@Override
protected CompilationUnit makeUnit(List<CompiledClass> compiledClasses,
- Dependencies dependencies,
+ List<JDeclaredType> types, Dependencies dependencies,
Collection<? extends JsniMethod> jsniMethods,
MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
return new GeneratedCompilationUnit(generatedUnit, compiledClasses,
- dependencies, jsniMethods, methodArgs, problems);
+ types, dependencies, jsniMethods, methodArgs, problems);
}
@Override
@@ -145,11 +146,12 @@
@Override
protected CompilationUnit makeUnit(List<CompiledClass> compiledClasses,
- Dependencies dependencies,
+ List<JDeclaredType> types, Dependencies dependencies,
Collection<? extends JsniMethod> jsniMethods,
MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
return new SourceFileCompilationUnit(getResource(), contentId,
- compiledClasses, dependencies, jsniMethods, methodArgs, problems);
+ compiledClasses, types, dependencies, jsniMethods, methodArgs,
+ problems);
}
}
@@ -158,10 +160,12 @@
private final GeneratedUnit generatedUnit;
public GeneratedCompilationUnit(GeneratedUnit generatedUnit,
- List<CompiledClass> compiledClasses, Dependencies dependencies,
+ List<CompiledClass> compiledClasses, List<JDeclaredType> types,
+ Dependencies dependencies,
Collection<? extends JsniMethod> jsniMethods,
MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
- super(compiledClasses, dependencies, jsniMethods, methodArgs, problems);
+ super(compiledClasses, types, dependencies, jsniMethods, methodArgs,
+ problems);
this.generatedUnit = generatedUnit;
}
@@ -241,12 +245,13 @@
}
public CompilationUnit build(List<CompiledClass> compiledClasses,
- Dependencies dependencies, Collection<? extends JsniMethod> jsniMethods,
+ List<JDeclaredType> types, Dependencies dependencies,
+ Collection<? extends JsniMethod> jsniMethods,
MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
// Free the source now.
source = null;
- return makeUnit(compiledClasses, dependencies, jsniMethods, methodArgs,
- problems);
+ return makeUnit(compiledClasses, types, dependencies, jsniMethods,
+ methodArgs, problems);
}
public abstract ContentId getContentId();
@@ -270,8 +275,8 @@
protected abstract String doGetSource();
protected abstract CompilationUnit makeUnit(
- List<CompiledClass> compiledClasses, Dependencies dependencies,
- Collection<? extends JsniMethod> jsniMethods,
+ List<CompiledClass> compiledClasses, List<JDeclaredType> types,
+ Dependencies dependencies, Collection<? extends JsniMethod> jsniMethods,
MethodArgNamesLookup methodArgs, CategorizedProblem[] errors);
/**
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
index 3248fd8..a16776e 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.dev.javac;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.util.collect.Lists;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
@@ -26,15 +27,18 @@
private final Dependencies dependencies;
private final List<CompiledClass> exposedCompiledClasses;
+ private final List<JDeclaredType> exposedTypes;
private final boolean hasErrors;
private final List<JsniMethod> jsniMethods;
private final MethodArgNamesLookup methodArgs;
private final CategorizedProblem[] problems;
public CompilationUnitImpl(List<CompiledClass> compiledClasses,
- Dependencies dependencies, Collection<? extends JsniMethod> jsniMethods,
+ List<JDeclaredType> types, Dependencies dependencies,
+ Collection<? extends JsniMethod> jsniMethods,
MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
this.exposedCompiledClasses = Lists.normalizeUnmodifiable(compiledClasses);
+ this.exposedTypes = Lists.normalizeUnmodifiable(types);
this.dependencies = dependencies;
this.jsniMethods = Lists.create(jsniMethods.toArray(new JsniMethod[jsniMethods.size()]));
this.methodArgs = methodArgs;
@@ -64,6 +68,11 @@
}
@Override
+ public List<JDeclaredType> getTypes() {
+ return exposedTypes;
+ }
+
+ @Override
public boolean isError() {
return hasErrors;
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
index 37350b8..c55229f 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JSORestrictionsChecker.java
@@ -257,18 +257,11 @@
checker.check();
}
- static String errAlreadyImplemented(String intfName, String impl1,
- String impl2) {
- return "Only one JavaScriptObject type may implement the methods of an "
- + "interface that declared methods. The interface (" + intfName
- + ") is implemented by both (" + impl1 + ") and (" + impl2 + ")";
- }
-
/**
* Returns {@code true} if {@code typeBinding} is {@code JavaScriptObject} or
* any subtype.
*/
- static boolean isJso(TypeBinding typeBinding) {
+ public static boolean isJso(TypeBinding typeBinding) {
if (!(typeBinding instanceof ReferenceBinding)) {
return false;
}
@@ -286,7 +279,7 @@
* Returns {@code true} if {@code typeBinding} is a subtype of
* {@code JavaScriptObject}, but not {@code JavaScriptObject} itself.
*/
- static boolean isJsoSubclass(TypeBinding typeBinding) {
+ public static boolean isJsoSubclass(TypeBinding typeBinding) {
if (!(typeBinding instanceof ReferenceBinding)) {
return false;
}
@@ -294,6 +287,13 @@
return isJso(binding.superclass());
}
+ static String errAlreadyImplemented(String intfName, String impl1,
+ String impl2) {
+ return "Only one JavaScriptObject type may implement the methods of an "
+ + "interface that declared methods. The interface (" + intfName
+ + ") is implemented by both (" + impl1 + ") and (" + impl2 + ")";
+ }
+
private static void errorOn(ASTNode node, CompilationUnitDeclaration cud,
String error) {
GWTProblem.recordError(node, cud, error, new InstalledHelpInfo(
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
index 17de260..f7e6ab9 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
@@ -17,6 +17,7 @@
import com.google.gwt.dev.jdt.TypeRefVisitor;
import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.util.Name.BinaryName;
import com.google.gwt.dev.util.collect.Lists;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
@@ -124,7 +125,8 @@
public void process(CompilationUnitBuilder builder,
CompilationUnitDeclaration cud, List<CompiledClass> compiledClasses) {
- CompilationUnit unit = builder.build(compiledClasses, new Dependencies(),
+ CompilationUnit unit = builder.build(compiledClasses,
+ Collections.<JDeclaredType> emptyList(), new Dependencies(),
Collections.<JsniMethod> emptyList(), new MethodArgNamesLookup(),
cud.compilationResult().getProblems());
results.add(unit);
diff --git a/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
index 44fc619..d57e611 100644
--- a/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.dev.javac;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.resource.Resource;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
@@ -39,10 +40,11 @@
private final ContentId contentId;
public SourceFileCompilationUnit(Resource sourceFile, ContentId contentId,
- List<CompiledClass> compiledClasses, Dependencies dependencies,
- Collection<? extends JsniMethod> jsniMethods,
+ List<CompiledClass> compiledClasses, List<JDeclaredType> types,
+ Dependencies dependencies, Collection<? extends JsniMethod> jsniMethods,
MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
- super(compiledClasses, dependencies, jsniMethods, methodArgs, problems);
+ super(compiledClasses, types, dependencies, jsniMethods, methodArgs,
+ problems);
this.sourceFile = sourceFile;
this.contentId = contentId;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
index 9cc04c0..eeb174b 100755
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
@@ -92,6 +92,15 @@
}
/**
+ * TODO: remove me, just for source compatibility.
+ */
+ @Deprecated
+ public void addField(int index, JField field) {
+ assert field.getEnclosingType() == this;
+ fields = Lists.add(fields, index, field);
+ }
+
+ /**
* Adds a field to this type.
*/
public void addField(JField field) {
@@ -298,7 +307,7 @@
annotations = (List<JAnnotation>) stream.readObject();
}
-/**
+ /**
* See {@link #writeMethodBodies(ObjectOutputStream).
*
* @see #writeMethodBodies(ObjectOutputStream)
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
index eccad57..dea6b2e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
@@ -27,7 +27,7 @@
public static final JNullType INSTANCE = new JNullType(SourceOrigin.UNKNOWN);
private JNullType(SourceInfo sourceInfo) {
- super(sourceInfo, "<null>");
+ super(sourceInfo, "null");
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
new file mode 100644
index 0000000..387b37e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -0,0 +1,3236 @@
+/*
+ * 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.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.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.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.
+ *
+ * TODO: move more error reporting to
+ * {@link com.google.gwt.dev.javac.JsniChecker}.
+ */
+ private class JsniResolver extends JsModVisitor {
+ 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::");
+ } else if (binding instanceof TypeBinding) {
+ JType type = typeMap.get((TypeBinding) binding);
+ processClassLiteral(type, info, ctx);
+ } else if (binding instanceof FieldBinding) {
+ JField field = typeMap.get((FieldBinding) binding);
+ processField(x, info, field, ctx);
+ } else {
+ JMethod method = typeMap.get((MethodBinding) binding);
+ processMethod(x, info, method);
+ }
+ }
+ }
+
+ private void processClassLiteral(JType type, SourceInfo info, JsContext ctx) {
+ assert !ctx.isLvalue();
+ JClassLiteral classLiteral = new JClassLiteral(info, type);
+ nativeMethodBody.addClassRef(classLiteral);
+ }
+
+ private void processField(JsNameRef nameRef, SourceInfo info, JField field, JsContext ctx) {
+ /*
+ * We must replace any compile-time constants with the constant value of
+ * the field.
+ */
+ if (field.isCompileTimeConstant()) {
+ assert !ctx.isLvalue();
+ JLiteral initializer = field.getConstInitializer();
+ JType type = initializer.getType();
+ if (type instanceof JPrimitiveType || initializer instanceof JStringLiteral) {
+ GenerateJavaScriptLiterals generator = new GenerateJavaScriptLiterals();
+ generator.accept(initializer);
+ JsExpression result = generator.peek();
+ assert (result != null);
+ ctx.replaceMe(result);
+ return;
+ }
+ }
+
+ // Normal: create a jsniRef.
+ 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 ArrayList<JNode> nodeStack = new ArrayList<JNode>();
+
+ @Override
+ public void endVisit(AllocationExpression x, BlockScope scope) {
+ try {
+ List<JExpression> arguments = popCallArgs(x.arguments, x.binding);
+ pushNewExpression(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 BinaryExpression.LEFT_SHIFT:
+ op = JBinaryOperator.SHL;
+ break;
+ case BinaryExpression.RIGHT_SHIFT:
+ op = JBinaryOperator.SHR;
+ break;
+ case BinaryExpression.UNSIGNED_RIGHT_SHIFT:
+ op = JBinaryOperator.SHRU;
+ break;
+ case BinaryExpression.PLUS:
+ if (x.resolvedType instanceof ReferenceBinding) {
+ op = JBinaryOperator.CONCAT;
+ } else {
+ op = JBinaryOperator.ADD;
+ }
+ break;
+ case BinaryExpression.MINUS:
+ op = JBinaryOperator.SUB;
+ break;
+ case BinaryExpression.REMAINDER:
+ op = JBinaryOperator.MOD;
+ break;
+ case BinaryExpression.XOR:
+ op = JBinaryOperator.BIT_XOR;
+ break;
+ case BinaryExpression.AND:
+ op = JBinaryOperator.BIT_AND;
+ break;
+ case BinaryExpression.MULTIPLY:
+ op = JBinaryOperator.MUL;
+ break;
+ case BinaryExpression.OR:
+ op = JBinaryOperator.BIT_OR;
+ break;
+ case BinaryExpression.DIVIDE:
+ op = JBinaryOperator.DIV;
+ break;
+ case BinaryExpression.LESS_EQUAL:
+ op = JBinaryOperator.LTE;
+ break;
+ case BinaryExpression.GREATER_EQUAL:
+ op = JBinaryOperator.GTE;
+ break;
+ case BinaryExpression.GREATER:
+ op = JBinaryOperator.GT;
+ break;
+ case BinaryExpression.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 CompoundAssignment.PLUS:
+ if (x.resolvedType instanceof ReferenceBinding) {
+ op = JBinaryOperator.ASG_CONCAT;
+ } else {
+ op = JBinaryOperator.ASG_ADD;
+ }
+ break;
+ case CompoundAssignment.MINUS:
+ op = JBinaryOperator.ASG_SUB;
+ break;
+ case CompoundAssignment.MULTIPLY:
+ op = JBinaryOperator.ASG_MUL;
+ break;
+ case CompoundAssignment.DIVIDE:
+ op = JBinaryOperator.ASG_DIV;
+ break;
+ case CompoundAssignment.AND:
+ op = JBinaryOperator.ASG_BIT_AND;
+ break;
+ case CompoundAssignment.OR:
+ op = JBinaryOperator.ASG_BIT_OR;
+ break;
+ case CompoundAssignment.XOR:
+ op = JBinaryOperator.ASG_BIT_XOR;
+ break;
+ case CompoundAssignment.REMAINDER:
+ op = JBinaryOperator.ASG_MOD;
+ break;
+ case CompoundAssignment.LEFT_SHIFT:
+ op = JBinaryOperator.ASG_SHL;
+ break;
+ case CompoundAssignment.RIGHT_SHIFT:
+ op = JBinaryOperator.ASG_SHR;
+ break;
+ case CompoundAssignment.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 (declaringClass instanceof NestedTypeBinding) {
+ 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 & BinaryExpression.OperatorMASK) >> BinaryExpression.OperatorSHIFT) {
+ case BinaryExpression.EQUAL_EQUAL:
+ op = JBinaryOperator.EQ;
+ break;
+ case BinaryExpression.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(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 = superClass.isNestedType() && !superClass.isStatic();
+ 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 = declaringClass.isNestedType() && !declaringClass.isStatic();
+ 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) {
+ /*
+ * Compute the collection element type by walking the iterator()
+ * method, which may be parameterized.
+ */
+ ReferenceBinding collectionType = (ReferenceBinding) x.collection.resolvedType;
+ MethodBinding iteratorMethod =
+ collectionType.getExactMethod(ITERATOR, NO_TYPES, cudScope);
+ ReferenceBinding iteratorType = (ReferenceBinding) iteratorMethod.returnType;
+ MethodBinding nextMethod = iteratorType.getMethods(NEXT)[0];
+ TypeBinding collectionElementType = nextMethod.returnType;
+ 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(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 PostfixExpression.MINUS:
+ op = JUnaryOperator.DEC;
+ break;
+
+ case PostfixExpression.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 PostfixExpression.MINUS:
+ op = JUnaryOperator.DEC;
+ break;
+
+ case PostfixExpression.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 {
+ List<JExpression> arguments = popCallArgs(x.arguments, x.binding);
+ pushNewExpression(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 instanceof ReferenceBinding) {
+ // Must be an enum; 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 & UnaryExpression.OperatorMASK) >> UnaryExpression.OperatorSHIFT);
+
+ switch (operator) {
+ case UnaryExpression.MINUS:
+ op = JUnaryOperator.NEG;
+ break;
+
+ case UnaryExpression.NOT:
+ op = JUnaryOperator.NOT;
+ break;
+
+ case UnaryExpression.PLUS:
+ // Odd case.. useless + operator; just leave the operand on the
+ // stack.
+ return;
+
+ case UnaryExpression.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 ("com.google.gwt.core.client.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.getEnclosingType().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();
+ if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
+ 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 (declaringClass.isNestedType() && !declaringClass.isStatic()) {
+ // 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).getEnclosingType().isExternal();
+ pushInitializerMethodInfo(x, scope);
+ return true;
+ } catch (Throwable e) {
+ throw translateException(x, e);
+ }
+ }
+
+ public boolean visit(ForStatement x, BlockScope scope) {
+ // SEE NOTE ON JDT FORCED OPTIMIZATIONS
+ if (isOptimizedFalse(x.condition)) {
+ x.action = null;
+ }
+ return true;
+ }
+
+ 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.getEnclosingType().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);
+ }
+
+ 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);
+ }
+
+ // Reimplement GWT.isClient(), GWT.isProdMode(), GWT.isScript().
+ if ("com.google.gwt.core.client.GWT".equals(type.getName())) {
+ implementGwtMethods(type);
+ }
+
+ if (type instanceof JEnumType) {
+ processEnumType((JEnumType) type);
+ }
+
+ if (type instanceof JClassType) {
+ addBridgeMethods(x.binding);
+ }
+
+ // TODO: uprefs???
+
+ 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 (binding.isNestedType() && !binding.isStatic()) {
+ // 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) {
+ SourceInfo info = curClass.classType.getSourceInfo();
+ JMethod implMethod = typeMap.get(jdtBridgeMethod.targetMethod);
+ 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);
+ curMethod.body.addLocal(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 Constant.T_int:
+ return JIntLiteral.get(constant.intValue());
+ case Constant.T_byte:
+ return JIntLiteral.get(constant.byteValue());
+ case Constant.T_short:
+ return JIntLiteral.get(constant.shortValue());
+ case Constant.T_char:
+ return JCharLiteral.get(constant.charValue());
+ case Constant.T_float:
+ return JFloatLiteral.get(constant.floatValue());
+ case Constant.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);
+ }
+
+ 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 implementGwtMethods(JDeclaredType type) {
+ for (JMethod method : type.getMethods()) {
+ if (method.getOriginalParamTypes().size() != 0) {
+ continue;
+ }
+ if ("isClient".equals(method.getName())) {
+ implementMethod(method, JBooleanLiteral.TRUE);
+ } else if ("isProdMode".equals(method.getName())) {
+ implementMethod(method, JBooleanLiteral.TRUE);
+ } else if ("isScript".equals(method.getName())) {
+ implementMethod(method, JBooleanLiteral.TRUE);
+ }
+ }
+ }
+
+ 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(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(SourceOrigin.UNKNOWN, 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(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();
+
+ SourceInfo info = makeSourceInfo(x);
+ 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();
+ NestedTypeBinding nestedBinding = null;
+ if (targetBinding.isNestedType() && !targetBinding.isStatic()) {
+ nestedBinding = (NestedTypeBinding) targetBinding;
+ }
+ if (nestedBinding != null) {
+ // Synthetic this args for inner classes
+ if (nestedBinding.enclosingInstances != null) {
+ ReferenceBinding checkedTargetType =
+ targetBinding.isAnonymousType() ? (ReferenceBinding) targetBinding.superclass()
+ .erasure() : targetBinding;
+ ReferenceBinding targetEnclosingType = checkedTargetType.enclosingType();
+ for (SyntheticArgumentBinding arg : nestedBinding.enclosingInstances) {
+ TypeBinding argType = arg.type.erasure();
+ if (qualifier != null && argType == targetEnclosingType) {
+ call.addArg(qualExpr);
+ } else {
+ JExpression thisRef =
+ makeThisReference(info, (ReferenceBinding) argType, false, scope);
+ call.addArg(thisRef);
+ }
+ }
+ }
+ }
+
+ // Plain old regular user arguments
+ call.addArgs(arguments);
+
+ // Synthetic args for inner classes
+ if (nestedBinding != null) {
+ // Synthetic locals for local classes
+ if (nestedBinding.outerLocalVariables != null) {
+ for (SyntheticArgumentBinding arg : nestedBinding.outerLocalVariables) {
+ 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 = false;
+
+ private static final char[] _STRING = "_String".toCharArray();
+ private static final String ARRAY_LENGTH_FIELD = "length";
+ 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();
+ }
+
+ 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 String intern(char[] cs) {
+ return intern(String.valueOf(cs));
+ }
+
+ static String intern(String s) {
+ return stringInterner.intern(s);
+ }
+
+ /**
+ * 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;
+ }
+
+ 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<MethodDeclaration, JsniMethod> jsniMethods, Map<String, Binding> jsniRefs) {
+ if (cud.types == null) {
+ return Collections.emptyList();
+ }
+ 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(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 {
+ boolean isCompileTimeConstant =
+ binding.isStatic() && (binding.isFinal())
+ && (binding.constant() != Constant.NotAConstant) && (binding.type.isBaseType());
+ assert (type instanceof JPrimitiveType || !isCompileTimeConstant);
+
+ assert (!binding.isFinal() || !binding.isVolatile());
+ Disposition disposition;
+ if (isCompileTimeConstant) {
+ disposition = Disposition.COMPILE_TIME_CONSTANT;
+ } else if (binding.isFinal()) {
+ disposition = Disposition.FINAL;
+ } else if (binding.isVolatile()) {
+ disposition = Disposition.VOLATILE;
+ } else {
+ disposition = Disposition.NONE;
+ }
+
+ field =
+ new JField(info, intern(binding.name), enclosingType, type, binding.isStatic(),
+ disposition);
+ }
+ 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;
+ 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 (declaringClass.isNestedType() && !declaringClass.isStatic()) {
+ 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 (declaringClass.isNestedType() && !declaringClass.isStatic()) {
+ // 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.getEnclosingType().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;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java
new file mode 100644
index 0000000..e0e2612
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReferenceMapper.java
@@ -0,0 +1,378 @@
+/*
+ * 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.jjs.InternalCompilerException;
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
+import com.google.gwt.dev.jjs.ast.JArrayType;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JConstructor;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JEnumType;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JField.Disposition;
+import com.google.gwt.dev.jjs.ast.JInterfaceType;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JNullType;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JPrimitiveType;
+import com.google.gwt.dev.jjs.ast.JType;
+import com.google.gwt.dev.util.StringInterner;
+
+import org.eclipse.jdt.internal.compiler.impl.Constant;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+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.TypeBinding;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Creates unresolved references to types, fields, and methods.
+ */
+public class ReferenceMapper {
+
+ private final List<String> argNames = new ArrayList<String>();
+ private final Map<String, JField> fields = new HashMap<String, JField>();
+ private final Map<String, JMethod> methods = new HashMap<String, JMethod>();
+ private final Map<String, JField> sourceFields = new HashMap<String, JField>();
+ private final Map<String, JMethod> sourceMethods = new HashMap<String, JMethod>();
+ private final Map<String, JDeclaredType> sourceTypes = new HashMap<String, JDeclaredType>();
+ private final StringInterner stringInterner = StringInterner.get();
+ private final Map<String, JType> types = new HashMap<String, JType>();
+
+ {
+ put(JPrimitiveType.BOOLEAN, JPrimitiveType.BYTE, JPrimitiveType.CHAR, JPrimitiveType.DOUBLE,
+ JPrimitiveType.FLOAT, JPrimitiveType.INT, JPrimitiveType.LONG, JPrimitiveType.SHORT,
+ JPrimitiveType.VOID, JNullType.INSTANCE);
+ }
+
+ public void clearSource() {
+ sourceFields.clear();
+ sourceMethods.clear();
+ sourceTypes.clear();
+ }
+
+ public JField get(FieldBinding binding) {
+ binding = binding.original();
+ String key = signature(binding);
+ JField sourceField = sourceFields.get(key);
+ if (sourceField != null) {
+ assert !sourceField.getEnclosingType().isExternal();
+ return sourceField;
+ }
+ JField field = fields.get(key);
+ if (field == null) {
+ field = createField(binding);
+ assert field.getEnclosingType().isExternal();
+ fields.put(key, field);
+ }
+ return field;
+ }
+
+ public JMethod get(MethodBinding binding) {
+ binding = binding.original();
+ String key = signature(binding);
+ JMethod sourceMethod = sourceMethods.get(key);
+ if (sourceMethod != null) {
+ assert !sourceMethod.getEnclosingType().isExternal();
+ return sourceMethod;
+ }
+ JMethod method = methods.get(key);
+ if (method == null) {
+ if (binding.isConstructor()) {
+ method = createConstructor(SourceOrigin.UNKNOWN, binding);
+ } else {
+ method = createMethod(SourceOrigin.UNKNOWN, binding, null);
+ }
+ assert method.getEnclosingType().isExternal();
+ methods.put(key, method);
+ }
+ return method;
+ }
+
+ public JType get(TypeBinding binding) {
+ binding = binding.erasure();
+ String key = signature(binding);
+ JDeclaredType sourceType = sourceTypes.get(key);
+ if (sourceType != null) {
+ assert !sourceType.isExternal();
+ return sourceType;
+ }
+ JType type = types.get(key);
+ if (type == null) {
+ assert !(binding instanceof BaseTypeBinding);
+ if (binding instanceof ArrayBinding) {
+ ArrayBinding arrayBinding = (ArrayBinding) binding;
+ type = new JArrayType(get(arrayBinding.elementsType()));
+ } else {
+ ReferenceBinding refBinding = (ReferenceBinding) binding;
+ type = createType(refBinding);
+ if (type instanceof JClassType) {
+ ReferenceBinding superclass = refBinding.superclass();
+ if (superclass != null) {
+ ((JClassType) type).setSuperClass((JClassType) get(superclass));
+ }
+ }
+ if (type instanceof JDeclaredType) {
+ ReferenceBinding[] superInterfaces = refBinding.superInterfaces();
+ JDeclaredType declType = (JDeclaredType) type;
+ if (superInterfaces != null) {
+ for (ReferenceBinding intf : superInterfaces) {
+ declType.addImplements((JInterfaceType) get(intf));
+ }
+ }
+ declType.setExternal(true);
+ // Emulate clinit method for super clinit calls.
+ JMethod clinit =
+ new JMethod(SourceOrigin.UNKNOWN, "$clinit", declType, JPrimitiveType.VOID, false,
+ true, true, true);
+ clinit.freezeParamTypes();
+ clinit.setSynthetic();
+ declType.addMethod(clinit);
+ }
+ }
+ types.put(key, type);
+ }
+ return type;
+ }
+
+ public void setField(FieldBinding binding, JField field) {
+ String key = signature(binding);
+ sourceFields.put(key, field);
+ }
+
+ public void setMethod(MethodBinding binding, JMethod method) {
+ String key = signature(binding);
+ sourceMethods.put(key, method);
+ }
+
+ public void setSourceType(SourceTypeBinding binding, JDeclaredType type) {
+ String key = signature(binding);
+ sourceTypes.put(key, type);
+ }
+
+ JMethod createConstructor(SourceInfo info, MethodBinding b) {
+ JDeclaredType enclosingType = (JDeclaredType) get(b.declaringClass);
+ JMethod method = new JConstructor(info, (JClassType) enclosingType);
+ enclosingType.addMethod(method);
+
+ /*
+ * Don't need to synthesize enum intrinsic args because enum ctors can only
+ * be called locally.
+ */
+
+ int argPosition = 0;
+
+ ReferenceBinding declaringClass = b.declaringClass;
+ if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
+ // add synthetic args for outer this
+ if (declaringClass.syntheticEnclosingInstanceTypes() != null) {
+ for (ReferenceBinding argType : declaringClass.syntheticEnclosingInstanceTypes()) {
+ createParameter(info, argType, method, argPosition++);
+ }
+ }
+ }
+
+ // User args.
+ argPosition = mapParameters(info, method, b, argPosition);
+
+ if (declaringClass.isNestedType() && !declaringClass.isStatic()) {
+ // add synthetic args for locals
+ if (declaringClass.syntheticOuterLocalVariables() != null) {
+ for (SyntheticArgumentBinding arg : declaringClass.syntheticOuterLocalVariables()) {
+ createParameter(info, arg.type, method, argPosition++);
+ }
+ }
+ }
+
+ mapExceptions(method, b);
+ if (b.isSynthetic()) {
+ method.setSynthetic();
+ }
+ return method;
+ }
+
+ JMethod createMethod(SourceInfo info, MethodBinding b, String[] paramNames) {
+ JDeclaredType enclosingType = (JDeclaredType) get(b.declaringClass);
+ JMethod method =
+ new JMethod(info, intern(b.selector), enclosingType, get(b.returnType), b.isAbstract(), b
+ .isStatic(), b.isFinal(), b.isPrivate());
+ enclosingType.addMethod(method);
+ if (paramNames == null) {
+ mapParameters(info, method, b, 0);
+ } else {
+ mapParameters(info, method, b, paramNames);
+ }
+ mapExceptions(method, b);
+ if (b.isSynthetic()) {
+ method.setSynthetic();
+ }
+ return method;
+ }
+
+ private JField createField(FieldBinding binding) {
+ JDeclaredType enclosingType = (JDeclaredType) get(binding.declaringClass);
+
+ boolean isCompileTimeConstant =
+ binding.isStatic() && (binding.isFinal()) && (binding.constant() != Constant.NotAConstant)
+ && (binding.type.isBaseType());
+ assert (get(binding.type) instanceof JPrimitiveType || !isCompileTimeConstant);
+
+ assert (!binding.isFinal() || !binding.isVolatile());
+ Disposition disposition;
+ if (isCompileTimeConstant) {
+ disposition = Disposition.COMPILE_TIME_CONSTANT;
+ } else if (binding.isFinal()) {
+ disposition = Disposition.FINAL;
+ } else if (binding.isVolatile()) {
+ disposition = Disposition.VOLATILE;
+ } else {
+ disposition = Disposition.NONE;
+ }
+
+ JField field =
+ new JField(SourceOrigin.UNKNOWN, intern(binding.name), enclosingType, get(binding.type),
+ binding.isStatic(), disposition);
+ enclosingType.addField(field);
+ return field;
+ }
+
+ private JParameter createParameter(SourceInfo info, TypeBinding paramType,
+ JMethod enclosingMethod, int argPosition) {
+ JType type = get(paramType);
+ ensureArgNames(argPosition);
+ JParameter param =
+ new JParameter(info, argNames.get(argPosition), type, true, false, enclosingMethod);
+ enclosingMethod.addParam(param);
+ return param;
+ }
+
+ private JParameter createParameter(SourceInfo info, TypeBinding paramType,
+ JMethod enclosingMethod, String name) {
+ JParameter param = new JParameter(info, name, get(paramType), true, false, enclosingMethod);
+ enclosingMethod.addParam(param);
+ return param;
+ }
+
+ private JDeclaredType createType(ReferenceBinding binding) {
+ String name = GwtAstBuilder.dotify(binding.compoundName);
+ SourceInfo info = SourceOrigin.UNKNOWN;
+ if (binding.isClass()) {
+ return new JClassType(info, name, binding.isAbstract(), binding.isFinal());
+ } else if (binding.isInterface() || binding.isAnnotationType()) {
+ return new JInterfaceType(info, name);
+ } else if (binding.isEnum()) {
+ if (binding.isAnonymousType()) {
+ // Don't model an enum subclass as a JEnumType.
+ return new JClassType(info, name, false, true);
+ } else {
+ return new JEnumType(info, name, binding.isAbstract());
+ }
+ } else {
+ throw new InternalCompilerException("ReferenceBinding is not a class, interface, or enum.");
+ }
+ }
+
+ private void ensureArgNames(int required) {
+ for (int i = argNames.size(); i <= required; ++i) {
+ argNames.add(intern("arg" + i));
+ }
+ }
+
+ private String intern(char[] cs) {
+ return intern(String.valueOf(cs));
+ }
+
+ private String intern(String s) {
+ return stringInterner.intern(s);
+ }
+
+ private void mapExceptions(JMethod method, MethodBinding binding) {
+ for (ReferenceBinding thrownBinding : binding.thrownExceptions) {
+ JClassType type = (JClassType) get(thrownBinding);
+ method.addThrownException(type);
+ }
+ }
+
+ private int mapParameters(SourceInfo info, JMethod method, MethodBinding binding, int argPosition) {
+ if (binding.parameters != null) {
+ ensureArgNames(argPosition + binding.parameters.length);
+ for (TypeBinding argType : binding.parameters) {
+ createParameter(info, argType, method, argNames.get(argPosition++));
+ }
+ }
+ method.freezeParamTypes();
+ return argPosition;
+ }
+
+ private void mapParameters(SourceInfo info, JMethod method, MethodBinding binding,
+ String[] paramNames) {
+ if (binding.parameters != null) {
+ int i = 0;
+ for (TypeBinding argType : binding.parameters) {
+ createParameter(info, argType, method, paramNames[i++]);
+ }
+ }
+ method.freezeParamTypes();
+ }
+
+ private void put(JType... baseTypes) {
+ for (JType type : baseTypes) {
+ types.put(type.getName(), type);
+ }
+ }
+
+ private String signature(FieldBinding binding) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(binding.declaringClass.constantPoolName());
+ sb.append('.');
+ sb.append(binding.name);
+ sb.append(':');
+ sb.append(binding.type.constantPoolName());
+ return sb.toString();
+ }
+
+ private String signature(MethodBinding binding) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(binding.declaringClass.constantPoolName());
+ sb.append('.');
+ sb.append(binding.selector);
+ sb.append('(');
+ for (TypeBinding paramType : binding.parameters) {
+ sb.append(paramType.constantPoolName());
+ }
+ sb.append(')');
+ sb.append(binding.returnType.constantPoolName());
+ return sb.toString();
+ }
+
+ private String signature(TypeBinding binding) {
+ if (binding.isBaseType()) {
+ return String.valueOf(binding.sourceName());
+ } else {
+ return String.valueOf(binding.constantPoolName());
+ }
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java b/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java
index 18d7465..016e933 100644
--- a/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java
+++ b/dev/core/test/com/google/gwt/dev/javac/impl/JavaResourceBase.java
@@ -143,6 +143,8 @@
code.append("import java.io.Serializable;\n");
code.append("public abstract class Enum<E extends Enum<E>> implements Serializable {\n");
code.append(" protected Enum(String name, int ordinal) {}\n");
+ code.append(" protected static Object createValueOfMap(Enum[] constants) { return null; }\n");
+ code.append(" protected static Enum valueOf(Object map, String name) { return null; }\n");
code.append("}\n");
return code;
}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/PrunerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/PrunerTest.java
index bb44cd6..19ee872 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/PrunerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/PrunerTest.java
@@ -112,7 +112,7 @@
assertNull(result.findClass("UninstantiatedClass"));
assertEquals(
- "public static <null> returnUninstantiatedClass(){\n" +
+ "public static null returnUninstantiatedClass(){\n" +
" return null;\n" +
"}",
result.findMethod("returnUninstantiatedClass").toSource());
@@ -123,11 +123,11 @@
result.findMethod("methodWithUninstantiatedParam").toSource());
assertEquals(
- "[final <null> nullField, int field2]",
+ "[final null nullField, int field2]",
((JsniMethodBody) result.findMethod("usedNativeMethod").getBody()).
getJsniFieldRefs().toString());
assertEquals(
- "[public final <null> nullMethod(), public void method2()]",
+ "[public final null nullMethod(), public void method2()]",
((JsniMethodBody) result.findMethod("usedNativeMethod").getBody()).
getJsniMethodRefs().toString());
diff --git a/eclipse/samples/Hello/Hello-compModule.launch b/eclipse/samples/Hello/Hello-compModule.launch
new file mode 100644
index 0000000..48d2178
--- /dev/null
+++ b/eclipse/samples/Hello/Hello-compModule.launch
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/Hello"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER" javaProject="Hello" path="1" type="4"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/Hello/core/src" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/src" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/super" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-dev/core/super" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="Hello"/> </runtimeClasspathEntry> "/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.CompileModule"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="com.google.gwt.sample.hello.Hello"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Hello"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Xmx512M -Dgwt.devjar=${gwt_devjar} -Dgwt.speedtracerlog=log.html"/>
+</launchConfiguration>
diff --git a/user/build.xml b/user/build.xml
index 6d4a2dc..f8f3236 100755
--- a/user/build.xml
+++ b/user/build.xml
@@ -35,6 +35,9 @@
<property name="gwt.tck.testcase.dev.includes" value="com/google/gwt/validation/tck/**/*GwtSuite.class" />
<property name="gwt.tct.testcase.dev.excludes" value="" />
+ <property name="gwt.nongwt.testcase.includes" value="com/google/gwt/dev/jjs/GwtAstBuilderTest.class" />
+ <property name="gwt.nongwt.testcase.excludes" value="" />
+
<!--
Test args can be specified per test target type.
-->
@@ -522,6 +525,7 @@
<antcall target="test.web.htmlunit"/>
<antcall target="test.draft.htmlunit"/>
<antcall target="test.nometa.htmlunit"/>
+ <antcall target="test.nongwt"/>
</parallel>
</limit>
</target>
@@ -547,6 +551,24 @@
</limit>
</target>
+ <target name="test.nongwt"
+ depends="compile, compile.tests"
+ description="Run JRE-only tests."
+ unless="test.nongwt.disable">
+ <fileset id="test.nongwt.tests" dir="${javac.junit.out}"
+ includes="${gwt.nongwt.testcase.includes}"
+ excludes="${gwt.nongwt.testcase.excludes}" />
+ <gwt.junit test.name="test.nongwt"
+ test.args="${test.args}"
+ test.jvmargs="${test.jvmargs}"
+ test.out="${junit.out}/nongwt"
+ test.cases="test.nongwt.tests" >
+ <extraclasspaths>
+ <path refid="test.extraclasspath" />
+ </extraclasspaths>
+ </gwt.junit>
+ </target>
+
<target name="test.dev"
depends="compile, compile.tests"
description="Run dev-mode tests for this project.">
diff --git a/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java b/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java
new file mode 100644
index 0000000..6cc32c9
--- /dev/null
+++ b/user/test/com/google/gwt/dev/jjs/GwtAstBuilderTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 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;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.CompileModule;
+import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.CompilationUnit;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import junit.framework.TestCase;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Massive test for {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder}, uses
+ * CompilerSuite under the hood to test source compatibility between
+ * {@link com.google.gwt.dev.jjs.impl.GwtAstBuilder} and
+ * {@link com.google.gwt.dev.jjs.impl.GenerateJavaAST}.
+ */
+public class GwtAstBuilderTest extends TestCase {
+
+ private static TreeLogger createLogger() {
+ PrintWriterTreeLogger logger = new PrintWriterTreeLogger(new PrintWriter(System.err, true));
+ logger.setMaxDetail(TreeLogger.ERROR);
+ return logger;
+ }
+
+ public void testGwtAstBuilder() throws UnableToCompleteException {
+ TreeLogger logger = createLogger();
+ ModuleDef module =
+ ModuleDefLoader.createSyntheticModule(logger,
+ "com.google.gwt.dev.jjs.CompilerSuite.GwtAstBuilderTest", new String[]{
+ "com.google.gwt.junit.JUnit", "com.google.gwt.dev.jjs.CompilerSuite"}, false);
+ CompilationState compilationState = CompileModule.buildGwtAst(logger, module);
+ assertFalse(compilationState.hasErrors());
+ JProgram jprogram = CompileModule.buildGenerateJavaAst(logger, module, compilationState);
+
+ Map<String, JDeclaredType> compStateTypes = new HashMap<String, JDeclaredType>();
+ for (CompilationUnit unit : compilationState.getCompilationUnits()) {
+ for (JDeclaredType type : unit.getTypes()) {
+ compStateTypes.put(type.getName(), type);
+ }
+ }
+
+ for (JDeclaredType genJavaAstType : jprogram.getDeclaredTypes()) {
+ String typeName = genJavaAstType.getName();
+ if ("com.google.gwt.core.client.JavaScriptObject".equals(typeName)) {
+ // Known mismatch; genJavaAst version implements all JSO interfaces.
+ continue;
+ }
+ if (typeName.startsWith("com.google.gwt.lang.asyncloaders")) {
+ // GwtAstBuilder doesn't build these; added later.
+ continue;
+ }
+ if ("com.google.gwt.dev.jjs.test.B$1".equals(typeName)) {
+ // Known mismatch; genJavaAst is "wrong".
+ continue;
+ }
+ if (typeName.startsWith("com.google.gwt.dev.jjs.test.CoverageTest$Inner$1")) {
+ // Known mismatch; two different emulation paths do the same thing.
+ continue;
+ }
+ JDeclaredType compStateType = compStateTypes.get(typeName);
+ assertNotNull("No matching prebuilt type for '" + typeName + "'", compStateType);
+ String oldSource = genJavaAstType.toSource();
+ String newSource = compStateType.toSource();
+ assertEquals("Mismatched output for '" + typeName + "'", oldSource, newSource);
+ }
+ }
+}