Make compiler go faster. Particularly when you have:
a) Several permutations
b) More than one processor
It's also faster with several permutations and just one processor.
Here's the basic design:
1) Build a unified Java AST from Eclipse's JDT tree; represent unresolved deferred binding decisions as explicit nodes
2) Do optimizations on this tree.
3) Serialize into a byte buffer (thanks to Bruce for this tasty suggestion)
4) Crank up N worker threads
5) Each worker thread:
a) Grabs a particular permutation and deserializes its own copy of the unified Java AST
b) Resolves all deferred binding for that particular permutation
c) Optimizes the permutation with the new knowledge of deferred binding decisions
6) Profit!
Some notes:
- Doing step 2 up front on the unified tree actually realizes some performance gains by making step 5c take less time in each permutation, even when you have only one worker thread
- The number of worker threads created is Math.min(numberOfMachineProcessors, estimatedNumberOfASTsThatCanFitInMemoryConcurrently); in other words, there's no point creating extra threads if you don't have the memory for them, or you can't actually utilize the hardware. Permutation optimization barely touches the I/O, it's all CPU bound.
- Making the Java and JS ASTs serializable was surprisingly easy.
Review by: spoon
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3303 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index 7505c9f..3fe8068 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -22,7 +22,6 @@
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.core.ext.linker.impl.StandardCompilationResult;
import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
-import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.cfg.Properties;
@@ -37,7 +36,9 @@
import com.google.gwt.dev.jjs.JJSOptions;
import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
import com.google.gwt.dev.jjs.JsOutputOption;
+import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.shell.StandardRebindOracle;
+import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
@@ -54,11 +55,8 @@
import com.google.gwt.util.tools.ToolBase;
import java.io.File;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -118,16 +116,21 @@
private class CompilationRebindOracle extends StandardRebindOracle {
private final Map<String, String> cache = new HashMap<String, String>();
+ private final StaticPropertyOracle propOracle;
- public CompilationRebindOracle(ArtifactSet generatorArtifacts) {
+ public CompilationRebindOracle(ArtifactSet generatorArtifacts,
+ StaticPropertyOracle propOracle) {
super(compilationState, propOracle, module, rules, genDir,
generatorResourcesDir, generatorArtifacts);
+ this.propOracle = propOracle;
+ }
+
+ public StaticPropertyOracle getPropertyOracle() {
+ return propOracle;
}
/**
- * Overridden so that we can selectively record inputs and outputs to derive
- * the cache key for a compilation. Note that the cache gets invalidated if
- * the propOracle changes state.
+ * Overridden so that we can cache answers.
*/
@Override
public String rebind(TreeLogger logger, String in)
@@ -151,23 +154,19 @@
private class DistillerRebindPermutationOracle implements
RebindPermutationOracle {
- private final StandardRebindOracle rebindOracle;
+ private CompilationRebindOracle[] rebindOracles;
- public DistillerRebindPermutationOracle(ArtifactSet generatorArtifacts) {
- rebindOracle = new StandardRebindOracle(compilationState, propOracle,
- module, rules, genDir, generatorResourcesDir, generatorArtifacts) {
-
- /**
- * Record generated types.
- */
- @Override
- protected void onGeneratedTypes(String result, JClassType[] genTypes) {
- List<JClassType> list = new ArrayList<JClassType>();
- Util.addAll(list, genTypes);
- Object existing = generatedTypesByResultTypeName.put(result, list);
- assert (existing == null) : "Internal error: redundant notification of generated types";
- }
- };
+ public DistillerRebindPermutationOracle(ArtifactSet generatorArtifacts,
+ PropertyPermutations perms) {
+ rebindOracles = new CompilationRebindOracle[perms.size()];
+ Property[] orderedProps = perms.getOrderedProperties();
+ for (int i = 0; i < rebindOracles.length; ++i) {
+ String[] orderedPropValues = perms.getOrderedPropertyValues(i);
+ StaticPropertyOracle propOracle = new StaticPropertyOracle(
+ orderedProps, orderedPropValues);
+ rebindOracles[i] = new CompilationRebindOracle(generatorArtifacts,
+ propOracle);
+ }
}
public String[] getAllPossibleRebindAnswers(TreeLogger logger,
@@ -179,24 +178,211 @@
Set<String> answers = new HashSet<String>();
- Property[] orderedProps = perms.getOrderedProperties();
- for (Iterator<String[]> iter = perms.iterator(); iter.hasNext();) {
- String[] orderedPropValues = iter.next();
-
- // Create a snapshot of the property values by setting their values
- // in the property oracle. Because my rebindOracle uses the shared
- // generator context (which in turns uses the propOracle), this
- // has the effect we're after. It isn't reentrant, though, so don't
- // expect to call this recursively.
- propOracle.setPropertyValues(orderedProps, orderedPropValues);
-
- // Ask the rebind oracle.
- logProperties(logger, orderedProps, orderedPropValues);
+ for (CompilationRebindOracle rebindOracle : rebindOracles) {
String resultTypeName = rebindOracle.rebind(logger, requestTypeName);
answers.add(resultTypeName);
}
return Util.toArray(String.class, answers);
}
+
+ public int getPermuationCount() {
+ return rebindOracles.length;
+ }
+
+ public CompilationRebindOracle getRebindOracle(int permNumber) {
+ return rebindOracles[permNumber];
+ }
+ }
+
+ /**
+ * Represents the state of a single permutation for compile.
+ */
+ private static final class Permuation {
+ final TreeLogger logger;
+ final int number;
+ final CompilationRebindOracle rebindOracle;
+
+ public Permuation(TreeLogger logger, int number,
+ CompilationRebindOracle rebindOracle) {
+ this.logger = logger;
+ this.number = number;
+ this.rebindOracle = rebindOracle;
+ }
+ }
+
+ /**
+ * Contains all shared state and synchronizes among permutation workers and
+ * the main thread.
+ *
+ * TODO: clean this up using java.util.concurrent
+ */
+ private static final class PermutationManager {
+
+ private boolean failed;
+ private final StandardLinkerContext linkerContext;
+ private final TreeLogger logger;
+ private int nextPerm = 0;
+ private int numThreads;
+ private final CompilationRebindOracle[] rebindOracles;
+
+ public PermutationManager(TreeLogger logger,
+ StandardLinkerContext linkerContext,
+ DistillerRebindPermutationOracle rpo, int numThreads) {
+ this.logger = logger;
+ this.linkerContext = linkerContext;
+ this.numThreads = numThreads;
+ rebindOracles = new CompilationRebindOracle[rpo.getPermuationCount()];
+ for (int i = 0; i < rebindOracles.length; ++i) {
+ rebindOracles[i] = rpo.getRebindOracle(i);
+ }
+ }
+
+ public synchronized void finishPermuation(Permuation perm, String js) {
+ StandardCompilationResult compilation;
+ try {
+ compilation = linkerContext.getCompilation(perm.logger, js);
+ } catch (UnableToCompleteException e) {
+ recordFailure();
+ return;
+ }
+ StaticPropertyOracle propOracle = perm.rebindOracle.getPropertyOracle();
+ Property[] orderedProps = propOracle.getOrderedProps();
+ String[] orderedPropValues = propOracle.getOrderedPropValues();
+ Map<SelectionProperty, String> unboundProperties = new HashMap<SelectionProperty, String>();
+ for (int i = 0; i < orderedProps.length; i++) {
+ SelectionProperty key = linkerContext.getProperty(orderedProps[i].getName());
+ if (key.tryGetValue() != null) {
+ /*
+ * The view of the Permutation doesn't include properties with defined
+ * values.
+ */
+ continue;
+ }
+ unboundProperties.put(key, orderedPropValues[i]);
+ }
+ compilation.addSelectionPermutation(unboundProperties);
+ }
+
+ public synchronized int getActiveThreadCount() {
+ return numThreads;
+ }
+
+ public synchronized Permuation getNextPermuation() {
+ if (hasNextPermutation()) {
+ // Make sure we have enough memory.
+ int permNumber = nextPerm++;
+ while (nextPerm < rebindOracles.length
+ && rebindOracles[nextPerm] == null) {
+ ++nextPerm;
+ }
+ TreeLogger branch = logger.branch(TreeLogger.TRACE,
+ "Analyzing permutation #" + (permNumber + 1), null);
+ return new Permuation(branch, permNumber, rebindOracles[permNumber]);
+ }
+ return null;
+ }
+
+ public synchronized boolean isFailed() {
+ return failed;
+ }
+
+ public synchronized void outOfMemory(Permuation perm, OutOfMemoryError e) {
+ if (getActiveThreadCount() > 1) {
+ // Recycle and try on a different thread.
+ perm.logger.log(TreeLogger.WARN,
+ "Out of memory; will retry permutation using fewer concurrent threads");
+ rebindOracles[perm.number] = perm.rebindOracle;
+ if (nextPerm > perm.number) {
+ nextPerm = perm.number;
+ }
+ } else {
+ // Only one thread, we're truly out of memory!
+ perm.logger.log(TreeLogger.ERROR, null, e);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public synchronized void recordFailure() {
+ failed = true;
+ }
+
+ public synchronized void threadDied() {
+ --numThreads;
+ }
+
+ private boolean hasNextPermutation() {
+ return !isFailed() && nextPerm < rebindOracles.length;
+ }
+ }
+
+ /**
+ * A worker thread for compiling individual permutations.
+ */
+ private static final class PermutationWorker implements Runnable {
+ private static void logProperties(TreeLogger logger,
+ StaticPropertyOracle propOracle) {
+ Property[] props = propOracle.getOrderedProps();
+ String[] values = propOracle.getOrderedPropValues();
+ if (logger.isLoggable(TreeLogger.DEBUG)) {
+ logger = logger.branch(TreeLogger.DEBUG, "Setting properties", null);
+ for (int i = 0; i < props.length; i++) {
+ String name = props[i].getName();
+ String value = values[i];
+ logger.log(TreeLogger.TRACE, name + " = " + value, null);
+ }
+ }
+ }
+
+ private final JavaToJavaScriptCompiler jjs;
+ private final PermutationManager manager;
+
+ public PermutationWorker(PermutationManager localState,
+ JavaToJavaScriptCompiler jjs) {
+ this.manager = localState;
+ this.jjs = jjs;
+ }
+
+ public void run() {
+ try {
+ while (hasEnoughMemory()) {
+ Permuation perm = manager.getNextPermuation();
+ if (perm == null) {
+ return;
+ }
+ PerfLogger.start("Permutation #" + (perm.number + 1));
+ try {
+ logProperties(perm.logger, perm.rebindOracle.getPropertyOracle());
+ String js = jjs.compile(perm.logger, perm.rebindOracle);
+ manager.finishPermuation(perm, js);
+ // Allow GC.
+ js = null;
+ } catch (OutOfMemoryError e) {
+ manager.outOfMemory(perm, e);
+ return;
+ } catch (Throwable e) {
+ perm.logger.log(TreeLogger.ERROR, "Permutation failed", e);
+ manager.recordFailure();
+ return;
+ } finally {
+ PerfLogger.end();
+ }
+ }
+ } finally {
+ manager.threadDied();
+ }
+ }
+
+ private boolean hasEnoughMemory() {
+ if (manager.getActiveThreadCount() == 1) {
+ // I'm the last thread, so it doesn't matter, we have to try.
+ return true;
+ }
+ if (jjs.getAstMemoryUsage() < getPotentialFreeMemory()) {
+ // Best effort memory reclaim.
+ System.gc();
+ }
+ return jjs.getAstMemoryUsage() < getPotentialFreeMemory();
+ }
}
public static final String GWT_COMPILER_DIR = ".gwt-tmp" + File.separatorChar
@@ -220,18 +406,23 @@
System.exit(1);
}
+ private static long getPotentialFreeMemory() {
+ long used = Runtime.getRuntime().totalMemory()
+ - Runtime.getRuntime().freeMemory();
+ assert (used > 0);
+ long potentialFree = Runtime.getRuntime().maxMemory() - used;
+ assert (potentialFree >= 0);
+ return potentialFree;
+ }
+
private CompilationState compilationState;
private String[] declEntryPts;
private File genDir;
- private Map<String, List<JClassType>> generatedTypesByResultTypeName = new HashMap<String, List<JClassType>>();
-
private File generatorResourcesDir;
- private JavaToJavaScriptCompiler jjs;
-
private JJSOptions jjsOptions = new JJSOptions();
private Type logLevel;
@@ -246,9 +437,7 @@
private Properties properties;
- private StaticPropertyOracle propOracle = new StaticPropertyOracle();
-
- private RebindPermutationOracle rebindPermOracle;
+ private DistillerRebindPermutationOracle rebindPermOracle;
private Rules rules;
@@ -345,23 +534,25 @@
}
ArtifactSet generatorArtifacts = new ArtifactSet();
- rebindPermOracle = new DistillerRebindPermutationOracle(generatorArtifacts);
properties = module.getProperties();
perms = new PropertyPermutations(properties);
+ rebindPermOracle = new DistillerRebindPermutationOracle(generatorArtifacts,
+ perms);
+
WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd(
compilationState, rebindPermOracle);
- jjs = new JavaToJavaScriptCompiler(logger, frontEnd, declEntryPts,
- jjsOptions);
-
- StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
- module, outDir, generatorResourcesDir, jjsOptions);
- compilePermutations(logger, linkerContext, generatorArtifacts);
+ JavaToJavaScriptCompiler jjs = new JavaToJavaScriptCompiler(logger,
+ frontEnd, declEntryPts, jjsOptions);
if (jjsOptions.isValidateOnly()) {
logger.log(TreeLogger.INFO, "Validation succeeded", null);
return;
}
+ StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
+ module, outDir, generatorResourcesDir, jjsOptions);
+ compilePermutations(logger, jjs, linkerContext);
+
logger.log(TreeLogger.INFO, "Compilation succeeded", null);
linkerContext.addOrReplaceArtifacts(generatorArtifacts);
linkerContext.link(logger, linkerContext, null);
@@ -431,84 +622,100 @@
}
private void compilePermutations(TreeLogger logger,
- StandardLinkerContext linkerContext, ArtifactSet generatedArtifacts)
+ JavaToJavaScriptCompiler jjs, final StandardLinkerContext linkerContext)
throws UnableToCompleteException {
- logger = logger.branch(TreeLogger.DEBUG, "Compiling permutations", null);
- Property[] orderedProps = perms.getOrderedProperties();
- int permNumber = 1;
- for (Iterator<String[]> iter = perms.iterator(); iter.hasNext(); ++permNumber) {
+ PerfLogger.start("Compiling " + perms.size() + " permutations");
- String[] orderedPropValues = iter.next();
- String js = realizePermutation(logger, orderedProps, orderedPropValues,
- permNumber, generatedArtifacts);
+ int threadCount = computeThreadCount(logger, jjs);
- // This is the case in validateOnly mode, which doesn't produce output
- if (js == null) {
- continue;
+ PermutationManager manager = new PermutationManager(logger.branch(
+ TreeLogger.DEBUG, "Compiling permutations", null), linkerContext,
+ rebindPermOracle, threadCount);
+
+ Thread[] threads = new Thread[threadCount];
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i] = new Thread(new PermutationWorker(manager, jjs));
+ }
+
+ for (Thread thread : threads) {
+ thread.start();
+ }
+
+ for (Thread thread : threads) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Unexpected interruption");
}
+ }
- StandardCompilationResult compilation = linkerContext.getCompilation(
- logger, js);
+ PerfLogger.end();
- Map<SelectionProperty, String> unboundProperties = new HashMap<SelectionProperty, String>();
- for (int i = 0; i < orderedProps.length; i++) {
- SelectionProperty key = linkerContext.getProperty(orderedProps[i].getName());
- if (key.tryGetValue() != null) {
- // The view of the Permutation doesn't include properties with defined
- // values.
- continue;
- }
- unboundProperties.put(key, orderedPropValues[i]);
- }
-
- compilation.addSelectionPermutation(unboundProperties);
+ if (manager.failed) {
+ throw new UnableToCompleteException();
}
}
- private void logProperties(TreeLogger logger, Property[] props,
- String[] values) {
- if (logger.isLoggable(TreeLogger.DEBUG)) {
- logger = logger.branch(TreeLogger.DEBUG, "Setting properties", null);
- for (int i = 0; i < props.length; i++) {
- String name = props[i].getName();
- String value = values[i];
- logger.log(TreeLogger.TRACE, name + " = " + value, null);
- }
+ private int computeThreadCount(TreeLogger logger, JavaToJavaScriptCompiler jjs) {
+ /*
+ * Don't need more threads than the number of permutations.
+ */
+ int result = rebindPermOracle.getPermuationCount();
+
+ /*
+ * Computation is mostly CPU bound, so don't use more threads than
+ * processors.
+ */
+ result = Math.min(Runtime.getRuntime().availableProcessors(), result);
+
+ /*
+ * Allow user-defined override as an escape valve.
+ */
+ result = Math.min(result, Integer.getInteger("gwt.jjs.maxThreads", result));
+
+ if (result == 1) {
+ return 1;
}
- }
- /**
- * Attempts to compile with a single permutation of properties. The result can
- * be one of the following:
- * <ul>
- * <li>There is an existing compilation having the same deferred binding
- * results (and thus would create identical output); compilation is skipped
- * <li>No existing compilation unit matches, so the compilation proceeds
- * </ul>
- */
- private String realizePermutation(TreeLogger logger, Property[] currentProps,
- String[] currentValues, int permNumber, ArtifactSet generatorArtifacts)
- throws UnableToCompleteException {
- String msg = "Analyzing permutation #" + permNumber;
- logger = logger.branch(TreeLogger.TRACE, msg, null);
+ // More than one thread would definitely be faster at this point.
- logProperties(logger, currentProps, currentValues);
+ if (JProgram.isTracingEnabled()) {
+ logger.log(TreeLogger.INFO,
+ "Parallel compilation disabled due to gwt.jjs.traceMethods being enabled");
+ return 1;
+ }
- // Create a rebind oracle that will record decisions so that we can cache
- // them and avoid future computations.
- //
- CompilationRebindOracle rebindOracle = new CompilationRebindOracle(
- generatorArtifacts);
+ int desiredThreads = result;
- // Tell the property provider above about the current property values.
- // Note that the rebindOracle is actually sensitive to these values because
- // in its ctor is uses propOracle as its property oracle.
- //
- propOracle.setPropertyValues(currentProps, currentValues);
+ /*
+ * Need to do some memory estimation to figure out how many concurrent
+ * threads we can safely run.
+ */
+ long potentialFreeMemory = getPotentialFreeMemory();
+ long astMemoryUsage = jjs.getAstMemoryUsage();
+ int memUsageThreads = (int) (potentialFreeMemory / astMemoryUsage) + 1;
+ logger.log(TreeLogger.TRACE,
+ "Extra threads constrained by estimated memory usage: "
+ + memUsageThreads + " = " + potentialFreeMemory + " / "
+ + astMemoryUsage);
- // Create JavaScript.
- //
- return jjs.compile(logger, rebindOracle);
+ if (memUsageThreads < desiredThreads) {
+ long currentMaxMemory = Runtime.getRuntime().maxMemory();
+ // Convert to megabytes.
+ currentMaxMemory /= 1024 * 1024;
+
+ long suggestedMaxMemory = currentMaxMemory * 2;
+
+ logger.log(TreeLogger.WARN, desiredThreads
+ + " threads could be run concurrently, but only " + memUsageThreads
+ + " threads will be run due to limited memory; "
+ + "increasing the amount of memory by using the -Xmx flag "
+ + "at startup (java -Xmx" + suggestedMaxMemory
+ + "M ...) may result in faster compiles");
+ }
+
+ result = Math.min(memUsageThreads, desiredThreads);
+ return result;
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java b/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
index c5ebadd..b9ce882 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/PropertyPermutations.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -15,13 +15,13 @@
*/
package com.google.gwt.dev.cfg;
+import java.util.Arrays;
import java.util.Iterator;
-import java.util.NoSuchElementException;
/**
* Generates all possible permutations of properties in a module.
*/
-public class PropertyPermutations {
+public class PropertyPermutations implements Iterable<String[]> {
private int currPermIndex;
@@ -48,31 +48,21 @@
return properties;
}
+ public String[] getOrderedPropertyValues(int permutation) {
+ return values[permutation];
+ }
+
/**
* Enumerates each permutation as an array of strings such that the index of
* each string in the array corresponds to the property at the same index in
* the array returned from {@link #getOrderedProperties()}.
*/
public Iterator<String[]> iterator() {
- return new Iterator<String[]>() {
+ return Arrays.asList(values).iterator();
+ }
- private int iterPermIndex;
-
- public boolean hasNext() {
- return iterPermIndex < values.length;
- }
-
- public String[] next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
- return values[iterPermIndex++];
- }
-
- public void remove() {
- throw new UnsupportedOperationException("remove");
- }
- };
+ public int size() {
+ return values.length;
}
private int countPermutations() {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
index 6e760e4..460ef95 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
@@ -20,16 +20,27 @@
import com.google.gwt.core.ext.TreeLogger;
/**
- * An implementation of {@link PropertyOracle} that maintains explicit property
- * values, rather than computing them.
+ * An implementation of {@link PropertyOracle} that contains property values,
+ * rather than computing them.
*/
public class StaticPropertyOracle implements PropertyOracle {
- private Property[] currentProps;
+ private final Property[] orderedProps;
- private String[] currentValues;
+ private final String[] orderedPropValues;
- public StaticPropertyOracle() {
+ public StaticPropertyOracle(Property[] orderedProps,
+ String[] orderedPropValues) {
+ this.orderedProps = orderedProps;
+ this.orderedPropValues = orderedPropValues;
+ }
+
+ public Property[] getOrderedProps() {
+ return orderedProps;
+ }
+
+ public String[] getOrderedPropValues() {
+ return orderedPropValues;
}
public String getPropertyValue(TreeLogger logger, String propertyName)
@@ -39,10 +50,10 @@
// If that turns out not to be the case, the ctor could build a
// name-to-index map.
//
- for (int i = 0; i < currentProps.length; i++) {
- Property prop = currentProps[i];
+ for (int i = 0; i < orderedProps.length; i++) {
+ Property prop = orderedProps[i];
if (prop.getName().equals(propertyName)) {
- String value = currentValues[i];
+ String value = orderedPropValues[i];
if (prop.isKnownValue(value)) {
return value;
} else {
@@ -58,8 +69,8 @@
public String[] getPropertyValueSet(TreeLogger logger, String propertyName)
throws BadPropertyValueException {
- for (int i = 0; i < currentProps.length; i++) {
- Property prop = currentProps[i];
+ for (int i = 0; i < orderedProps.length; i++) {
+ Property prop = orderedProps[i];
if (prop.getName().equals(propertyName)) {
return prop.getKnownValues();
}
@@ -69,9 +80,4 @@
//
throw new BadPropertyValueException(propertyName);
}
-
- public void setPropertyValues(Property[] props, String[] values) {
- currentProps = props;
- currentValues = values;
- }
}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/RebindPermutationOracle.java b/dev/core/src/com/google/gwt/dev/jdt/RebindPermutationOracle.java
index 6695ce6..8108e76 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/RebindPermutationOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/RebindPermutationOracle.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * 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
@@ -29,4 +29,6 @@
*/
String[] getAllPossibleRebindAnswers(TreeLogger logger, String sourceTypeName)
throws UnableToCompleteException;
+
+ int getPermuationCount();
}
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 aaadac7..baf9709 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -25,11 +25,12 @@
import com.google.gwt.dev.jjs.ast.JBinaryOperator;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JGwtCreate;
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.JNewInstance;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReboundEntryPoint;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JStatement;
import com.google.gwt.dev.jjs.impl.ArrayNormalizer;
@@ -54,6 +55,7 @@
import com.google.gwt.dev.jjs.impl.PostOptimizationCompoundAssignmentNormalizer;
import com.google.gwt.dev.jjs.impl.Pruner;
import com.google.gwt.dev.jjs.impl.ReplaceRebinds;
+import com.google.gwt.dev.jjs.impl.ResolveRebinds;
import com.google.gwt.dev.jjs.impl.TypeMap;
import com.google.gwt.dev.jjs.impl.TypeTightener;
import com.google.gwt.dev.js.JsIEBlockSizeVisitor;
@@ -69,6 +71,7 @@
import com.google.gwt.dev.js.JsVerboseNamer;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.util.DefaultTextOutput;
+import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.Util;
import org.eclipse.jdt.core.compiler.IProblem;
@@ -76,6 +79,12 @@
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -87,101 +96,109 @@
*/
public class JavaToJavaScriptCompiler {
+ private static JMethodCall createReboundModuleLoad(TreeLogger logger,
+ JProgram program, JReferenceType reboundEntryType,
+ String originalMainClassName) throws UnableToCompleteException {
+ if (!(reboundEntryType instanceof JClassType)) {
+ logger.log(TreeLogger.ERROR, "Module entry point class '"
+ + originalMainClassName + "' must be a class", null);
+ throw new UnableToCompleteException();
+ }
+
+ JClassType entryClass = (JClassType) reboundEntryType;
+ if (entryClass.isAbstract()) {
+ logger.log(TreeLogger.ERROR, "Module entry point class '"
+ + originalMainClassName + "' must not be abstract", null);
+ throw new UnableToCompleteException();
+ }
+
+ JMethod entryMethod = findMainMethodRecurse(reboundEntryType);
+ if (entryMethod == null) {
+ logger.log(TreeLogger.ERROR,
+ "Could not find entry method 'onModuleLoad()' method in entry point class '"
+ + originalMainClassName + "'", null);
+ throw new UnableToCompleteException();
+ }
+
+ if (entryMethod.isAbstract()) {
+ logger.log(TreeLogger.ERROR,
+ "Entry method 'onModuleLoad' in entry point class '"
+ + originalMainClassName + "' must not be abstract", null);
+ throw new UnableToCompleteException();
+ }
+
+ JExpression qualifier = null;
+ if (!entryMethod.isStatic()) {
+ qualifier = JGwtCreate.createInstantiationExpression(program, null,
+ entryClass);
+
+ if (qualifier == null) {
+ logger.log(
+ TreeLogger.ERROR,
+ "No default (zero argument) constructor could be found in entry point class '"
+ + originalMainClassName
+ + "' to qualify a call to non-static entry method 'onModuleLoad'",
+ null);
+ throw new UnableToCompleteException();
+ }
+ }
+ return new JMethodCall(program, null, qualifier, entryMethod);
+ }
+
private static void findEntryPoints(TreeLogger logger,
- RebindOracle rebindOracle, String[] mainClassNames, JProgram program)
+ RebindPermutationOracle rpo, String[] mainClassNames, JProgram program)
throws UnableToCompleteException {
JMethod bootStrapMethod = program.createMethod(null, "init".toCharArray(),
null, program.getTypeVoid(), false, true, true, false, false);
bootStrapMethod.freezeParamTypes();
JMethodBody body = (JMethodBody) bootStrapMethod.getBody();
- for (int i = 0; i < mainClassNames.length; ++i) {
- String mainClassName = mainClassNames[i];
- JReferenceType referenceType = program.getFromTypeMap(mainClassName);
+ List<JStatement> statements = body.getStatements();
+ for (String mainClassName : mainClassNames) {
+ statements.add(makeStatsCalls(program, mainClassName));
+ JReferenceType mainType = program.getFromTypeMap(mainClassName);
- if (referenceType == null) {
+ if (mainType == null) {
logger.log(TreeLogger.ERROR,
"Could not find module entry point class '" + mainClassName + "'",
null);
throw new UnableToCompleteException();
}
- JExpression qualifier = null;
- JMethod mainMethod = findMainMethod(referenceType);
- if (mainMethod == null || !mainMethod.isStatic()) {
- // Couldn't find a static main method; must rebind the class
- String originalClassName = mainClassName;
- mainClassName = rebindOracle.rebind(logger, originalClassName);
- referenceType = program.getFromTypeMap(mainClassName);
- if (referenceType == null) {
- logger.log(TreeLogger.ERROR,
- "Could not find module entry point class '" + mainClassName
- + "' after rebinding from '" + originalClassName + "'", null);
- throw new UnableToCompleteException();
- }
-
- if (!(referenceType instanceof JClassType)) {
- logger.log(TreeLogger.ERROR, "Module entry point class '"
- + mainClassName + "' must be a class", null);
- throw new UnableToCompleteException();
- }
-
- JClassType mainClass = (JClassType) referenceType;
- if (mainClass.isAbstract()) {
- logger.log(TreeLogger.ERROR, "Module entry point class '"
- + mainClassName + "' must not be abstract", null);
- throw new UnableToCompleteException();
- }
-
- mainMethod = findMainMethodRecurse(referenceType);
- if (mainMethod == null) {
- logger.log(TreeLogger.ERROR,
- "Could not find entry method 'onModuleLoad()' method in entry point class '"
- + mainClassName + "'", null);
- throw new UnableToCompleteException();
- }
-
- if (mainMethod.isAbstract()) {
- logger.log(TreeLogger.ERROR,
- "Entry method 'onModuleLoad' in entry point class '"
- + mainClassName + "' must not be abstract", null);
- throw new UnableToCompleteException();
- }
-
- if (!mainMethod.isStatic()) {
- // Find the appropriate (noArg) constructor
- JMethod noArgCtor = null;
- for (int j = 0; j < mainClass.methods.size(); ++j) {
- JMethod ctor = mainClass.methods.get(j);
- if (ctor.getName().equals(mainClass.getShortName())) {
- if (ctor.params.size() == 0) {
- noArgCtor = ctor;
- }
- }
- }
- if (noArgCtor == null) {
- logger.log(
- TreeLogger.ERROR,
- "No default (zero argument) constructor could be found in entry point class '"
- + mainClassName
- + "' to qualify a call to non-static entry method 'onModuleLoad'",
- null);
- throw new UnableToCompleteException();
- }
-
- // Construct a new instance of the class to qualify the non-static
- // call
- JNewInstance newInstance = new JNewInstance(program, null, mainClass);
- qualifier = new JMethodCall(program, null, newInstance, noArgCtor);
- }
+ JMethod mainMethod = findMainMethod(mainType);
+ if (mainMethod != null && mainMethod.isStatic()) {
+ JMethodCall onModuleLoadCall = new JMethodCall(program, null, null,
+ mainMethod);
+ statements.add(onModuleLoadCall.makeStatement());
+ continue;
}
- // qualifier will be null if onModuleLoad is static
- JMethodCall onModuleLoadCall = new JMethodCall(program, null, qualifier,
- mainMethod);
+ // Couldn't find a static main method; must rebind the class
+ String[] resultTypeNames = rpo.getAllPossibleRebindAnswers(logger,
+ mainClassName);
+ List<JClassType> resultTypes = new ArrayList<JClassType>();
+ List<JExpression> entryCalls = new ArrayList<JExpression>();
+ for (String resultTypeName : resultTypeNames) {
+ JReferenceType resultType = program.getFromTypeMap(resultTypeName);
+ if (resultType == null) {
+ logger.log(TreeLogger.ERROR,
+ "Could not find module entry point class '" + resultTypeName
+ + "' after rebinding from '" + mainClassName + "'", null);
+ throw new UnableToCompleteException();
+ }
- body.getStatements().add(makeStatsCalls(program, mainClassName));
- body.getStatements().add(onModuleLoadCall.makeStatement());
+ JMethodCall onModuleLoadCall = createReboundModuleLoad(logger, program,
+ resultType, mainClassName);
+ resultTypes.add((JClassType) resultType);
+ entryCalls.add(onModuleLoadCall);
+ }
+ if (resultTypes.size() == 1) {
+ statements.add(entryCalls.get(0).makeStatement());
+ } else {
+ JReboundEntryPoint reboundEntryPoint = new JReboundEntryPoint(program,
+ null, mainType, resultTypes, entryCalls);
+ statements.add(reboundEntryPoint);
+ }
}
program.addEntryMethod(bootStrapMethod);
}
@@ -208,6 +225,13 @@
return null;
}
+ private static long getUsedMemory() {
+ long used = Runtime.getRuntime().totalMemory()
+ - Runtime.getRuntime().freeMemory();
+ assert (used > 0);
+ return used;
+ }
+
/**
* Create a variable assignment to invoke a call to the statistics collector.
*
@@ -234,10 +258,14 @@
return amp.makeStatement();
}
+ private final long astMemoryUsage;
private final String[] declEntryPoints;
- private final CompilationUnitDeclaration[] goldenCuds;
+ private final Object myLockObject = new Object();
private final JJSOptions options;
private final Set<IProblem> problemSet = new HashSet<IProblem>();
+ private JProgram savedJProgram = null;
+ private JsProgram savedJsProgram = null;
+ private final byte[] serializedAst;
public JavaToJavaScriptCompiler(TreeLogger logger,
WebModeCompilerFrontEnd compiler, String[] declEntryPts)
@@ -259,9 +287,10 @@
//
this.declEntryPoints = declEntryPts;
+ RebindPermutationOracle rpo = compiler.getRebindPermutationOracle();
+
if (!options.isValidateOnly()) {
// Find all the possible rebound entry points.
- RebindPermutationOracle rpo = compiler.getRebindPermutationOracle();
Set<String> allEntryPoints = new TreeSet<String>();
for (String element : declEntryPts) {
String[] all = rpo.getAllPossibleRebindAnswers(logger, element);
@@ -277,59 +306,53 @@
// Compile the source and get the compiler so we can get the parse tree
//
- goldenCuds = compiler.getCompilationUnitDeclarations(logger, declEntryPts);
+ CompilationUnitDeclaration[] goldenCuds = compiler.getCompilationUnitDeclarations(
+ logger, declEntryPts);
// Check for compilation problems. We don't log here because any problems
// found here will have already been logged by AbstractCompiler.
//
- checkForErrors(logger, false);
- }
+ checkForErrors(logger, goldenCuds, false);
- /**
- * Creates finished JavaScript source code from the specified Java compilation
- * units.
- */
- public String compile(TreeLogger logger, RebindOracle rebindOracle)
- throws UnableToCompleteException {
+ PerfLogger.start("Build AST");
+ JProgram jprogram = savedJProgram = new JProgram();
+ JsProgram jsProgram = savedJsProgram = new JsProgram();
+ long memoryDelta;
try {
+ System.gc();
+ long usedMemoryBefore = getUsedMemory();
+ /*
+ * (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);
- JProgram jprogram = new JProgram(logger, rebindOracle);
- JsProgram jsProgram = new JsProgram();
+ // BuildTypeMap can uncover syntactic JSNI errors; report & abort
+ checkForErrors(logger, goldenCuds, true);
- if (JProgram.isTracingEnabled()) {
- System.out.println("------------------------------------------------------------");
- System.out.println("| (new permuation) |");
- System.out.println("------------------------------------------------------------");
- }
+ // Compute all super type/sub type info
+ jprogram.typeOracle.computeBeforeAST();
- {
- /*
- * (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);
+ // (2) Create our own Java AST from the JDT AST.
+ GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, jsProgram,
+ options.isEnableAssertions());
- // BuildTypeMap can uncover syntactic JSNI errors; report & abort
- checkForErrors(logger, true);
+ System.gc();
+ long usedMemoryAfter = getUsedMemory();
+ memoryDelta = usedMemoryAfter - usedMemoryBefore;
+ long localAstMemoryUsage = (long) (memoryDelta * 1.5);
- // Compute all super type/sub type info
- jprogram.typeOracle.computeBeforeAST();
+ // GenerateJavaAST can uncover semantic JSNI errors; report & abort
+ checkForErrors(logger, goldenCuds, true);
- // (2) Create our own Java AST from the JDT AST.
- GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, jsProgram,
- options.isEnableAssertions());
-
- // GenerateJavaAST can uncover semantic JSNI errors; report & abort
- checkForErrors(logger, true);
-
- // Enable GC.
- typeMap = null;
- allTypeDeclarations = null;
- }
+ // Allow GC
+ goldenCuds = null;
+ typeMap = null;
+ allTypeDeclarations = null;
// (3) Perform Java AST normalizations.
@@ -347,55 +370,114 @@
AssertionRemover.exec(jprogram);
}
- // Resolve all rebinds through GWT.create().
- ReplaceRebinds.exec(jprogram);
+ // Replace GWT.create calls with JGwtCreate nodes.
+ ReplaceRebinds.exec(logger, jprogram, rpo);
- if (options.isValidateOnly()) {
- // That's it, we're done.
- return null;
- }
-
- // Also rebind all non-static entry points.
- findEntryPoints(logger, rebindOracle, declEntryPoints, jprogram);
+ // Resolve entry points, rebinding non-static entry points.
+ findEntryPoints(logger, rpo, declEntryPoints, jprogram);
// Replace references to JSO subtypes with JSO itself.
JavaScriptObjectNormalizer.exec(jprogram);
- // (4) Optimize the normalized Java AST
- boolean didChange;
- do {
- // Recompute clinits each time, they can become empty.
- jprogram.typeOracle.recomputeClinits();
+ /*
+ * (4) Optimize the normalized Java AST for the common AST. By doing
+ * optimizations early in the multiple permutation scenario, we're saving
+ * work. However, we can't fully optimized because we don't yet know the
+ * deferred binding decisions.
+ *
+ * Don't bother optimizing early if there's only one permutation.
+ */
+ if (rpo.getPermuationCount() > 1) {
+ optimize(jprogram);
- didChange = false;
- // Remove unreferenced types, fields, methods, [params, locals]
- didChange = Pruner.exec(jprogram, true) || didChange;
- // finalize locals, params, fields, methods, classes
- didChange = Finalizer.exec(jprogram) || didChange;
- // rewrite non-polymorphic calls as static calls; update all call sites
- didChange = MakeCallsStatic.exec(jprogram) || didChange;
+ PerfLogger.start("serialize");
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream os = new ObjectOutputStream(baos);
+ os.writeObject(jprogram);
+ os.writeObject(jsProgram);
+ os.close();
+ serializedAst = baos.toByteArray();
+ PerfLogger.end();
- // type flow tightening
- // - fields, locals based on assignment
- // - params based on assignment and call sites
- // - method bodies based on return statements
- // - polymorphic methods based on return types of all implementors
- // - optimize casts and instance of
- didChange = TypeTightener.exec(jprogram) || didChange;
+ // Very rough heuristic.
+ this.astMemoryUsage = Math.max(localAstMemoryUsage,
+ serializedAst.length * 4);
+ logger.log(TreeLogger.TRACE, "Estimated AST memory usage: "
+ + astMemoryUsage + " = Math.max(" + memoryDelta + " * 1.5, "
+ + serializedAst.length + " * 4)");
+ } else {
+ this.astMemoryUsage = localAstMemoryUsage;
+ this.serializedAst = null;
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "Should be impossible to get an IOException reading an in-memory stream");
+ } catch (Throwable e) {
+ throw logAndTranslateException(logger, e);
+ } finally {
+ PerfLogger.end();
+ synchronized (myLockObject) {
+ /*
+ * JLS 17.4.4: ensure all changes are visible to any other thread
+ * calling compile.
+ *
+ * TODO: is this necessary?
+ */
+ }
+ }
+ }
- // tighten method call bindings
- didChange = MethodCallTightener.exec(jprogram) || didChange;
+ /**
+ * Creates finished JavaScript source code from the specified Java compilation
+ * units.
+ */
+ public String compile(TreeLogger logger, RebindOracle rebindOracle)
+ throws UnableToCompleteException {
- // dead code removal??
- didChange = DeadCodeElimination.exec(jprogram) || didChange;
+ JProgram jprogram = null;
+ JsProgram jsProgram = null;
- if (options.isAggressivelyOptimize()) {
- // inlining
- didChange = MethodInliner.exec(jprogram) || didChange;
- }
- // prove that any types that have been culled from the main tree are
- // unreferenced due to type tightening?
- } while (didChange);
+ synchronized (myLockObject) {
+ if (savedJProgram != null && savedJsProgram != null) {
+ jprogram = savedJProgram;
+ jsProgram = savedJsProgram;
+ savedJProgram = null;
+ savedJsProgram = null;
+ }
+ }
+
+ if (jprogram == null || jsProgram == null) {
+ if (serializedAst == null) {
+ throw new IllegalStateException("No serialized AST was cached.");
+ }
+ try {
+ PerfLogger.start("deserialize");
+ ByteArrayInputStream bais = new ByteArrayInputStream(serializedAst);
+ ObjectInputStream is;
+ is = new ObjectInputStream(bais);
+ jprogram = (JProgram) is.readObject();
+ jsProgram = (JsProgram) is.readObject();
+ PerfLogger.end();
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "Should be impossible for memory based streams", e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(
+ "Should be impossible when deserializing in process", e);
+ }
+ }
+
+ try {
+ if (JProgram.isTracingEnabled()) {
+ System.out.println("------------------------------------------------------------");
+ System.out.println("| (new permuation) |");
+ System.out.println("------------------------------------------------------------");
+ }
+
+ ResolveRebinds.exec(logger, jprogram, rebindOracle);
+
+ // (4) Optimize the normalized Java AST for each permutation.
+ optimize(jprogram);
// (5) "Normalize" the high-level Java tree into a lower-level tree more
// suited for JavaScript code generation. Don't go reordering these
@@ -417,6 +499,9 @@
jprogram.typeOracle.recomputeClinits();
GenerateJavaScriptAST.exec(jprogram, jsProgram, options.getOutput());
+ // Allow GC.
+ jprogram = null;
+
// (8) Normalize the JS AST.
// Fix invalid constructs created during JS AST gen.
JsNormalizer.exec(jsProgram);
@@ -425,13 +510,13 @@
// (9) Optimize the JS AST.
if (options.isAggressivelyOptimize()) {
+ boolean didChange;
do {
didChange = false;
// Remove unused functions, possible
didChange = JsStaticEval.exec(jsProgram) || didChange;
// Inline JavaScript function invocations
- didChange = options.isAggressivelyOptimize()
- && JsInliner.exec(jsProgram) || didChange;
+ didChange = JsInliner.exec(jsProgram) || didChange;
// Remove unused functions, possible
didChange = JsUnusedFunctionRemover.exec(jsProgram) || didChange;
} while (didChange);
@@ -467,51 +552,60 @@
JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
v.accept(jsProgram);
return out.toString();
- } catch (UnableToCompleteException e) {
- // just rethrow
- throw e;
- } catch (InternalCompilerException e) {
- TreeLogger topBranch = logger.branch(TreeLogger.ERROR,
- "An internal compiler exception occurred", e);
- List<NodeInfo> nodeTrace = 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);
- }
- }
- throw new UnableToCompleteException();
} catch (Throwable e) {
- logger.log(TreeLogger.ERROR, "Unexpected internal compiler error", e);
- throw new UnableToCompleteException();
+ throw logAndTranslateException(logger, e);
}
}
- private void checkForErrors(final TreeLogger logger, boolean itemizeErrors)
+ public long getAstMemoryUsage() {
+ return astMemoryUsage;
+ }
+
+ protected void optimize(JProgram jprogram) {
+ boolean didChange;
+ do {
+ // Recompute clinits each time, they can become empty.
+ jprogram.typeOracle.recomputeClinits();
+
+ didChange = false;
+ // Remove unreferenced types, fields, methods, [params, locals]
+ didChange = Pruner.exec(jprogram, true) || didChange;
+ // finalize locals, params, fields, methods, classes
+ didChange = Finalizer.exec(jprogram) || didChange;
+ // rewrite non-polymorphic calls as static calls; update all call sites
+ didChange = MakeCallsStatic.exec(jprogram) || didChange;
+
+ // type flow tightening
+ // - fields, locals based on assignment
+ // - params based on assignment and call sites
+ // - method bodies based on return statements
+ // - polymorphic methods based on return types of all implementors
+ // - optimize casts and instance of
+ didChange = TypeTightener.exec(jprogram) || didChange;
+
+ // tighten method call bindings
+ didChange = MethodCallTightener.exec(jprogram) || didChange;
+
+ // dead code removal??
+ didChange = DeadCodeElimination.exec(jprogram) || didChange;
+
+ if (options.isAggressivelyOptimize()) {
+ // inlining
+ didChange = MethodInliner.exec(jprogram) || didChange;
+ }
+ // prove that any types that have been culled from the main tree are
+ // unreferenced due to type tightening?
+ } while (didChange);
+ }
+
+ private void checkForErrors(TreeLogger logger,
+ CompilationUnitDeclaration[] cuds, boolean itemizeErrors)
throws UnableToCompleteException {
boolean compilationFailed = false;
- if (goldenCuds.length == 0) {
+ if (cuds.length == 0) {
compilationFailed = true;
}
- for (CompilationUnitDeclaration cud : goldenCuds) {
+ for (CompilationUnitDeclaration cud : cuds) {
CompilationResult result = cud.compilationResult();
if (result.hasErrors()) {
compilationFailed = true;
@@ -552,4 +646,44 @@
throw new UnableToCompleteException();
}
}
+
+ private 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 {
+ logger.log(TreeLogger.ERROR, "Unexpected internal compiler error", e);
+ return new UnableToCompleteException();
+ }
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
index 4ec08e3..7debc2c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -15,10 +15,12 @@
*/
package com.google.gwt.dev.jjs;
+import java.io.Serializable;
+
/**
* Tracks file and line information for AST nodes.
*/
-public class SourceInfo {
+public class SourceInfo implements Serializable {
private final int endPos;
private final String fileName;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java
new file mode 100644
index 0000000..9a44731
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java
@@ -0,0 +1,110 @@
+/*
+ * 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.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a GWT.create() call before deferred binding decisions are
+ * finalized. Replaced with the entry call for the appropriate rebind result in
+ * that permutation.
+ */
+public class JGwtCreate extends JExpression implements HasSettableType {
+
+ public static JExpression createInstantiationExpression(JProgram program,
+ SourceInfo info, JClassType classType) {
+ /*
+ * Find the appropriate (noArg) constructor. In our AST, constructors are
+ * instance methods that should be qualified with a new expression.
+ */
+ JMethod noArgCtor = null;
+ for (int i = 0; i < classType.methods.size(); ++i) {
+ JMethod ctor = classType.methods.get(i);
+ if (ctor.getName().equals(classType.getShortName())) {
+ if (ctor.params.size() == 0) {
+ noArgCtor = ctor;
+ }
+ }
+ }
+ if (noArgCtor == null) {
+ return null;
+ }
+ // Call it, using a new expression as a qualifier
+ JNewInstance newInstance = new JNewInstance(program, info, classType);
+ return new JMethodCall(program, info, newInstance, noArgCtor);
+ }
+
+ private final ArrayList<JExpression> instantiationExpressions = new ArrayList<JExpression>();
+ private final List<JClassType> resultTypes;
+ private final JReferenceType sourceType;
+
+ private JType type;
+
+ public JGwtCreate(JProgram program, SourceInfo info,
+ JReferenceType sourceType, List<JClassType> resultTypes) {
+ super(program, info);
+ this.sourceType = sourceType;
+ this.resultTypes = resultTypes;
+
+ // Initially object; will be updated by type tightening.
+ this.type = program.getTypeJavaLangObject();
+
+ for (JClassType classType : resultTypes) {
+ JExpression expr = createInstantiationExpression(program, info, classType);
+ assert expr != null;
+ instantiationExpressions.add(expr);
+ }
+ }
+
+ public ArrayList<JExpression> getInstantiationExpressions() {
+ return instantiationExpressions;
+ }
+
+ public List<JClassType> getResultTypes() {
+ return resultTypes;
+ }
+
+ public JReferenceType getSourceType() {
+ return sourceType;
+ }
+
+ public JType getType() {
+ return type;
+ }
+
+ public boolean hasSideEffects() {
+ for (JExpression expr : instantiationExpressions) {
+ if (expr.hasSideEffects()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setType(JType newType) {
+ type = newType;
+ }
+
+ public void traverse(JVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ visitor.accept(instantiationExpressions);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
index 715014d..88fc652 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
@@ -21,10 +21,12 @@
import com.google.gwt.dev.jjs.impl.ToStringGenerationVisitor;
import com.google.gwt.dev.util.DefaultTextOutput;
+import java.io.Serializable;
+
/**
* Base class for all visitable AST nodes.
*/
-public abstract class JNode implements JVisitable, HasSourceInfo {
+public abstract class JNode implements JVisitable, HasSourceInfo, Serializable {
protected final JProgram program;
private final SourceInfo info;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index ad44449..28b8b28 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -15,9 +15,6 @@
*/
package com.google.gwt.dev.jjs.ast;
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.dev.jdt.RebindOracle;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
import com.google.gwt.dev.jjs.ast.JField.Disposition;
@@ -25,6 +22,7 @@
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
import com.google.gwt.dev.jjs.ast.js.JsonObject;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -44,6 +42,17 @@
*/
public class JProgram extends JNode {
+ private static final class ArrayTypeComparator implements
+ Comparator<JArrayType>, Serializable {
+ public int compare(JArrayType o1, JArrayType o2) {
+ int comp = o1.getDims() - o2.getDims();
+ if (comp != 0) {
+ return comp;
+ }
+ return o1.getName().compareTo(o2.getName());
+ }
+ }
+
public static final Set<String> CODEGEN_TYPES_SET = new LinkedHashSet<String>(
Arrays.asList(new String[] {
"com.google.gwt.lang.Array", "com.google.gwt.lang.Cast",
@@ -60,6 +69,8 @@
static final Map<String, Set<String>> traceMethods = new HashMap<String, Set<String>>();
+ private static final Comparator<JArrayType> ARRAYTYPE_COMPARATOR = new ArrayTypeComparator();
+
private static final int IS_ARRAY = 2;
private static final int IS_CLASS = 3;
@@ -120,7 +131,7 @@
}
public static boolean isTracingEnabled() {
- return System.getProperty("gwt.jjs.traceMethods") != null;
+ return traceMethods.size() > 0;
}
public static boolean methodsDoMatch(JMethod method1, JMethod method2) {
@@ -174,15 +185,7 @@
* Sorted to avoid nondeterministic iteration.
*/
private final Set<JArrayType> allArrayTypes = new TreeSet<JArrayType>(
- new Comparator<JArrayType>() {
- public int compare(JArrayType o1, JArrayType o2) {
- int comp = o1.getDims() - o2.getDims();
- if (comp != 0) {
- return comp;
- }
- return o1.getName().compareTo(o2.getName());
- }
- });
+ ARRAYTYPE_COMPARATOR);
private final List<JReferenceType> allTypes = new ArrayList<JReferenceType>();
@@ -218,16 +221,12 @@
private final JBooleanLiteral literalTrue = new JBooleanLiteral(this, true);
- private final TreeLogger logger;
-
private JField nullField;
private JMethod nullMethod;
private Map<JReferenceType, Integer> queryIds;
- private final RebindOracle rebindOracle;
-
private final Map<JMethod, JMethod> staticToInstanceMap = new IdentityHashMap<JMethod, JMethod>();
private final JPrimitiveType typeBoolean = new JPrimitiveType(this,
@@ -273,10 +272,8 @@
private final JPrimitiveType typeVoid = new JPrimitiveType(this, "void", "V",
"java.lang.Void", null);
- public JProgram(TreeLogger logger, RebindOracle rebindOracle) {
+ public JProgram() {
super(null, null);
- this.logger = logger;
- this.rebindOracle = rebindOracle;
}
public void addEntryMethod(JMethod entryPoint) {
@@ -440,10 +437,11 @@
return x;
}
- public JReferenceType generalizeTypes(Collection<JReferenceType> types) {
+ public JReferenceType generalizeTypes(
+ Collection<? extends JReferenceType> types) {
assert (types != null);
assert (!types.isEmpty());
- Iterator<JReferenceType> it = types.iterator();
+ Iterator<? extends JReferenceType> it = types.iterator();
JReferenceType curType = it.next();
while (it.hasNext()) {
curType = generalizeTypes(curType, it.next());
@@ -742,28 +740,6 @@
}
}
- public JClassType rebind(JType type) {
- JType result = type;
- // Rebinds are always on a source type name.
- String reqType = type.getName().replace('$', '.');
- String reboundClassName;
- try {
- reboundClassName = rebindOracle.rebind(logger, reqType);
- } catch (UnableToCompleteException e) {
- // The fact that we already compute every rebind permutation before
- // compiling should prevent this case from ever happening in real life.
- //
- throw new IllegalStateException("Unexpected failure to rebind '"
- + reqType + "'");
- }
- if (reboundClassName != null) {
- result = getFromTypeMap(reboundClassName);
- }
- assert (result != null);
- assert (result instanceof JClassType);
- return (JClassType) result;
- }
-
public void recordQueryIds(Map<JReferenceType, Integer> queryIds) {
this.queryIds = queryIds;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JReboundEntryPoint.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JReboundEntryPoint.java
new file mode 100644
index 0000000..54693ec
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JReboundEntryPoint.java
@@ -0,0 +1,60 @@
+/*
+ * 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.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+import java.util.List;
+
+/**
+ * Represents a rebound entry point before deferred binding decisions are
+ * finalized. Replaced with the entry call for the appropriate rebind result in
+ * that permutation.
+ */
+public class JReboundEntryPoint extends JStatement {
+
+ private final List<JExpression> entryCalls;
+ private final List<JClassType> resultTypes;
+ private final JReferenceType sourceType;
+
+ public JReboundEntryPoint(JProgram program, SourceInfo info,
+ JReferenceType sourceType, List<JClassType> resultTypes,
+ List<JExpression> entryCalls) {
+ super(program, info);
+ this.sourceType = sourceType;
+ this.resultTypes = resultTypes;
+ this.entryCalls = entryCalls;
+ }
+
+ public List<JExpression> getEntryCalls() {
+ return entryCalls;
+ }
+
+ public List<JClassType> getResultTypes() {
+ return resultTypes;
+ }
+
+ public JReferenceType getSourceType() {
+ return sourceType;
+ }
+
+ public void traverse(JVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ visitor.accept(entryCalls);
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index 64db362..c822c99 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -17,6 +17,7 @@
import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -28,7 +29,7 @@
/**
* Oracle that can answer questions regarding the types in a program.
*/
-public class JTypeOracle {
+public class JTypeOracle implements Serializable {
/**
* Checks a clinit method to find out a few things.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
index 4babd97..7e89ac8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
@@ -181,6 +181,9 @@
public void endVisit(JForStatement x, Context ctx) {
}
+ public void endVisit(JGwtCreate x, Context ctx) {
+ }
+
public void endVisit(JIfStatement x, Context ctx) {
}
@@ -250,6 +253,9 @@
public void endVisit(JProgram x, Context ctx) {
}
+ public void endVisit(JReboundEntryPoint x, Context ctx) {
+ }
+
public void endVisit(JReturnStatement x, Context ctx) {
}
@@ -385,6 +391,10 @@
return true;
}
+ public boolean visit(JGwtCreate x, Context ctx) {
+ return true;
+ }
+
public boolean visit(JIfStatement x, Context ctx) {
return true;
}
@@ -477,6 +487,10 @@
return true;
}
+ public boolean visit(JReboundEntryPoint x, Context ctx) {
+ return true;
+ }
+
public boolean visit(JReturnStatement x, Context ctx) {
return true;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
index 230d685..12c7248 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -29,6 +29,7 @@
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JFieldRef;
import com.google.gwt.dev.jjs.ast.JFloatLiteral;
+import com.google.gwt.dev.jjs.ast.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JInstanceOf;
import com.google.gwt.dev.jjs.ast.JIntLiteral;
import com.google.gwt.dev.jjs.ast.JLocalRef;
@@ -234,6 +235,21 @@
}
@Override
+ public boolean visit(JGwtCreate x, Context ctx) {
+ JGwtCreate gwtCreate = new JGwtCreate(program, x.getSourceInfo(),
+ x.getSourceType(), x.getResultTypes());
+
+ // Clone the internal instantiation expressions directly.
+ ArrayList<JExpression> exprs = x.getInstantiationExpressions();
+ ArrayList<JExpression> cloneExprs = gwtCreate.getInstantiationExpressions();
+ for (int i = 0; i < exprs.size(); ++i) {
+ cloneExprs.set(i, cloneExpression(exprs.get(i)));
+ }
+ expression = gwtCreate;
+ return false;
+ }
+
+ @Override
public boolean visit(JNullLiteral x, Context ctx) {
expression = x;
return false;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index 04942fb..7f1cf9a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -36,11 +36,11 @@
import com.google.gwt.dev.jjs.ast.JContinueStatement;
import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
import com.google.gwt.dev.jjs.ast.JDoStatement;
-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.JFieldRef;
import com.google.gwt.dev.jjs.ast.JForStatement;
+import com.google.gwt.dev.jjs.ast.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JIfStatement;
import com.google.gwt.dev.jjs.ast.JInstanceOf;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
@@ -59,6 +59,7 @@
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReboundEntryPoint;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JStatement;
@@ -127,6 +128,7 @@
import java.util.Arrays;
import java.util.Collections;
+import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
@@ -710,6 +712,11 @@
}
@Override
+ public void endVisit(JGwtCreate x, Context ctx) {
+ throw new InternalCompilerException("Should not get here.");
+ }
+
+ @Override
public void endVisit(JIfStatement x, Context ctx) {
JsIf stmt = new JsIf();
@@ -1001,6 +1008,11 @@
}
@Override
+ public void endVisit(JReboundEntryPoint x, Context ctx) {
+ throw new InternalCompilerException("Should not get here.");
+ }
+
+ @Override
public void endVisit(JReturnStatement x, Context ctx) {
if (x.getExpr() != null) {
push(new JsReturn((JsExpression) pop())); // expr
@@ -1197,18 +1209,6 @@
return false;
}
- /**
- * Return whether the expression could possibly evaluate to a string.
- */
- private boolean couldBeString(JExpression x) {
- JType type = x.getType();
- if (type instanceof JReferenceType) {
- return typeOracle.canTheoreticallyCast((JReferenceType) type,
- program.getTypeJavaLangString());
- }
- return false;
- }
-
private JsExpression createAssignment(JsExpression lhs, JsExpression rhs) {
return new JsBinaryOperation(JsBinaryOperator.ASG, lhs, rhs);
}
@@ -1514,8 +1514,10 @@
}
private static class JavaToJsOperatorMap {
- private static final Map<JBinaryOperator, JsBinaryOperator> bOpMap = new IdentityHashMap<JBinaryOperator, JsBinaryOperator>();
- private static final Map<JUnaryOperator, JsUnaryOperator> uOpMap = new IdentityHashMap<JUnaryOperator, JsUnaryOperator>();
+ private static final Map<JBinaryOperator, JsBinaryOperator> bOpMap = new EnumMap<JBinaryOperator, JsBinaryOperator>(
+ JBinaryOperator.class);
+ private static final Map<JUnaryOperator, JsUnaryOperator> uOpMap = new EnumMap<JUnaryOperator, JsUnaryOperator>(
+ JUnaryOperator.class);
static {
bOpMap.put(JBinaryOperator.MUL, JsBinaryOperator.MUL);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java
index 4679f71..a18f36f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaPrecedenceVisitor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -29,6 +29,7 @@
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JFieldRef;
import com.google.gwt.dev.jjs.ast.JFloatLiteral;
+import com.google.gwt.dev.jjs.ast.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JInstanceOf;
import com.google.gwt.dev.jjs.ast.JIntLiteral;
import com.google.gwt.dev.jjs.ast.JLocalRef;
@@ -67,145 +68,152 @@
private JavaPrecedenceVisitor() {
}
- // @Override
+ @Override
public boolean visit(JAbsentArrayDimension x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JArrayRef x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JBinaryOperation operation, Context ctx) {
answer = operation.getOp().getPrecedence();
return false;
}
- // @Override
+ @Override
public boolean visit(JBooleanLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JCastOperation operation, Context ctx) {
answer = 2;
return false;
}
- // @Override
+ @Override
public boolean visit(JCharLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JClassLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JConditional conditional, Context ctx) {
answer = 13;
return false;
}
- // @Override
+ @Override
public boolean visit(JDoubleLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JFieldRef x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JFloatLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JInstanceOf of, Context ctx) {
answer = 6;
return false;
}
- // @Override
+ @Override
public boolean visit(JIntLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JLocalRef x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JLongLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JMethodCall x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
+ public boolean visit(JGwtCreate x, Context ctx) {
+ // It's a method call.
+ answer = 0;
+ return false;
+ }
+
+ @Override
public boolean visit(JNewArray array, Context ctx) {
answer = 2;
return false;
}
- // @Override
+ @Override
public boolean visit(JNewInstance instance, Context ctx) {
answer = 2;
return false;
}
- // @Override
+ @Override
public boolean visit(JNullLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JParameterRef x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JPostfixOperation operation, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JPrefixOperation operation, Context ctx) {
answer = 1;
return false;
}
- // @Override
+ @Override
public boolean visit(JStringLiteral x, Context ctx) {
answer = 0;
return false;
}
- // @Override
+ @Override
public boolean visit(JThisRef x, Context ctx) {
answer = 0;
return false;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
index c2fabb6..b95f1b8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -15,19 +15,26 @@
*/
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.jdt.RebindPermutationOracle;
+import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression;
+import com.google.gwt.dev.jjs.ast.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
-import com.google.gwt.dev.jjs.ast.JNewInstance;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+
+import java.util.ArrayList;
+import java.util.List;
/**
- * Replaces any "GWT.create()" calls with a new expression for the actual result
- * of the deferred binding decision.
+ * Replaces any "GWT.create()" calls with a special node.
*/
public class ReplaceRebinds {
@@ -39,7 +46,7 @@
this.rebindCreateMethod = rebindCreateMethod;
}
- // @Override
+ @Override
public void endVisit(JMethodCall x, Context ctx) {
JMethod method = x.getTarget();
if (method == rebindCreateMethod) {
@@ -47,40 +54,56 @@
JExpression arg = x.getArgs().get(0);
assert (arg instanceof JClassLiteral);
JClassLiteral classLiteral = (JClassLiteral) arg;
- JClassType classType = program.rebind(classLiteral.getRefType());
-
- /*
- * Find the appropriate (noArg) constructor. In our AST, constructors
- * are instance methods that should be qualified with a new expression.
- */
- JMethod noArgCtor = null;
- for (int i = 0; i < classType.methods.size(); ++i) {
- JMethod ctor = classType.methods.get(i);
- if (ctor.getName().equals(classType.getShortName())) {
- if (ctor.params.size() == 0) {
- noArgCtor = ctor;
- }
- }
+ JReferenceType sourceType = (JReferenceType) classLiteral.getRefType();
+ List<JClassType> allRebindResults = getAllPossibleRebindResults(sourceType);
+ JGwtCreate gwtCreate = new JGwtCreate(program, x.getSourceInfo(),
+ sourceType, allRebindResults);
+ if (allRebindResults.size() == 1) {
+ // Just replace with the instantiation expression.
+ ctx.replaceMe(gwtCreate.getInstantiationExpressions().get(0));
+ } else {
+ ctx.replaceMe(gwtCreate);
}
- assert (noArgCtor != null);
- // Call it, using a new expression as a qualifier
- JNewInstance newInstance = new JNewInstance(program, x.getSourceInfo(),
- classType);
- JMethodCall call = new JMethodCall(program, x.getSourceInfo(),
- newInstance, noArgCtor);
- ctx.replaceMe(call);
}
}
}
- public static boolean exec(JProgram program) {
- return new ReplaceRebinds(program).execImpl();
+ public static boolean exec(TreeLogger logger, JProgram program,
+ RebindPermutationOracle rpo) {
+ return new ReplaceRebinds(logger, program, rpo).execImpl();
}
+ private final TreeLogger logger;
private final JProgram program;
+ private final RebindPermutationOracle rpo;
- private ReplaceRebinds(JProgram program) {
+ private ReplaceRebinds(TreeLogger logger, JProgram program,
+ RebindPermutationOracle rpo) {
+ this.logger = logger;
this.program = program;
+ this.rpo = rpo;
+ }
+
+ protected List<JClassType> getAllPossibleRebindResults(JReferenceType type) {
+ // Rebinds are always on a source type name.
+ String reqType = type.getName().replace('$', '.');
+ String[] answers;
+ try {
+ answers = rpo.getAllPossibleRebindAnswers(logger, reqType);
+ } catch (UnableToCompleteException e) {
+ // Should never happen.
+ throw new InternalCompilerException(
+ "Unexpected failure to get possible rebind answers for '" + reqType
+ + "'");
+ }
+ List<JClassType> rebindAnswers = new ArrayList<JClassType>();
+ for (String answer : answers) {
+ JReferenceType result = program.getFromTypeMap(answer);
+ assert (result != null);
+ rebindAnswers.add((JClassType) result);
+ }
+ assert rebindAnswers.size() > 0;
+ return rebindAnswers;
}
private boolean execImpl() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java
new file mode 100644
index 0000000..d0cbdd7
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java
@@ -0,0 +1,113 @@
+/*
+ * 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.jdt.RebindOracle;
+import com.google.gwt.dev.jjs.InternalCompilerException;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JGwtCreate;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReboundEntryPoint;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JType;
+
+import java.util.List;
+
+/**
+ * Replaces any "GWT.create()" calls with a new expression for the actual result
+ * of the deferred binding decision.
+ */
+public class ResolveRebinds {
+
+ private class RebindVisitor extends JModVisitor {
+ @Override
+ public void endVisit(JGwtCreate x, Context ctx) {
+ JClassType rebindResult = rebind(x.getSourceType());
+ List<JClassType> rebindResults = x.getResultTypes();
+ for (int i = 0; i < rebindResults.size(); ++i) {
+ // Find the matching rebound type.
+ if (rebindResult == rebindResults.get(i)) {
+ // Replace with the associated instantiation expression.
+ ctx.replaceMe(x.getInstantiationExpressions().get(i));
+ return;
+ }
+ }
+ throw new InternalCompilerException(
+ "No matching rebind result in all rebind results!");
+ }
+
+ @Override
+ public void endVisit(JReboundEntryPoint x, Context ctx) {
+ JClassType rebindResult = rebind(x.getSourceType());
+ List<JClassType> rebindResults = x.getResultTypes();
+ for (int i = 0; i < rebindResults.size(); ++i) {
+ // Find the matching rebound type.
+ if (rebindResult == rebindResults.get(i)) {
+ // Replace with the associated instantiation expression.
+ ctx.replaceMe(x.getEntryCalls().get(i).makeStatement());
+ return;
+ }
+ }
+ throw new InternalCompilerException(
+ "No matching rebind result in all rebind results!");
+ }
+ }
+
+ public static boolean exec(TreeLogger logger, JProgram program,
+ RebindOracle rebindOracle) {
+ return new ResolveRebinds(logger, program, rebindOracle).execImpl();
+ }
+
+ private final TreeLogger logger;
+ private final JProgram program;
+ private final RebindOracle rebindOracle;
+
+ private ResolveRebinds(TreeLogger logger, JProgram program,
+ RebindOracle rebindOracle) {
+ this.logger = logger;
+ this.program = program;
+ this.rebindOracle = rebindOracle;
+ }
+
+ public JClassType rebind(JType type) {
+ // Rebinds are always on a source type name.
+ String reqType = type.getName().replace('$', '.');
+ String reboundClassName;
+ try {
+ reboundClassName = rebindOracle.rebind(logger, reqType);
+ } catch (UnableToCompleteException e) {
+ // The fact that we already compute every rebind permutation before
+ // compiling should prevent this case from ever happening in real life.
+ //
+ throw new InternalCompilerException("Unexpected failure to rebind '"
+ + reqType + "'");
+ }
+ JReferenceType result = program.getFromTypeMap(reboundClassName);
+ assert (result != null);
+ return (JClassType) result;
+ }
+
+ private boolean execImpl() {
+ RebindVisitor rebinder = new RebindVisitor();
+ rebinder.accept(program);
+ return rebinder.didChange();
+ }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
index ed705ca..b06d6cc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
@@ -46,6 +46,7 @@
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.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JIfStatement;
import com.google.gwt.dev.jjs.ast.JInstanceOf;
import com.google.gwt.dev.jjs.ast.JIntLiteral;
@@ -69,6 +70,7 @@
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.JReboundEntryPoint;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
import com.google.gwt.dev.jjs.ast.JStatement;
@@ -345,6 +347,21 @@
}
@Override
+ public boolean visit(JDeclarationStatement x, Context ctx) {
+ if (!suppressType) {
+ accept(x.getVariableRef().getTarget());
+ } else {
+ accept(x.getVariableRef());
+ }
+ JExpression initializer = x.getInitializer();
+ if (initializer != null) {
+ print(" = ");
+ accept(initializer);
+ }
+ return false;
+ }
+
+ @Override
public boolean visit(JDoStatement x, Context ctx) {
print(CHARS_DO);
if (x.getBody() != null) {
@@ -447,6 +464,14 @@
}
@Override
+ public boolean visit(JGwtCreate x, Context ctx) {
+ print("GWT.create(");
+ printTypeName(x.getSourceType());
+ print(".class)");
+ return false;
+ }
+
+ @Override
public boolean visit(JIfStatement x, Context ctx) {
print(CHARS_IF);
lparen();
@@ -544,21 +569,6 @@
}
@Override
- public boolean visit(JDeclarationStatement x, Context ctx) {
- if (!suppressType) {
- accept(x.getVariableRef().getTarget());
- } else {
- accept(x.getVariableRef());
- }
- JExpression initializer = x.getInitializer();
- if (initializer != null) {
- print(" = ");
- accept(initializer);
- }
- return false;
- }
-
- @Override
public boolean visit(JLocalRef x, Context ctx) {
printName(x.getLocal());
return false;
@@ -720,6 +730,13 @@
}
@Override
+ public boolean visit(JReboundEntryPoint x, Context ctx) {
+ print("<JReboundEntryPoint>");
+ printTypeName(x.getSourceType());
+ return false;
+ }
+
+ @Override
public boolean visit(JReturnStatement x, Context ctx) {
print(CHARS_RETURN);
if (x.getExpr() != null) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 943df9d..d304a7f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -25,6 +25,7 @@
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JFieldRef;
+import com.google.gwt.dev.jjs.ast.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JInstanceOf;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLocal;
@@ -448,6 +449,21 @@
}
@Override
+ public void endVisit(JGwtCreate x, Context ctx) {
+ List<JClassType> typeList = new ArrayList<JClassType>();
+ for (JExpression expr : x.getInstantiationExpressions()) {
+ JType type = expr.getType();
+ typeList.add((JClassType) type);
+ }
+
+ JReferenceType resultType = program.generalizeTypes(typeList);
+ if (x.getType() != resultType) {
+ x.setType(resultType);
+ myDidChange = true;
+ }
+ }
+
+ @Override
public void endVisit(JInstanceOf x, Context ctx) {
JType argType = x.getExpr().getType();
if (!(argType instanceof JReferenceType)) {
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperator.java b/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperator.java
index 1692d1a..5cac60d 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperator.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsBinaryOperator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -18,77 +18,72 @@
/**
* Represents the operator in a JavaScript binary operation.
*/
-public final class JsBinaryOperator extends JsOperator {
+public enum JsBinaryOperator implements JsOperator {
- // Precedence indices from "JavaScript - The Definitive Guide" 4th Edition
- // (page 57)
- //
+ /*
+ * Precedence indices from "JavaScript - The Definitive Guide" 4th Edition
+ * (page 57)
+ *
+ *
+ * Precedence 15 is for really important things that have their own AST
+ * classes.
+ *
+ * Precedence 14 is for unary operators.
+ */
- // Precendence 15 is for really important things that have their own AST
- // classes.
+ MUL("*", 13, LEFT | INFIX), DIV("/", 13, LEFT | INFIX), MOD("%", 13, LEFT
+ | INFIX),
- // Precendence 14 is for unary operators.
+ ADD("+", 12, LEFT | INFIX), SUB("-", 12, LEFT | INFIX),
- private static final int LEFT_INFIX = LEFT | INFIX;
- public static final JsBinaryOperator MUL = create("*", 13, LEFT_INFIX);
- public static final JsBinaryOperator DIV = create("/", 13, LEFT_INFIX);
- public static final JsBinaryOperator MOD = create("%", 13, LEFT_INFIX);
+ SHL("<<", 11, LEFT | INFIX), SHR(">>", 11, LEFT | INFIX), SHRU(">>>", 11,
+ LEFT | INFIX),
- public static final JsBinaryOperator ADD = create("+", 12, LEFT_INFIX);
- public static final JsBinaryOperator SUB = create("-", 12, LEFT_INFIX);
+ LT("<", 10, LEFT | INFIX), LTE("<=", 10, LEFT | INFIX), GT(">", 10, LEFT
+ | INFIX), GTE(">=", 10, LEFT | INFIX), INSTANCEOF("instanceof", 10, LEFT
+ | INFIX), INOP("in", 10, LEFT | INFIX),
- public static final JsBinaryOperator SHL = create("<<", 11, LEFT_INFIX);
- public static final JsBinaryOperator SHR = create(">>", 11, LEFT_INFIX);
- public static final JsBinaryOperator SHRU = create(">>>", 11, LEFT_INFIX);
+ EQ("==", 9, LEFT | INFIX), NEQ("!=", 9, LEFT | INFIX), REF_EQ("===", 9, LEFT
+ | INFIX), REF_NEQ("!==", 9, LEFT | INFIX),
- public static final JsBinaryOperator LT = create("<", 10, LEFT_INFIX);
- public static final JsBinaryOperator LTE = create("<=", 10, LEFT_INFIX);
- public static final JsBinaryOperator GT = create(">", 10, LEFT_INFIX);
- public static final JsBinaryOperator GTE = create(">=", 10, LEFT_INFIX);
- public static final JsBinaryOperator INSTANCEOF = create("instanceof", 10,
- LEFT_INFIX);
- public static final JsBinaryOperator INOP = create("in", 10, LEFT_INFIX);
+ BIT_AND("&", 8, LEFT | INFIX),
- public static final JsBinaryOperator EQ = create("==", 9, LEFT_INFIX);
- public static final JsBinaryOperator NEQ = create("!=", 9, LEFT_INFIX);
- public static final JsBinaryOperator REF_EQ = create("===", 9, LEFT_INFIX);
- public static final JsBinaryOperator REF_NEQ = create("!==", 9, LEFT_INFIX);
+ BIT_XOR("^", 7, LEFT | INFIX),
- public static final JsBinaryOperator BIT_AND = create("&", 8, LEFT_INFIX);
+ BIT_OR("|", 6, LEFT | INFIX),
- public static final JsBinaryOperator BIT_XOR = create("^", 7, LEFT_INFIX);
+ AND("&&", 5, LEFT | INFIX),
- public static final JsBinaryOperator BIT_OR = create("|", 6, LEFT_INFIX);
+ OR("||", 4, LEFT | INFIX),
- public static final JsBinaryOperator AND = create("&&", 5, LEFT_INFIX);
-
- public static final JsBinaryOperator OR = create("||", 4, LEFT_INFIX);
-
- // Precendence 3 is for the condition operator.
+ // Precedence 3 is for the condition operator.
// These assignment operators are right-associative.
- public static final JsBinaryOperator ASG = create("=", 2, INFIX);
- public static final JsBinaryOperator ASG_ADD = create("+=", 2, INFIX);
- public static final JsBinaryOperator ASG_SUB = create("-=", 2, INFIX);
- public static final JsBinaryOperator ASG_MUL = create("*=", 2, INFIX);
- public static final JsBinaryOperator ASG_DIV = create("/=", 2, INFIX);
- public static final JsBinaryOperator ASG_MOD = create("%=", 2, INFIX);
- public static final JsBinaryOperator ASG_SHL = create("<<=", 2, INFIX);
- public static final JsBinaryOperator ASG_SHR = create(">>=", 2, INFIX);
- public static final JsBinaryOperator ASG_SHRU = create(">>>=", 2, INFIX);
- public static final JsBinaryOperator ASG_BIT_AND = create("&=", 2, INFIX);
- public static final JsBinaryOperator ASG_BIT_OR = create("|=", 2, INFIX);
- public static final JsBinaryOperator ASG_BIT_XOR = create("^=", 2, INFIX);
+ ASG("=", 2, INFIX), ASG_ADD("+=", 2, INFIX), ASG_SUB("-=", 2, INFIX), ASG_MUL(
+ "*=", 2, INFIX), ASG_DIV("/=", 2, INFIX), ASG_MOD("%=", 2, INFIX), ASG_SHL(
+ "<<=", 2, INFIX), ASG_SHR(">>=", 2, INFIX), ASG_SHRU(">>>=", 2, INFIX), ASG_BIT_AND(
+ "&=", 2, INFIX), ASG_BIT_OR("|=", 2, INFIX), ASG_BIT_XOR("^=", 2, INFIX),
- public static final JsBinaryOperator COMMA = create(",", 1, LEFT_INFIX);
+ COMMA(",", 1, LEFT | INFIX);
- private static JsBinaryOperator create(String symbol, int precedence, int mask) {
- JsBinaryOperator op = new JsBinaryOperator(symbol, precedence, mask);
- return op;
- }
+ private final int mask;
+
+ private final int precedence;
+
+ private final String symbol;
private JsBinaryOperator(String symbol, int precedence, int mask) {
- super(symbol, precedence, mask);
+ this.symbol = symbol;
+ this.precedence = precedence;
+ this.mask = mask;
+ }
+
+ public int getPrecedence() {
+ return precedence;
+ }
+
+ public String getSymbol() {
+ return symbol;
}
public boolean isAssignment() {
@@ -99,9 +94,32 @@
return (getPrecedence() == ASG.getPrecedence());
}
- @Override
public boolean isKeyword() {
return this == INSTANCEOF || this == INOP;
}
+ public boolean isLeftAssociative() {
+ return (mask & LEFT) != 0;
+ }
+
+ public boolean isPrecedenceLessThan(JsOperator other) {
+ return precedence < other.getPrecedence();
+ }
+
+ public boolean isValidInfix() {
+ return (mask & INFIX) != 0;
+ }
+
+ public boolean isValidPostfix() {
+ return (mask & POSTFIX) != 0;
+ }
+
+ public boolean isValidPrefix() {
+ return (mask & PREFIX) != 0;
+ }
+
+ @Override
+ public String toString() {
+ return symbol;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsName.java b/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
index 30e9497..8321727 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsName.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -15,10 +15,12 @@
*/
package com.google.gwt.dev.js.ast;
+import java.io.Serializable;
+
/**
* An abstract base class for named JavaScript objects.
*/
-public class JsName {
+public class JsName implements Serializable {
private final JsScope enclosing;
private final String ident;
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNode.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNode.java
index 125a8d1..6ca9618 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsNode.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -21,13 +21,15 @@
import com.google.gwt.dev.js.JsToStringGenerationVisitor;
import com.google.gwt.dev.util.DefaultTextOutput;
+import java.io.Serializable;
+
/**
* Base class for all JS AST elements.
*
* @param <T>
*/
-public abstract class JsNode<T extends JsVisitable<T>> implements JsVisitable<T>,
- HasSourceInfo {
+public abstract class JsNode<T extends JsVisitable<T>> implements
+ JsVisitable<T>, HasSourceInfo, Serializable {
public SourceInfo getSourceInfo() {
// TODO: make this real
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsOperator.java b/dev/core/src/com/google/gwt/dev/js/ast/JsOperator.java
index 0d19ab8..543b6c5 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsOperator.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsOperator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -18,57 +18,27 @@
/**
* A JavaScript operator.
*/
-public abstract class JsOperator {
+public interface JsOperator {
+ int INFIX = 0x02;
+ int LEFT = 0x01;
+ int POSTFIX = 0x04;
+ int PREFIX = 0x08;
- protected static final int LEFT = 0x01;
- protected static final int INFIX = 0x02;
- protected static final int POSTFIX = 0x04;
- protected static final int PREFIX = 0x08;
+ int getPrecedence();
- private final int mask;
+ String getSymbol();
- private final int precedence;
+ boolean isKeyword();
- private final String symbol;
+ boolean isLeftAssociative();
- protected JsOperator(String symbol, int precedence, int mask) {
- this.symbol = symbol;
- this.precedence = precedence;
- this.mask = mask;
- }
+ boolean isPrecedenceLessThan(JsOperator other);
- public int getPrecedence() {
- return precedence;
- }
+ boolean isValidInfix();
- public String getSymbol() {
- return symbol;
- }
+ boolean isValidPostfix();
- public abstract boolean isKeyword();
+ boolean isValidPrefix();
- public boolean isLeftAssociative() {
- return (mask & LEFT) != 0;
- }
-
- public boolean isPrecedenceLessThan(JsOperator other) {
- return precedence < other.precedence;
- }
-
- public boolean isValidInfix() {
- return (mask & INFIX) != 0;
- }
-
- public boolean isValidPostfix() {
- return (mask & POSTFIX) != 0;
- }
-
- public boolean isValidPrefix() {
- return (mask & PREFIX) != 0;
- }
-
- @Override
- public String toString() {
- return symbol;
- }
+ String toString();
}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
index 67c94ef..1a6a5f9 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsScope.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -17,6 +17,7 @@
import com.google.gwt.dev.js.JsKeywords;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -48,7 +49,7 @@
* a qualifier and could therefore never be confused with the global scope
* hierarchy.
*/
-public class JsScope {
+public class JsScope implements Serializable {
/**
* Prevents the client from programmatically creating an illegal ident.
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperator.java b/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperator.java
index fe13ff2..0520f01 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperator.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsUnaryOperator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2007 Google Inc.
+ * 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
@@ -18,35 +18,66 @@
/**
* A JavaScript unary operator.
*/
-public final class JsUnaryOperator extends JsOperator {
- // Precedence indices from "JavaScript - The Definitive Guide" 4th Edition
- // (page 57)
- //
+public enum JsUnaryOperator implements JsOperator {
+ /*
+ * Precedence indices from "JavaScript - The Definitive Guide" 4th Edition
+ * (page 57)
+ */
- public static final JsUnaryOperator BIT_NOT = create("~", 14, PREFIX);
- public static final JsUnaryOperator NEG = create("-", 14, PREFIX);
- public static final JsUnaryOperator NOT = create("!", 14, PREFIX);
- public static final JsUnaryOperator DEC = create("--", 14, POSTFIX | PREFIX);
- public static final JsUnaryOperator INC = create("++", 14, POSTFIX | PREFIX);
- public static final JsUnaryOperator DELETE = create("delete", 14, PREFIX);
- public static final JsUnaryOperator TYPEOF = create("typeof", 14, PREFIX);
- public static final JsUnaryOperator VOID = create("void", 14, PREFIX);
+ BIT_NOT("~", 14, PREFIX), DEC("--", 14, POSTFIX | PREFIX), DELETE("delete",
+ 14, PREFIX), INC("++", 14, POSTFIX | PREFIX), NEG("-", 14, PREFIX), NOT(
+ "!", 14, PREFIX), TYPEOF("typeof", 14, PREFIX), VOID("void", 14, PREFIX);
- private static JsUnaryOperator create(String symbol, int precedence, int mask) {
- JsUnaryOperator op = new JsUnaryOperator(symbol, precedence, mask);
- return op;
- }
+ private final int mask;
+
+ private final int precedence;
+
+ private final String symbol;
private JsUnaryOperator(String symbol, int precedence, int mask) {
- super(symbol, precedence, mask);
+ this.symbol = symbol;
+ this.precedence = precedence;
+ this.mask = mask;
}
- @Override
+ public int getPrecedence() {
+ return precedence;
+ }
+
+ public String getSymbol() {
+ return symbol;
+ }
+
public boolean isKeyword() {
return this == DELETE || this == TYPEOF || this == VOID;
}
+ public boolean isLeftAssociative() {
+ return (mask & LEFT) != 0;
+ }
+
public boolean isModifying() {
return this == DEC || this == INC || this == DELETE;
}
+
+ public boolean isPrecedenceLessThan(JsOperator other) {
+ return precedence < other.getPrecedence();
+ }
+
+ public boolean isValidInfix() {
+ return (mask & INFIX) != 0;
+ }
+
+ public boolean isValidPostfix() {
+ return (mask & POSTFIX) != 0;
+ }
+
+ public boolean isValidPrefix() {
+ return (mask & PREFIX) != 0;
+ }
+
+ @Override
+ public String toString() {
+ return symbol;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
index c338a91..0ee638c 100644
--- a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
@@ -19,7 +19,6 @@
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.core.ext.typeinfo.JClassType;
import com.google.gwt.dev.cfg.PublicOracle;
import com.google.gwt.dev.cfg.Rule;
import com.google.gwt.dev.cfg.Rules;
@@ -59,17 +58,10 @@
throws UnableToCompleteException {
String result = tryRebind(logger, typeName);
+ genCtx.finish(logger);
if (result == null) {
result = typeName;
}
-
- // Announce the newly-generated types.
- //
- JClassType[] genTypes = genCtx.finish(logger);
- if (genTypes.length > 0) {
- onGeneratedTypes(result, genTypes);
- }
-
return result;
}
@@ -169,8 +161,4 @@
return result;
}
-
- protected void onGeneratedTypes(String result, JClassType[] genTypes) {
- }
-
}