Creates infrastructure to test individual compiler passes.
Also started some very basic tests for ControlFlowAnalyzer, ExpressionAnalyzer, and DeadCodeElimination.
Hopefully this will start the process of fixing broken windows and result in more test coverage in the future. :)
Review by: spoon, cromwellian
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5943 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jdt/BasicWebModeCompiler.java b/dev/core/src/com/google/gwt/dev/jdt/BasicWebModeCompiler.java
new file mode 100644
index 0000000..8937921
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jdt/BasicWebModeCompiler.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2008 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.jdt;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.CompilationUnit;
+import com.google.gwt.dev.javac.CompiledClass;
+import com.google.gwt.dev.javac.JdtCompiler.CompilationUnitAdapter;
+import com.google.gwt.dev.util.Empty;
+import com.google.gwt.dev.util.JsniRef;
+import com.google.gwt.dev.util.Memory;
+
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Provides a basic front-end based on the JDT compiler that incorporates
+ * GWT-specific concepts such as JSNI.
+ */
+public class BasicWebModeCompiler extends AbstractCompiler {
+
+ public static CompilationUnitDeclaration[] getCompilationUnitDeclarations(
+ TreeLogger logger, CompilationState state, String... seedTypeNames)
+ throws UnableToCompleteException {
+ return new BasicWebModeCompiler(state).getCompilationUnitDeclarations(
+ logger, seedTypeNames);
+ }
+
+ /**
+ * Construct a BasicWebModeCompiler.
+ */
+ public BasicWebModeCompiler(CompilationState compilationState) {
+ super(compilationState, false);
+ }
+
+ /**
+ * Build the initial set of compilation units.
+ */
+ public CompilationUnitDeclaration[] getCompilationUnitDeclarations(
+ TreeLogger logger, String[] seedTypeNames)
+ throws UnableToCompleteException {
+
+ TypeOracle oracle = compilationState.getTypeOracle();
+ Set<JClassType> intfTypes = oracle.getSingleJsoImplInterfaces();
+ Map<String, CompiledClass> classMapBySource = compilationState.getClassFileMapBySource();
+
+ /*
+ * The alreadyAdded set prevents duplicate CompilationUnits from being added
+ * to the icu list in the case of multiple JSO implementations as inner
+ * classes in the same top-level class or seed classes as SingleJsoImpls
+ * (e.g. JSO itself as the SingleImpl for all tag interfaces).
+ */
+ Set<CompilationUnit> alreadyAdded = new HashSet<CompilationUnit>();
+
+ List<ICompilationUnit> icus = new ArrayList<ICompilationUnit>(
+ seedTypeNames.length + intfTypes.size());
+
+ for (String seedTypeName : seedTypeNames) {
+ CompilationUnit unit = getUnitForType(logger, classMapBySource,
+ seedTypeName);
+
+ if (alreadyAdded.add(unit)) {
+ icus.add(new CompilationUnitAdapter(unit));
+ } else {
+ logger.log(TreeLogger.WARN, "Duplicate compilation unit '"
+ + unit.getDisplayLocation() + "'in seed types");
+ }
+ }
+
+ /*
+ * Add all SingleJsoImpl types that we know about. It's likely that the
+ * concrete types are never explicitly referenced from the seed types.
+ */
+ for (JClassType intf : intfTypes) {
+ String implName = oracle.getSingleJsoImpl(intf).getQualifiedSourceName();
+ CompilationUnit unit = getUnitForType(logger, classMapBySource, implName);
+
+ if (alreadyAdded.add(unit)) {
+ icus.add(new CompilationUnitAdapter(unit));
+ logger.log(TreeLogger.SPAM, "Forced compilation of unit '"
+ + unit.getDisplayLocation()
+ + "' becasue it contains a SingleJsoImpl type");
+ }
+ }
+
+ /*
+ * Compile, which will pull in everything else via the
+ * doFindAdditionalTypesUsingFoo() methods.
+ */
+ CompilationUnitDeclaration[] cuds = compile(logger,
+ icus.toArray(new ICompilationUnit[icus.size()]));
+ Memory.maybeDumpMemory("WebModeCompiler");
+ return cuds;
+ }
+
+ /**
+ * Pull in types referenced only via JSNI.
+ */
+ @Override
+ protected String[] doFindAdditionalTypesUsingJsni(TreeLogger logger,
+ CompilationUnitDeclaration cud) {
+ FindJsniRefVisitor v = new FindJsniRefVisitor();
+ cud.traverse(v, cud.scope);
+ Set<String> jsniRefs = v.getJsniRefs();
+ Set<String> dependentTypeNames = new HashSet<String>();
+ for (String jsniRef : jsniRefs) {
+ JsniRef parsed = JsniRef.parse(jsniRef);
+ if (parsed != null) {
+ // If we fail to parse, don't add a class reference.
+ dependentTypeNames.add(parsed.className());
+ }
+ }
+ return dependentTypeNames.toArray(Empty.STRINGS);
+ }
+
+ /**
+ * Get the CompilationUnit for a named type or throw an
+ * UnableToCompleteException.
+ */
+ private CompilationUnit getUnitForType(TreeLogger logger,
+ Map<String, CompiledClass> classMapBySource, String typeName)
+ throws UnableToCompleteException {
+
+ CompiledClass compiledClass = classMapBySource.get(typeName);
+ if (compiledClass == null) {
+ logger.log(TreeLogger.ERROR, "Unable to find compilation unit for type '"
+ + typeName + "'");
+ throw new UnableToCompleteException();
+ }
+
+ assert compiledClass.getUnit() != null;
+ return compiledClass.getUnit();
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
index e9925b3..77b7665 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/WebModeCompilerFrontEnd.java
@@ -17,26 +17,17 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.typeinfo.JClassType;
-import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.javac.ArtificialRescueChecker;
import com.google.gwt.dev.javac.CompilationState;
-import com.google.gwt.dev.javac.CompilationUnit;
-import com.google.gwt.dev.javac.CompiledClass;
-import com.google.gwt.dev.javac.JdtCompiler.CompilationUnitAdapter;
import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor.MessageSendSite;
import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
import com.google.gwt.dev.util.Empty;
-import com.google.gwt.dev.util.JsniRef;
-import com.google.gwt.dev.util.Memory;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
-import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -48,7 +39,7 @@
* Provides a reusable front-end based on the JDT compiler that incorporates
* GWT-specific concepts such as JSNI and deferred binding.
*/
-public class WebModeCompilerFrontEnd extends AbstractCompiler {
+public class WebModeCompilerFrontEnd extends BasicWebModeCompiler {
public static CompilationUnitDeclaration[] getCompilationUnitDeclarations(
TreeLogger logger, String[] seedTypeNames,
@@ -70,72 +61,12 @@
*/
private WebModeCompilerFrontEnd(CompilationState compilationState,
RebindPermutationOracle rebindPermOracle) {
- super(compilationState, false);
+ super(compilationState);
this.rebindPermOracle = rebindPermOracle;
this.fragmentLoaderCreator = new FragmentLoaderCreator(
rebindPermOracle.getGeneratorContext());
}
- /**
- * Build the initial set of compilation units.
- */
- public CompilationUnitDeclaration[] getCompilationUnitDeclarations(
- TreeLogger logger, String[] seedTypeNames)
- throws UnableToCompleteException {
-
- TypeOracle oracle = compilationState.getTypeOracle();
- Set<JClassType> intfTypes = oracle.getSingleJsoImplInterfaces();
- Map<String, CompiledClass> classMapBySource = compilationState.getClassFileMapBySource();
-
- /*
- * The alreadyAdded set prevents duplicate CompilationUnits from being added
- * to the icu list in the case of multiple JSO implementations as inner
- * classes in the same top-level class or seed classes as SingleJsoImpls
- * (e.g. JSO itself as the SingleImpl for all tag interfaces).
- */
- Set<CompilationUnit> alreadyAdded = new HashSet<CompilationUnit>();
-
- List<ICompilationUnit> icus = new ArrayList<ICompilationUnit>(
- seedTypeNames.length + intfTypes.size());
-
- for (String seedTypeName : seedTypeNames) {
- CompilationUnit unit = getUnitForType(logger, classMapBySource,
- seedTypeName);
-
- if (alreadyAdded.add(unit)) {
- icus.add(new CompilationUnitAdapter(unit));
- } else {
- logger.log(TreeLogger.WARN, "Duplicate compilation unit '"
- + unit.getDisplayLocation() + "'in seed types");
- }
- }
-
- /*
- * Add all SingleJsoImpl types that we know about. It's likely that the
- * concrete types are never explicitly referenced from the seed types.
- */
- for (JClassType intf : intfTypes) {
- String implName = oracle.getSingleJsoImpl(intf).getQualifiedSourceName();
- CompilationUnit unit = getUnitForType(logger, classMapBySource, implName);
-
- if (alreadyAdded.add(unit)) {
- icus.add(new CompilationUnitAdapter(unit));
- logger.log(TreeLogger.SPAM, "Forced compilation of unit '"
- + unit.getDisplayLocation()
- + "' becasue it contains a SingleJsoImpl type");
- }
- }
-
- /*
- * Compile, which will pull in everything else via
- * doFindAdditionalTypesUsingMagic()
- */
- CompilationUnitDeclaration[] cuds = compile(logger,
- icus.toArray(new ICompilationUnit[icus.size()]));
- Memory.maybeDumpMemory("WebModeCompiler");
- return cuds;
- }
-
@Override
protected void doCompilationUnitDeclarationValidation(
CompilationUnitDeclaration cud, TreeLogger logger) {
@@ -154,26 +85,6 @@
}
/**
- * Pull in types referenced only via JSNI.
- */
- @Override
- protected String[] doFindAdditionalTypesUsingJsni(TreeLogger logger,
- CompilationUnitDeclaration cud) {
- FindJsniRefVisitor v = new FindJsniRefVisitor();
- cud.traverse(v, cud.scope);
- Set<String> jsniRefs = v.getJsniRefs();
- Set<String> dependentTypeNames = new HashSet<String>();
- for (String jsniRef : jsniRefs) {
- JsniRef parsed = JsniRef.parse(jsniRef);
- if (parsed != null) {
- // If we fail to parse, don't add a class reference.
- dependentTypeNames.add(parsed.className());
- }
- }
- return dependentTypeNames.toArray(Empty.STRINGS);
- }
-
- /**
* Pull in types implicitly referenced through rebind answers.
*/
@Override
@@ -291,23 +202,4 @@
return;
}
}
-
- /**
- * Get the CompilationUnit for a named type or throw an
- * UnableToCompleteException.
- */
- private CompilationUnit getUnitForType(TreeLogger logger,
- Map<String, CompiledClass> classMapBySource, String typeName)
- throws UnableToCompleteException {
-
- CompiledClass compiledClass = classMapBySource.get(typeName);
- if (compiledClass == null) {
- logger.log(TreeLogger.ERROR, "Unable to find compilation unit for type '"
- + typeName + "'");
- throw new UnableToCompleteException();
- }
-
- assert compiledClass.getUnit() != null;
- return compiledClass.getUnit();
- }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 02b1199..538823d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -615,7 +615,7 @@
return didChange;
}
- private static void checkForErrors(TreeLogger logger,
+ static void checkForErrors(TreeLogger logger,
CompilationUnitDeclaration[] cuds, boolean itemizeErrors)
throws UnableToCompleteException {
boolean compilationFailed = false;
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 1eaede2..1da0628 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
@@ -95,6 +95,7 @@
code.append("public class Object {\n");
code.append(" public String toString() { return \"Object\"; }\n");
code.append(" public Object clone() { return this; } ");
+ code.append(" public Class getClass() { return Object.class; } ");
code.append("}\n");
return code;
}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaResource.java b/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaResource.java
index 2f3de7a..3b003e7 100644
--- a/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaResource.java
+++ b/dev/core/test/com/google/gwt/dev/javac/impl/MockJavaResource.java
@@ -17,7 +17,14 @@
public abstract class MockJavaResource extends MockResource {
+ private final String qualifiedTypeName;
+
public MockJavaResource(String qualifiedTypeName) {
super(Shared.toPath(qualifiedTypeName));
+ this.qualifiedTypeName = qualifiedTypeName;
+ }
+
+ public String getTypeName() {
+ return qualifiedTypeName;
}
}
diff --git a/dev/core/test/com/google/gwt/dev/javac/impl/MockResourceOracle.java b/dev/core/test/com/google/gwt/dev/javac/impl/MockResourceOracle.java
index 22383fe..a280b1b 100644
--- a/dev/core/test/com/google/gwt/dev/javac/impl/MockResourceOracle.java
+++ b/dev/core/test/com/google/gwt/dev/javac/impl/MockResourceOracle.java
@@ -48,6 +48,14 @@
export(newMap);
}
+ public void addOrReplace(Resource... resources) {
+ Map<String, Resource> newMap = new HashMap<String, Resource>(exportedMap);
+ for (Resource resource : resources) {
+ newMap.put(resource.getPath(), resource);
+ }
+ export(newMap);
+ }
+
public void clear() {
}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
new file mode 100644
index 0000000..2684691
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2008 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.javac.CompilationState;
+import com.google.gwt.dev.javac.impl.JavaResourceBase;
+import com.google.gwt.dev.javac.impl.MockJavaResource;
+import com.google.gwt.dev.jdt.BasicWebModeCompiler;
+import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.impl.AssertionNormalizer;
+import com.google.gwt.dev.jjs.impl.BuildTypeMap;
+import com.google.gwt.dev.jjs.impl.FixAssignmentToUnbox;
+import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
+import com.google.gwt.dev.jjs.impl.JavaScriptObjectNormalizer;
+import com.google.gwt.dev.jjs.impl.TypeMap;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.util.Empty;
+
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Constructs a Java AST for testing.
+ */
+public class JavaAstConstructor {
+
+ public static final MockJavaResource ARRAY = new MockJavaResource(
+ "com.google.gwt.lang.Array") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.lang;\n");
+ code.append("public final class Array {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource CLASS = new MockJavaResource(
+ "java.lang.Class") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package java.lang;\n");
+ code.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+ code.append("public final class Class<T> {\n");
+ code.append(" static <T> Class<T> createForArray(String packageName, String className, String seedName, Class<?> componentType) { return new Class<T>(); }\n");
+ code.append(" static <T> Class<T> createForClass(String packageName, String className, String seedName, Class<? super T> superclass) { return new Class<T>(); }\n");
+ code.append(" static <T> Class<T> createForEnum(String packageName, String className, String seedName, Class<? super T> superclass, JavaScriptObject enumConstantsFunc) { return new Class<T>(); }\n");
+ code.append(" static <T> Class<T> createForInterface(String packageName, String className) { return new Class<T>(); }\n");
+ code.append(" static boolean isClassMetadataEnabled() { return true; }\n");
+ code.append(" public boolean desiredAssertionStatus() { return true; }\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource CLASSLITERALHOLDER = new MockJavaResource(
+ "com.google.gwt.lang.ClassLiteralHolder") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.lang;\n");
+ code.append("final class ClassLiteralHolder {\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource GWT = new MockJavaResource(
+ "com.google.gwt.core.client.GWT") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.core.client;\n");
+ code.append("public final class GWT {\n");
+ code.append(" public boolean isClient() { return true; };\n");
+ code.append(" public boolean isScript() { return true; };\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+ public static final MockJavaResource STATS = new MockJavaResource(
+ "com.google.gwt.lang.Stats") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package com.google.gwt.lang;\n");
+ code.append("public class Stats {\n");
+ code.append(" public boolean isStatsAvailable() { return false; };\n");
+ code.append("}\n");
+ return code;
+ }
+ };
+
+ public static JProgram construct(TreeLogger logger, CompilationState state,
+ String... entryPoints) throws UnableToCompleteException {
+
+ Set<String> allRootTypes = new TreeSet<String>(Arrays.asList(entryPoints));
+ for (MockJavaResource resource : getCompilerTypes()) {
+ allRootTypes.add(resource.getTypeName());
+ }
+
+ CompilationUnitDeclaration[] goldenCuds = BasicWebModeCompiler.getCompilationUnitDeclarations(
+ logger, state, allRootTypes.toArray(Empty.STRINGS));
+
+ // Check for compilation problems. We don't log here because any problems
+ // found here will have already been logged by AbstractCompiler.
+ //
+ JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, false);
+
+ CorrelationFactory correlator = new DummyCorrelationFactory();
+ JProgram jprogram = new JProgram(correlator);
+ JsProgram jsProgram = new JsProgram(correlator);
+
+ /*
+ * (1) Build a flattened map of TypeDeclarations => JType. The resulting map
+ * contains entries for all reference types. BuildTypeMap also parses all
+ * JSNI.
+ */
+ TypeMap typeMap = new TypeMap(jprogram);
+ TypeDeclaration[] allTypeDeclarations = BuildTypeMap.exec(typeMap,
+ goldenCuds, jsProgram);
+
+ // BuildTypeMap can uncover syntactic JSNI errors; report & abort
+ JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, true);
+
+ // Compute all super type/sub type info
+ jprogram.typeOracle.computeBeforeAST();
+
+ // (2) Create our own Java AST from the JDT AST.
+ JJSOptionsImpl options = new JJSOptionsImpl();
+ options.setEnableAssertions(true);
+ GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, jsProgram,
+ options);
+
+ // GenerateJavaAST can uncover semantic JSNI errors; report & abort
+ JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, true);
+
+ // (3) Perform Java AST normalizations.
+ FixAssignmentToUnbox.exec(jprogram);
+ // Turn into assertion checking calls.
+ AssertionNormalizer.exec(jprogram);
+
+ // Add entry methods for entry points.
+ for (String entryPoint : entryPoints) {
+ JDeclaredType entryType = jprogram.getFromTypeMap(entryPoint);
+ for (JMethod method : entryType.getMethods()) {
+ if (method.isStatic() && JProgram.isClinit(method)) {
+ jprogram.addEntryMethod(method);
+ }
+ }
+ }
+ // Replace references to JSO subtypes with JSO itself.
+ JavaScriptObjectNormalizer.exec(jprogram);
+
+ // Tree is now ready to optimize.
+ return jprogram;
+ }
+
+ public static MockJavaResource[] getCompilerTypes() {
+ List<MockJavaResource> result = new ArrayList<MockJavaResource>();
+ Collections.addAll(result, JavaResourceBase.getStandardResources());
+ // Replace the basic Class with a compiler-specific one.
+ result.remove(JavaResourceBase.CLASS);
+ Collections.addAll(result, ARRAY, CLASS, CLASSLITERALHOLDER, GWT);
+ return result.toArray(new MockJavaResource[result.size()]);
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzerTest.java
new file mode 100644
index 0000000..c02e02b
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzerTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2008 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.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.javac.impl.MockJavaResource;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.util.Empty;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests {@link ControlFlowAnalyzer}.
+ */
+public class ControlFlowAnalyzerTest extends OptimizerTestBase {
+
+ /**
+ * Answers predicates about an analyzed program.
+ */
+ private static final class Result {
+ private final ControlFlowAnalyzer cfa;
+ private final JProgram program;
+
+ public Result(JProgram program, ControlFlowAnalyzer cfa) {
+ this.program = program;
+ this.cfa = cfa;
+ }
+
+ public void assertOnlyFieldsWritten(String... expectedFields) {
+ Set<JField> expectedSet = new HashSet<JField>();
+ for (String expectedField : expectedFields) {
+ JField field = findField(program, expectedField);
+ assertNotNull(field);
+ expectedSet.add(field);
+ }
+ assertEquals(expectedSet, cfa.getFieldsWritten());
+ }
+
+ public void assertOnlyInstantiatedTypes(String... expectedTypes) {
+ Set<JDeclaredType> expectedSet = new HashSet<JDeclaredType>();
+ for (String expectedType : expectedTypes) {
+ JDeclaredType type = findType(program, expectedType);
+ assertNotNull(type);
+ expectedSet.add(type);
+ }
+ assertEquals(expectedSet, cfa.getInstantiatedTypes());
+ }
+
+ public void assertOnlyLiveStrings(String... expectedStrings) {
+ Set<String> expectedSet = new HashSet<String>();
+ Collections.addAll(expectedSet, expectedStrings);
+ cfa.getLiveStrings();
+ assertEquals(expectedSet, cfa.getLiveStrings());
+ }
+ }
+
+ /**
+ * Tests properties of an empty program.
+ */
+ public void testEmpty() throws Exception {
+ Result result = analyzeSnippet("");
+ result.assertOnlyFieldsWritten(Empty.STRINGS);
+ result.assertOnlyInstantiatedTypes(Empty.STRINGS);
+ result.assertOnlyLiveStrings(Empty.STRINGS);
+ }
+
+ /**
+ * Tests that the JavaScriptObject type gets rescued in the three specific
+ * circumstances where values can pass from JS into Java.
+ */
+ public void testRescueJavaScriptObjectFromJsni() throws Exception {
+ sourceOracle.addOrReplace(new MockJavaResource("test.Foo") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ code.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+ code.append("public class Foo {\n");
+ code.append(" public static native JavaScriptObject returnsJso() /*-{ return {}; }-*/;\n");
+ code.append(" public static native void assignsJsoField() /*-{ @test.Foo::jsoField = {}; }-*/;\n");
+ code.append(" public static native void readsJsoField() /*-{ var x = @test.Foo::jsoField; }-*/;\n");
+ code.append(" public static native void passesJsoParam() /*-{ @test.Foo::calledFromJsni(Lcom/google/gwt/core/client/JavaScriptObject;)({}); }-*/;\n");
+ code.append(" private static JavaScriptObject jsoField = null;\n");
+ code.append(" private static void calledFromJsni(JavaScriptObject arg) { }\n");
+ code.append("}\n");
+ return code;
+ }
+ });
+ addSnippetImport("test.Foo");
+
+ analyzeSnippet("").assertOnlyInstantiatedTypes(Empty.STRINGS);
+
+ // Returning a JSO from a JSNI method rescues.
+ analyzeSnippet("Foo.returnsJso();").assertOnlyInstantiatedTypes(
+ "JavaScriptObject", "Object");
+
+ // Assigning into a JSO field from a JSNI method rescues.
+ analyzeSnippet("Foo.assignsJsoField();").assertOnlyInstantiatedTypes(
+ "JavaScriptObject", "Object");
+
+ // Passing from Java to JS via a JSNI field read should NOT rescue.
+ analyzeSnippet("Foo.readsJsoField();").assertOnlyInstantiatedTypes(
+ Empty.STRINGS);
+
+ // Passing a parameter from JS to Java rescues.
+ analyzeSnippet("Foo.passesJsoParam();").assertOnlyInstantiatedTypes(
+ "JavaScriptObject", "Object");
+ }
+
+ private Result analyzeSnippet(String codeSnippet)
+ throws UnableToCompleteException {
+ JProgram program = compileSnippet("void", codeSnippet);
+ ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(program);
+ cfa.traverseFrom(findMainMethod(program));
+ return new Result(program, cfa);
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java
new file mode 100644
index 0000000..b26f401
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/DeadCodeEliminationTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2008 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.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.jjs.ast.JProgram;
+
+/**
+ * Tests {@link DeadCodeElimination}.
+ */
+public class DeadCodeEliminationTest extends OptimizerTestBase {
+
+ private final class Result {
+ private final String optimized;
+ private final String returnType;
+ private final String userCode;
+
+ public Result(String returnType, String userCode, String optimized) {
+ this.returnType = returnType;
+ this.userCode = userCode;
+ this.optimized = optimized;
+ }
+
+ public void into(String expected) throws UnableToCompleteException {
+ JProgram program = compileSnippet(returnType, expected);
+ expected = getMainMethodSource(program);
+ assertEquals(userCode, expected, optimized);
+ }
+ }
+
+ public void testConditionalOptimizations() throws Exception {
+ optimize("int", "return true ? 3 : 4;").into("return 3;");
+ optimize("int", "return false ? 3 : 4;").into("return 4;");
+
+ addSnippetClassDecl("static volatile boolean TEST");
+ addSnippetClassDecl("static volatile boolean RESULT");
+
+ optimize("boolean", "return TEST ? true : RESULT;").into(
+ "return TEST || RESULT;");
+
+ optimize("boolean", "return TEST ? false : RESULT;").into(
+ "return !TEST && RESULT;");
+
+ optimize("boolean", "return TEST ? RESULT : true;").into(
+ "return !TEST || RESULT;");
+
+ optimize("boolean", "return TEST ? RESULT : false;").into(
+ "return TEST && RESULT;");
+ }
+
+ public void testIfOptimizations() throws Exception {
+ optimize("int", "if (true) return 1; return 0;").into("return 1;");
+ optimize("int", "if (false) return 1; return 0;").into("return 0;");
+ optimize("int", "if (true) return 1; else return 2;").into("return 1;");
+ optimize("int", "if (false) return 1; else return 2;").into("return 2;");
+
+ optimize("int", "if (true) {} else return 4; return 0;").into("return 0;");
+
+ addSnippetClassDecl("static volatile boolean TEST");
+ addSnippetClassDecl("static boolean test() { return TEST; }");
+ optimize("int", "if (test()) {} else {}; return 0;").into(
+ "test(); return 0;");
+ }
+
+ private Result optimize(final String returnType, final String codeSnippet)
+ throws UnableToCompleteException {
+ JProgram program = compileSnippet(returnType, codeSnippet);
+ DeadCodeElimination.exec(program);
+ return new Result(returnType, codeSnippet, getMainMethodSource(program));
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/ExpressionAnalyzerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/ExpressionAnalyzerTest.java
new file mode 100644
index 0000000..47bc7b3
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/ExpressionAnalyzerTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2008 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.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.javac.impl.MockJavaResource;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodBody;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReturnStatement;
+
+/**
+ * Tests {@link ExpressionAnalyzer}.
+ */
+public class ExpressionAnalyzerTest extends OptimizerTestBase {
+
+ private static class Result {
+ private final ExpressionAnalyzer ea;
+ private boolean accessesField;
+ private boolean accessesFieldNonFinal;
+ private boolean accessesLocal;
+ private boolean accessesParameter;
+ private boolean canThrowException;
+ private boolean createsObject;
+ private boolean hasAssignment;
+ private boolean hasAssignmentToField;
+ private boolean hasAssignmentToLocal;
+ private boolean hasAssignmentToParameter;
+
+ public Result(ExpressionAnalyzer ea) {
+ this.ea = ea;
+ }
+
+ public Result accessesField() {
+ accessesField = true;
+ return this;
+ }
+
+ public Result accessesFieldNonFinal() {
+ accessesField = true;
+ accessesFieldNonFinal = true;
+ return this;
+ }
+
+ public Result accessesLocal() {
+ accessesLocal = true;
+ return this;
+ }
+
+ public Result accessesParameter() {
+ accessesParameter = true;
+ return this;
+ }
+
+ public Result canThrowException() {
+ canThrowException = true;
+ return this;
+ }
+
+ public Result createsObject() {
+ createsObject = true;
+ return this;
+ }
+
+ public Result hasAssignment() {
+ hasAssignment = true;
+ return this;
+ }
+
+ public Result hasAssignmentToField() {
+ hasAssignment = true;
+ hasAssignmentToField = true;
+ return this;
+ }
+
+ public Result hasAssignmentToLocal() {
+ hasAssignment = true;
+ hasAssignmentToLocal = true;
+ return this;
+ }
+
+ public Result hasAssignmentToParameter() {
+ hasAssignment = true;
+ hasAssignmentToParameter = true;
+ return this;
+ }
+
+ public void check() {
+ assertEquals(accessesField, ea.accessesField());
+ assertEquals(accessesFieldNonFinal, ea.accessesFieldNonFinal());
+ assertEquals(accessesLocal, ea.accessesLocal());
+ assertEquals(accessesParameter, ea.accessesParameter());
+ assertEquals(canThrowException, ea.canThrowException());
+ assertEquals(createsObject, ea.createsObject());
+ assertEquals(hasAssignment, ea.hasAssignment());
+ assertEquals(hasAssignmentToField, ea.hasAssignmentToField());
+ assertEquals(hasAssignmentToLocal, ea.hasAssignmentToLocal());
+ assertEquals(hasAssignmentToParameter, ea.hasAssignmentToParameter());
+ }
+ }
+
+ public void testEmpty() throws Exception {
+ analyzeExpression("int", "0").check();
+ }
+
+ public void testFieldAccessInstance() throws Exception {
+ sourceOracle.addOrReplace(new MockJavaResource("test.Foo") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ code.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+ code.append("public class Foo {\n");
+ code.append(" public final boolean BOOL_CONST = true;\n");
+ code.append(" public boolean FOO = true;\n");
+ code.append("}\n");
+ return code;
+ }
+ });
+ addSnippetImport("test.Foo");
+ addSnippetClassDecl("static final Foo f = new Foo();");
+
+ analyzeExpression("boolean", "f.BOOL_CONST").accessesField().canThrowException().check();
+
+ analyzeExpression("boolean", "f.FOO").accessesFieldNonFinal().canThrowException().check();
+
+ analyzeExpression("boolean", "f.FOO = false").accessesFieldNonFinal().canThrowException().hasAssignment().hasAssignmentToField().check();
+ }
+
+ public void testFieldAccessStatic() throws Exception {
+ addSnippetClassDecl("static boolean trueMethod() { return true; }");
+ addSnippetClassDecl("static final boolean BOOL_CONST = trueMethod();");
+ addSnippetClassDecl("static volatile boolean FOO;");
+
+ analyzeExpression("boolean", "BOOL_CONST").accessesField().check();
+
+ analyzeExpression("boolean", "FOO").accessesFieldNonFinal().check();
+
+ analyzeExpression("boolean", "FOO = false").accessesFieldNonFinal().hasAssignmentToField().check();
+ }
+
+ private Result analyzeExpression(String type, String expression)
+ throws UnableToCompleteException {
+ JProgram program = compileSnippet(type, "return " + expression + ";");
+ ExpressionAnalyzer ea = new ExpressionAnalyzer();
+ JMethod mainMethod = findMainMethod(program);
+ JMethodBody body = (JMethodBody) mainMethod.getBody();
+ JReturnStatement returnStmt = (JReturnStatement) body.getStatements().get(0);
+ ea.accept(returnStmt.getExpr());
+ return new Result(ea);
+ }
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java b/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java
new file mode 100644
index 0000000..9ef05dc
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2008 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.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.impl.MockJavaResource;
+import com.google.gwt.dev.javac.impl.MockResourceOracle;
+import com.google.gwt.dev.jjs.JavaAstConstructor;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.util.log.AbstractTreeLogger;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import junit.framework.TestCase;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Tests {@link DeadCodeElimination}.
+ */
+public abstract class OptimizerTestBase extends TestCase {
+
+ /**
+ * Finds a field with a type.
+ */
+ public static JField findField(JDeclaredType type, String fieldName) {
+ for (JField field : type.getFields()) {
+ if (field.getName().equals(fieldName)) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds a field by name, e.g. <code>Foo.field</code>.
+ */
+ public static JField findField(JProgram program, String qualifiedFieldName) {
+ int pos = qualifiedFieldName.lastIndexOf('.');
+ assertTrue(pos > 0);
+ String typeName = qualifiedFieldName.substring(0, pos);
+ String fieldName = qualifiedFieldName.substring(pos + 1);
+ JDeclaredType type = findType(program, typeName);
+ JField field = findField(type, fieldName);
+ return field;
+ }
+
+ public static JMethod findMainMethod(JProgram program) {
+ JDeclaredType mainType = program.getFromTypeMap("test.EntryPoint");
+ JMethod mainMethod = null;
+ for (JMethod method : mainType.getMethods()) {
+ if (method.getName().equals("onModuleLoad")) {
+ mainMethod = method;
+ break;
+ }
+ }
+ return mainMethod;
+ }
+
+ /**
+ * Finds a type by name. The type name may be short, e.g. <code>"Foo"</code>,
+ * or fully-qualified, e.g. <code>"com.google.example.Foo"</code>. If a
+ * short name is used, it must be unambiguous.
+ */
+ public static JDeclaredType findType(JProgram program, String typeName) {
+ JDeclaredType type = program.getFromTypeMap(typeName);
+ if (type == null && typeName.indexOf('.') < 0) {
+ // Do a slow lookup by short name.
+ for (JDeclaredType checkType : program.getDeclaredTypes()) {
+ if (checkType.getShortName().equals(typeName)) {
+ if (type == null) {
+ type = checkType;
+ } else {
+ fail("Ambiguous type reference '" + typeName + "' might be '"
+ + type.getName() + "' or '" + checkType.getName()
+ + "' (possibly more matches)");
+ }
+ }
+ }
+ }
+ return type;
+ }
+
+ public static String getMainMethodSource(JProgram program) {
+ JMethod mainMethod = findMainMethod(program);
+ return mainMethod.getBody().toSource();
+ }
+
+ /**
+ * Tweak this if you want to see the log output.
+ */
+ private static TreeLogger createTreeLogger() {
+ boolean reallyLog = true;
+ if (reallyLog) {
+ AbstractTreeLogger logger = new PrintWriterTreeLogger();
+ logger.setMaxDetail(TreeLogger.WARN);
+ return logger;
+ }
+ return TreeLogger.NULL;
+ }
+
+ protected final MockResourceOracle sourceOracle = new MockResourceOracle();
+
+ private final TreeLogger logger = createTreeLogger();
+
+ private final Set<String> snippetClassDecls = new TreeSet<String>();
+
+ private final Set<String> snippetImports = new TreeSet<String>();
+
+ private final CompilationState state = new CompilationState(logger,
+ sourceOracle);
+
+ public OptimizerTestBase() {
+ sourceOracle.add(JavaAstConstructor.getCompilerTypes());
+ }
+
+ /**
+ * Adds an import statement for any code subsequently passed to
+ * {@link #compileSnippet(String, String)}.
+ */
+ protected void addSnippetImport(String typeName) {
+ snippetImports.add(typeName);
+ }
+
+ /**
+ * Adds a local field declaration for code subsequently passed to
+ * {@link #compileSnippet(String, String)}.
+ */
+ protected void addSnippetClassDecl(String fieldDecl) {
+ snippetClassDecls.add(fieldDecl);
+ }
+
+ /**
+ * Returns the program that results from compiling the specified code snippet
+ * as the body of an entry point method.
+ *
+ * @param returnType the return type of the method to compile; use "void" if
+ * the code snippet has no return statement
+ * @param codeSnippet the body of the entry method
+ */
+ protected JProgram compileSnippet(final String returnType,
+ final String codeSnippet) throws UnableToCompleteException {
+ sourceOracle.addOrReplace(new MockJavaResource("test.EntryPoint") {
+ @Override
+ protected CharSequence getContent() {
+ StringBuffer code = new StringBuffer();
+ code.append("package test;\n");
+ for (String snippetImport : snippetImports) {
+ code.append("import " + snippetImport + ";\n");
+ }
+ code.append("public class EntryPoint {\n");
+ for (String snippetClassDecl : snippetClassDecls) {
+ code.append(snippetClassDecl + ";\n");
+ }
+ code.append(" public static " + returnType + " onModuleLoad() {\n");
+ code.append(codeSnippet);
+ code.append(" }\n");
+ code.append("}\n");
+ return code;
+ }
+ });
+ state.refresh(logger);
+
+ JProgram program = JavaAstConstructor.construct(logger, state,
+ "test.EntryPoint");
+ return program;
+ }
+}