Base Framework for Generator Result Caching
Review at http://gwt-code-reviews.appspot.com/1232801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9468 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/GeneratorContextExt.java b/dev/core/src/com/google/gwt/core/ext/GeneratorContextExt.java
new file mode 100644
index 0000000..5dfbea7
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/GeneratorContextExt.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.dev.javac.rebind.CachedRebindResult;
+
+/**
+ * EXPERIMENTAL and subject to change. Do not use this in production code.
+ * <p>
+ * An extension to GeneratorContext which includes access to previously cached
+ * rebind results.
+ * <p>
+ * TODO(jbrosenberg): Merge this into {@link GeneratorContext} directly, once
+ * the api has stabilized and we can remove the "experimental" moniker.
+ */
+public interface GeneratorContextExt extends GeneratorContext {
+
+ /**
+ * Get cached result from a previous run of the current generator, if available.
+ *
+ * @return A {@link com.google.gwt.dev.javac.rebind.CachedRebindResult} object,
+ * if one has been provided to the context. Null is returned if there
+ * is no previous result, or if generator result caching is not enabled.
+ */
+ CachedRebindResult getCachedGeneratorResult();
+
+ /**
+ * Get source last modified time.
+ * <p>
+ * TODO(jbrosenberg): Implement in terms of a getVersion method yet to be
+ * added to TypeOracle, instead of looking for age of a java source file.
+ * This will soon be removed.
+ */
+ long getSourceLastModifiedTime(JClassType sourceType);
+
+ /**
+ * Check whether generator result caching is currently enabled.
+ */
+ boolean isGeneratorResultCachingEnabled();
+
+ /**
+ * Mark a type to be reused from the generator result cache. Calling this
+ * method with a successful response indicates that the calling generator will
+ * not re-generate this type. A cached version of this type will be added
+ * to the context once the calling generator returns from
+ * {@link GeneratorExt#generateIncrementally}, with a result containing
+ * {@link com.google.gwt.dev.javac.rebind.RebindStatus#USE_PARTIAL_CACHED}.
+ *
+ * @param typeName the fully qualified name of a type.
+ * @return true if the requested type is available from the generator result
+ * cache, false otherwise.
+ */
+ boolean reuseTypeFromCacheIfAvailable(String typeName);
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/GeneratorExt.java b/dev/core/src/com/google/gwt/core/ext/GeneratorExt.java
new file mode 100644
index 0000000..2a1313c
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/GeneratorExt.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext;
+
+import com.google.gwt.dev.javac.rebind.RebindResult;
+import com.google.gwt.dev.javac.rebind.RebindStatus;
+
+/**
+ * EXPERIMENTAL and subject to change. Do not use this in production code.
+ * <p>
+ * Adds a new {@link #generateIfNecessary} method.
+ * <p>
+ * TODO(jbrosenberg): Merge this into {@link Generator} directly, once the api
+ * has stabilized and we can remove the "experimental" moniker.
+ */
+public abstract class GeneratorExt extends Generator {
+
+ /**
+ * A wrapper class for using old style {@link Generator} implementations where
+ * a GeneratorExt instance is needed.
+ */
+ private static class BaseGeneratorWrapper extends GeneratorExt {
+ final Generator baseGenerator;
+
+ public BaseGeneratorWrapper(Generator baseGenerator) {
+ this.baseGenerator = baseGenerator;
+ }
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+ return this.baseGenerator.generate(logger, context, typeName);
+ }
+ }
+
+ /**
+ * Get a new instance wrapped from an old style {@link Generator}
+ * implementation.
+ */
+ public static GeneratorExt getWrappedInstance(Generator baseGenerator) {
+ return new BaseGeneratorWrapper(baseGenerator);
+ }
+
+ /**
+ * A default implementation of the abstract method defined in the old style
+ * {@link Generator}.
+ * <p>
+ * Note, it is recommended that {@link #generateIncrementally} be used instead.
+ *
+ * @return the name of a subclass to substitute for the requested class, or
+ * return <code>null</code> to cause the requested type itself to be
+ * used
+ */
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+ // to override (implementing generateIncrementally instead is recommended)
+ return null;
+ }
+
+ /**
+ * Incrementally generate a default constructible subclass of the requested
+ * type. The generator can use information from the context to determine
+ * whether it needs to regenerate everything, or whether it can selectively
+ * regenerate a subset of its output, or whether it can return quickly to
+ * allow use of all previously cached objects. It will return a
+ * {@link RebindResult}, which contains a {@link RebindStatus} field
+ * indicating whether to use previously cached artifacts, newly generated
+ * ones, or a partial mixture of both cached and newly generated objects.
+ * <p>
+ * The result also includes a field for the name of the subclass to
+ * substitute for the requested class.
+ * <p>
+ * For backwards compatibility, the default implementation calls the old-style
+ * generate() method, and doesn't attempt any generator result caching.
+ * <p>
+ * The generator throws an <code>UnableToCompleteException</code> if for
+ * any reason it cannot complete successfully.
+ *
+ * @return a GeneratorResult
+ */
+ public RebindResult generateIncrementally(TreeLogger logger,
+ GeneratorContextExt context, String typeName)
+ throws UnableToCompleteException {
+
+ // to override (default implementation calls unconditional generate() method)
+
+ RebindStatus status;
+ String resultTypeName = generate(logger, context, typeName);
+ if (resultTypeName == null) {
+ status = RebindStatus.USE_EXISTING;
+ resultTypeName = typeName;
+ } else {
+ status = RebindStatus.USE_ALL_NEW_WITH_NO_CACHING;
+ }
+
+ return new RebindResult(status, resultTypeName);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/DevModeBase.java b/dev/core/src/com/google/gwt/dev/DevModeBase.java
index cdec046..2bb3092 100644
--- a/dev/core/src/com/google/gwt/dev/DevModeBase.java
+++ b/dev/core/src/com/google/gwt/dev/DevModeBase.java
@@ -23,6 +23,7 @@
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.javac.CompilationState;
+import com.google.gwt.dev.javac.rebind.RebindCache;
import com.google.gwt.dev.jjs.JJSOptions;
import com.google.gwt.dev.shell.ArtifactAcceptor;
import com.google.gwt.dev.shell.BrowserChannelServer;
@@ -38,6 +39,7 @@
import com.google.gwt.dev.ui.DoneCallback;
import com.google.gwt.dev.ui.DoneEvent;
import com.google.gwt.dev.util.BrowserInfo;
+import com.google.gwt.dev.util.arg.ArgHandlerEnableGeneratorResultCaching;
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
import com.google.gwt.dev.util.arg.OptionGenDir;
@@ -662,6 +664,7 @@
registerHandler(new ArgHandlerPort(options));
registerHandler(new ArgHandlerWhitelist());
registerHandler(new ArgHandlerBlacklist());
+ registerHandler(new ArgHandlerEnableGeneratorResultCaching(options));
registerHandler(new ArgHandlerLogDir(options));
registerHandler(new ArgHandlerLogLevel(options));
registerHandler(new ArgHandlerGenDir(options));
@@ -737,6 +740,8 @@
private boolean headlessMode = false;
+ private Map<String, RebindCache> rebindCaches = null;
+
private boolean started;
private TreeLogger topLogger;
@@ -838,7 +843,8 @@
ArtifactAcceptor artifactAcceptor = createArtifactAcceptor(logger,
moduleDef);
return new ShellModuleSpaceHost(logger, compilationState, moduleDef,
- options.getGenDir(), artifactAcceptor);
+ options.getGenDir(), artifactAcceptor,
+ getRebindCache(moduleDef.getName()));
}
protected abstract void doShutDownServer();
@@ -1034,7 +1040,6 @@
Event startupEvent = SpeedTracerLogger.start(DevModeEventType.STARTUP);
try {
- boolean result = false;
// See if there was a UI specified by command-line args
ui = createUI();
@@ -1147,6 +1152,24 @@
return newUI;
}
+ private RebindCache getRebindCache(String moduleName) {
+
+ if (!options.isGeneratorResultCachingEnabled()) {
+ return null;
+ }
+
+ if (rebindCaches == null) {
+ rebindCaches = new HashMap<String, RebindCache>();
+ }
+
+ RebindCache cache = rebindCaches.get(moduleName);
+ if (cache == null) {
+ cache = new RebindCache();
+ rebindCaches.put(moduleName, cache);
+ }
+ return cache;
+ }
+
/**
* Perform hosted mode relink when new artifacts are generated, without
* overwriting newer or unmodified files in the output folder.
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index 830e240..cb9efac 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -202,6 +202,10 @@
return enableGeneratingOnShards;
}
+ public boolean isGeneratorResultCachingEnabled() {
+ return jjsOptions.isGeneratorResultCachingEnabled();
+ }
+
public boolean isOptimizePrecompile() {
return jjsOptions.isOptimizePrecompile();
}
@@ -261,6 +265,10 @@
public void setGenDir(File genDir) {
this.genDir = genDir;
}
+
+ public void setGeneratorResultCachingEnabled(boolean enabled) {
+ jjsOptions.setGeneratorResultCachingEnabled(enabled);
+ }
public void setMaxPermsPerPrecompile(int maxPermsPerPrecompile) {
this.maxPermsPerPrecompile = maxPermsPerPrecompile;
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
index d90ac5f6..6fccfee 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -264,6 +264,17 @@
return null;
}
+ /**
+ * Returns the Resource for a source file if it is found; <code>null</code>
+ * otherwise.
+ *
+ * @param partialPath the partial path of the source file
+ * @return the resource for the requested source file
+ */
+ public synchronized Resource findSourceFile(String partialPath) {
+ return lazySourceOracle.getResourceMap().get(partialPath);
+ }
+
public Set<String> getActiveLinkerNames() {
return new LinkedHashSet<String>(activeLinkers);
}
@@ -435,19 +446,6 @@
}
/**
- * Returns the URL for a source file if it is found; <code>false</code>
- * otherwise.
- *
- * NOTE: this method is for testing only.
- *
- * @param partialPath the partial path of the source file
- * @return the resource for the requested source file
- */
- synchronized Resource findSourceFile(String partialPath) {
- return lazySourceOracle.getResourceMap().get(partialPath);
- }
-
- /**
* The final method to call when everything is setup. Before calling this
* method, several of the getter methods may not be called. After calling this
* method, the add methods may not be called.
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Rule.java b/dev/core/src/com/google/gwt/dev/cfg/Rule.java
index 65d66b0..a24e1ca 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/Rule.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/Rule.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.javac.rebind.RebindResult;
/**
* Abstract base class for various kinds of deferred binding rules.
@@ -38,7 +39,7 @@
context.getTypeOracle(), typeName));
}
- public abstract String realize(TreeLogger logger,
+ public abstract RebindResult realize(TreeLogger logger,
StandardGeneratorContext context, String typeName)
throws UnableToCompleteException;
diff --git a/dev/core/src/com/google/gwt/dev/cfg/RuleFail.java b/dev/core/src/com/google/gwt/dev/cfg/RuleFail.java
index 87636ee..89c0fd0 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/RuleFail.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/RuleFail.java
@@ -18,14 +18,15 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.javac.rebind.RebindResult;
/**
- * A rule to explicitly fail during a deferred binding requrest.
+ * A rule to explicitly fail during a deferred binding request.
*/
public class RuleFail extends Rule {
@Override
- public String realize(TreeLogger logger, StandardGeneratorContext context,
+ public RebindResult realize(TreeLogger logger, StandardGeneratorContext context,
String typeName) throws UnableToCompleteException {
logger.log(TreeLogger.ERROR, "Deferred binding request failed for type '"
+ typeName + "'", null);
diff --git a/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java b/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
index f2b4d57..e41b51d 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/RuleGenerateWith.java
@@ -19,6 +19,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.javac.rebind.RebindResult;
/**
* A rule to replace the type being rebound with a class whose name is
@@ -34,9 +35,10 @@
}
@Override
- public String realize(TreeLogger logger, StandardGeneratorContext context,
- String typeName) throws UnableToCompleteException {
- return context.runGenerator(logger, generatorClass, typeName);
+ public RebindResult realize(TreeLogger logger,
+ StandardGeneratorContext context, String typeName)
+ throws UnableToCompleteException {
+ return context.runGeneratorIncrementally(logger, generatorClass, typeName);
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/cfg/RuleReplaceWith.java b/dev/core/src/com/google/gwt/dev/cfg/RuleReplaceWith.java
index 02766a4..4f4f273 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/RuleReplaceWith.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/RuleReplaceWith.java
@@ -18,6 +18,8 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.javac.rebind.RebindResult;
+import com.google.gwt.dev.javac.rebind.RebindStatus;
/**
* A rule to replace the type being rebound with an explicitly named class.
@@ -35,9 +37,11 @@
}
@Override
- public String realize(TreeLogger logger, StandardGeneratorContext context,
- String typeName) throws UnableToCompleteException {
- return replacementTypeName;
+ public RebindResult realize(TreeLogger logger,
+ StandardGeneratorContext context, String typeName)
+ throws UnableToCompleteException {
+ return new RebindResult(
+ RebindStatus.USE_EXISTING, replacementTypeName);
}
@Override
diff --git a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
index d46c927..bc7d51c 100644
--- a/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
+++ b/dev/core/src/com/google/gwt/dev/javac/StandardGeneratorContext.java
@@ -17,6 +17,8 @@
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.GeneratorContextExt;
+import com.google.gwt.core.ext.GeneratorExt;
import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
@@ -24,9 +26,14 @@
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.GeneratedResource;
import com.google.gwt.core.ext.linker.impl.StandardGeneratedResource;
+import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.dev.cfg.ModuleDef;
+import com.google.gwt.dev.javac.rebind.CachedRebindResult;
+import com.google.gwt.dev.javac.rebind.RebindResult;
+import com.google.gwt.dev.javac.rebind.RebindStatus;
+import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.resource.ResourceOracle;
import com.google.gwt.dev.util.DiskCache;
import com.google.gwt.dev.util.Util;
@@ -45,17 +52,18 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
-import java.util.Map.Entry;
/**
* Manages generators and generated units during a single compilation.
*/
-public class StandardGeneratorContext implements GeneratorContext {
+public class StandardGeneratorContext implements GeneratorContextExt {
/**
* Extras added to {@link CompilationUnit}.
@@ -226,7 +234,8 @@
private static DiskCache diskCache = new DiskCache();
- private static final Map<String,CompilerEventType> eventsByGeneratorType = new HashMap<String,CompilerEventType>();
+ private static final Map<String,CompilerEventType> eventsByGeneratorType =
+ new HashMap<String,CompilerEventType>();
static {
eventsByGeneratorType.put(
"com.google.gwt.resources.rebind.context.InlineClientBundleGenerator",
@@ -255,7 +264,8 @@
private final ArtifactSet allGeneratedArtifacts;
- private final Set<GeneratedUnit> committedGeneratedCups = new HashSet<GeneratedUnit>();
+ private final Map<String, GeneratedUnit> committedGeneratedCups =
+ new HashMap<String, GeneratedUnit>();
private CompilationState compilationState;
@@ -263,7 +273,8 @@
private final File genDir;
- private final Map<Class<? extends Generator>, Generator> generators = new IdentityHashMap<Class<? extends Generator>, Generator>();
+ private final Map<Class<? extends Generator>, Generator> generators =
+ new IdentityHashMap<Class<? extends Generator>, Generator>();
private final ModuleDef module;
@@ -271,11 +282,19 @@
private final Set<String> newlyGeneratedTypeNames = new HashSet<String>();
- private final Map<String, PendingResource> pendingResources = new HashMap<String, PendingResource>();
+ private final Map<String, PendingResource> pendingResources =
+ new HashMap<String, PendingResource>();
private transient PropertyOracle propOracle;
- private final Map<PrintWriter, Generated> uncommittedGeneratedCupsByPrintWriter = new IdentityHashMap<PrintWriter, Generated>();
+ private final Map<PrintWriter, Generated> uncommittedGeneratedCupsByPrintWriter =
+ new IdentityHashMap<PrintWriter, Generated>();
+
+ private CachedRebindResult cachedRebindResult = null;
+
+ private boolean generatorResultCachingEnabled = false;
+
+ private List<String> cachedTypeNamesToReuse = null;
/**
* Normally, the compiler host would be aware of the same types that are
@@ -288,6 +307,53 @@
this.genDir = genDir;
this.allGeneratedArtifacts = allGeneratedArtifacts;
}
+
+ /**
+ * Adds a generated unit to the context if not already present, but will not
+ * overwrite an existing unit.
+ */
+ public void addGeneratedUnit(GeneratedUnit gu) {
+ if (!committedGeneratedCups.containsKey(gu.getTypeName())) {
+ committedGeneratedCups.put(gu.getTypeName(), gu);
+ }
+ }
+
+ /**
+ * Adds generated units to the context, but will not overwrite any existing
+ * units that might already be present.
+ */
+ public void addGeneratedUnits(Collection<GeneratedUnit> generatedUnits) {
+ for (GeneratedUnit gu : generatedUnits) {
+ addGeneratedUnit(gu);
+ }
+ }
+
+ /**
+ * Adds all available cached generated units to the context. Existing units
+ * for a given type will not be overwritten.
+ */
+ public void addGeneratedUnitsFromCachedRebindResult() {
+ if (cachedRebindResult != null
+ && cachedRebindResult.getGeneratedUnits() != null) {
+ addGeneratedUnits(cachedRebindResult.getGeneratedUnits());
+ }
+ }
+
+ /**
+ * Adds cached generated units to the context that have been marked for reuse.
+ * Existing units for a given type will not be overwritten.
+ */
+ public void addGeneratedUnitsMarkedForReuseFromCache() {
+ if (cachedTypeNamesToReuse != null
+ && cachedRebindResult != null) {
+ for (String typeName : cachedTypeNamesToReuse) {
+ GeneratedUnit gu = cachedRebindResult.getGeneratedUnit(typeName);
+ if (gu != null) {
+ addGeneratedUnit(gu);
+ }
+ }
+ }
+ }
/**
* Frees memory used up by compilation state.
@@ -305,7 +371,7 @@
if (gcup != null) {
gcup.commit();
uncommittedGeneratedCupsByPrintWriter.remove(pw);
- committedGeneratedCups.add(gcup);
+ committedGeneratedCups.put(gcup.getTypeName(), gcup);
} else {
logger.log(TreeLogger.WARN,
"Generator attempted to commit an unknown PrintWriter", null);
@@ -313,14 +379,28 @@
}
/**
- * Adds an Artifact to the ArtifactSet if one has been provided to the
- * context.
+ * Adds an Artifact to the context's ArtifactSets. This will replace a
+ * pre-existing entry in allGeneratedArtifacts, but will not overwrite an
+ * entry in the newlyGeneratedArtifacts (since it is assumed by convention
+ * that only new entries will ever be inserted here for a given generator run).
*/
public void commitArtifact(TreeLogger logger, Artifact<?> artifact) {
allGeneratedArtifacts.replace(artifact);
newlyGeneratedArtifacts.add(artifact);
}
+ /**
+ * Commits all available cached Artifacts to the context.
+ */
+ public void commitArtifactsFromCachedRebindResult(TreeLogger logger) {
+ if (cachedRebindResult != null
+ && cachedRebindResult.getArtifacts() != null) {
+ for (Artifact<?> art : cachedRebindResult.getArtifacts()) {
+ commitArtifact(logger, art);
+ }
+ }
+ }
+
public GeneratedResource commitResource(TreeLogger logger, OutputStream os)
throws UnableToCompleteException {
@@ -358,9 +438,6 @@
public final ArtifactSet finish(TreeLogger logger) {
abortUncommittedResources(logger);
- // Process pending generated types.
- List<String> genTypeNames = new ArrayList<String>();
-
try {
TreeLogger branch;
if (!committedGeneratedCups.isEmpty()) {
@@ -375,16 +452,15 @@
"Generated source files...", null);
}
- for (GeneratedUnit gcup : committedGeneratedCups) {
+ for (GeneratedUnit gcup : committedGeneratedCups.values()) {
String qualifiedTypeName = gcup.getTypeName();
- genTypeNames.add(qualifiedTypeName);
if (subBranch != null) {
subBranch.log(TreeLogger.DEBUG, qualifiedTypeName, null);
}
}
compilationState.addGeneratedCompilationUnits(logger,
- committedGeneratedCups);
+ committedGeneratedCups.values());
}
return newlyGeneratedArtifacts;
} finally {
@@ -403,6 +479,8 @@
committedGeneratedCups.clear();
newlyGeneratedTypeNames.clear();
newlyGeneratedArtifacts = new ArtifactSet();
+ cachedRebindResult = null;
+ cachedTypeNamesToReuse = null;
}
}
@@ -410,10 +488,31 @@
return module.getActiveLinkerNames();
}
+ /**
+ * Gets newly committed artifacts.
+ */
+ public ArtifactSet getArtifacts() {
+ return new ArtifactSet(newlyGeneratedArtifacts);
+ }
+
+ /**
+ * Gets the previously cached rebind result for the current generator.
+ */
+ public CachedRebindResult getCachedGeneratorResult() {
+ return cachedRebindResult;
+ }
+
public GeneratorContext getCanonicalContext() {
return this;
}
+ /**
+ * Gets all committed Java units.
+ */
+ public Map<String, GeneratedUnit> getGeneratedUnitMap() {
+ return committedGeneratedCups;
+ }
+
public final PropertyOracle getPropertyOracle() {
return propOracle;
}
@@ -421,14 +520,105 @@
public ResourceOracle getResourcesOracle() {
return module.getResourcesOracle();
}
-
+
+ /**
+ * EXPERIMENTAL and subject to change. Do not use this in production code.
+ *
+ * Temporary solution to get last modified time for a sourceType. Finds the
+ * the source file, if possible. Note, this won't work for sources contained
+ * in jar files, or for recently generated source files.
+ *
+ * TODO(jbrosenberg): Replace this method by using a getVersion() method from
+ * TypeOracle (still under development).
+ */
+ public long getSourceLastModifiedTime(JClassType sourceType) {
+
+ while (sourceType instanceof JArrayType) {
+ sourceType = (JClassType) ((JArrayType) sourceType).getComponentType();
+ }
+
+ JClassType enclosingType;
+ while ((enclosingType = sourceType.getEnclosingType()) != null) {
+ sourceType = enclosingType;
+ }
+
+ String sourceName = sourceType.getQualifiedSourceName();
+ String sourcePath = sourceName.replace('.', '/') + ".java";
+
+ Resource sourceResource = module.findSourceFile(sourcePath);
+
+ if (sourceResource == null) {
+ return 0L;
+ }
+
+ return sourceResource.getLastModified();
+ }
+
public final TypeOracle getTypeOracle() {
return compilationState.getTypeOracle();
}
-
+
+ public boolean isGeneratorResultCachingEnabled() {
+ return generatorResultCachingEnabled;
+ }
+
+ /**
+ * Adds a type name to the list of types to be reused from cache, if available.
+ *
+ * @param typeName The fully qualified name of a type.
+ *
+ * @return true, if the type is available in the cache and was successfully
+ * added to the list for reuse, false otherwise.
+ */
+ public boolean reuseTypeFromCacheIfAvailable(String typeName) {
+ if (!isGeneratorResultCachingEnabled() ||
+ cachedRebindResult == null ||
+ !cachedRebindResult.isTypeCached(typeName)) {
+ return false;
+ }
+
+ if (cachedTypeNamesToReuse == null) {
+ cachedTypeNamesToReuse = new ArrayList<String>();
+ }
+ cachedTypeNamesToReuse.add(typeName);
+ return true;
+ }
+
+ /**
+ * This method is maintained for backwards compatibility.
+ * {@link #runGeneratorIncrementally} should be used instead.
+ */
public String runGenerator(TreeLogger logger,
Class<? extends Generator> generatorClass, String typeName)
throws UnableToCompleteException {
+
+ RebindResult result =
+ runGeneratorIncrementally(logger, generatorClass, typeName);
+
+ return result.getReturnedTypeName();
+ }
+
+ /**
+ * Runs a generator incrementally, with support for managing the returned
+ * {@link RebindResult} object, which can contain status and cached results.
+ * This is a replacement for the {@link #runGenerator} method.
+ * <p>
+ * If the passed in generatorClass is an instance of {@link GeneratorExt}, it's
+ * {@link GeneratorExt#generateIncrementally} method will be called.
+ * <p>
+ * Otherwise, for backwards compatibility, the generatorClass will be wrapped
+ * in a {@link GeneratorExt} instance, and it's {@link Generator#generate}
+ * method will be called.
+ *
+ * @param logger
+ * @param generatorClass
+ * @param typeName
+ * @return a RebindResult
+ * @throws UnableToCompleteException
+ */
+ public RebindResult runGeneratorIncrementally(TreeLogger logger,
+ Class<? extends Generator> generatorClass, String typeName)
+ throws UnableToCompleteException {
String msg = "Invoking generator " + generatorClass.getName();
logger = logger.branch(TreeLogger.DEBUG, msg, null);
@@ -459,16 +649,26 @@
generatorClassName, "type", typeName);
try {
- String className = generator.generate(logger, this, typeName);
- long after = System.currentTimeMillis();
- if (className == null) {
- msg = "Generator returned null, so the requested type will be used as is";
+
+ GeneratorExt generatorExt;
+ if (generator instanceof GeneratorExt) {
+ generatorExt = (GeneratorExt) generator;
} else {
- msg = "Generator returned class '" + className + "'";
+ generatorExt = GeneratorExt.getWrappedInstance(generator);
+ }
+
+ RebindResult result;
+ result = generatorExt.generateIncrementally(logger, this, typeName);
+
+ long after = System.currentTimeMillis();
+ if (result.getResultStatus() == RebindStatus.USE_EXISTING) {
+ msg = "Generator did not return a new class, type will be used as is";
+ } else {
+ msg = "Generator returned class '" + result.getReturnedTypeName() + "'";
}
msg += "; in " + (after - before) + " ms";
logger.log(TreeLogger.DEBUG, msg, null);
- return className;
+ return result;
} catch (AssertionError e) {
// Catch and log the assertion as a convenience to the developer
logger.log(TreeLogger.ERROR, "Generator '" + generatorClass.getName()
@@ -483,9 +683,20 @@
}
}
+ /**
+ * Set previously cached rebind result for currently active generator.
+ */
+ public void setCachedRebindResult(CachedRebindResult cachedRebindResult) {
+ this.cachedRebindResult = cachedRebindResult;
+ }
+
public void setCurrentGenerator(Class<? extends Generator> currentGenerator) {
this.currentGenerator = currentGenerator;
}
+
+ public void setGeneratorResultCachingEnabled(boolean enabled) {
+ this.generatorResultCachingEnabled = enabled;
+ }
/**
* Sets the current transient property oracle to answer current property
diff --git a/dev/core/src/com/google/gwt/dev/javac/rebind/CachedPropertyInformation.java b/dev/core/src/com/google/gwt/dev/javac/rebind/CachedPropertyInformation.java
new file mode 100644
index 0000000..fd20b0f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/rebind/CachedPropertyInformation.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.javac.rebind;
+
+import com.google.gwt.core.ext.BadPropertyValueException;
+import com.google.gwt.core.ext.ConfigurationProperty;
+import com.google.gwt.core.ext.PropertyOracle;
+import com.google.gwt.core.ext.SelectionProperty;
+import com.google.gwt.core.ext.TreeLogger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * A container for saving lists of deferred-binding and configuration properties
+ * to be compared subsequently with a PropertyOracle.
+ */
+public class CachedPropertyInformation {
+
+ private final List<SelectionProperty> selectionProperties;
+ private final List<ConfigurationProperty> configProperties;
+
+ public CachedPropertyInformation(TreeLogger logger, PropertyOracle oracle,
+ Collection<String> selectionPropertyNames,
+ Collection<String> configPropertyNames) {
+
+ if (oracle == null) {
+ selectionProperties = null;
+ configProperties = null;
+ return;
+ }
+
+ if (selectionPropertyNames == null) {
+ selectionProperties = null;
+ } else {
+ selectionProperties = new ArrayList<SelectionProperty>();
+ for (String name : selectionPropertyNames) {
+ try {
+ SelectionProperty selProp = oracle.getSelectionProperty(logger, name);
+ selectionProperties.add(selProp);
+ } catch (BadPropertyValueException e) {
+ }
+ }
+ }
+
+ if (configPropertyNames == null) {
+ configProperties = null;
+ } else {
+ configProperties = new ArrayList<ConfigurationProperty>();
+ for (String name : configPropertyNames) {
+ try {
+ ConfigurationProperty configProp = oracle.getConfigurationProperty(name);
+ configProperties.add(configProp);
+ } catch (BadPropertyValueException e) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Check a previously cached set of deferred-binding and configuration
+ * properties with the provided property oracle.
+ */
+ public boolean checkPropertiesWithPropertyOracle(TreeLogger logger,
+ PropertyOracle oracle) {
+
+ if (selectionProperties != null) {
+ try {
+ for (SelectionProperty selProp : selectionProperties) {
+ SelectionProperty currProp =
+ oracle.getSelectionProperty(logger, selProp.getName());
+ if (!currProp.getCurrentValue().equals(selProp.getCurrentValue())) {
+ return false;
+ }
+ }
+ } catch (BadPropertyValueException e) {
+ return false;
+ }
+ }
+
+ if (configProperties != null) {
+ try {
+ for (ConfigurationProperty configProp : configProperties) {
+ ConfigurationProperty currProp =
+ oracle.getConfigurationProperty(configProp.getName());
+ if (!currProp.equals(configProp)) {
+ return false;
+ }
+ }
+ } catch (BadPropertyValueException e) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/rebind/CachedRebindResult.java b/dev/core/src/com/google/gwt/dev/javac/rebind/CachedRebindResult.java
new file mode 100644
index 0000000..cd8e3c8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/rebind/CachedRebindResult.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.javac.rebind;
+
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.dev.javac.GeneratedUnit;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class to represent the results from a rebind operation. This can be
+ * cached and presented to subsequent rebind operations, providing the generator
+ * information needed to decide whether full or partial re-generation is required.
+ */
+public class CachedRebindResult {
+ private final ArtifactSet artifacts;
+ private final Map<String, GeneratedUnit> generatedUnitMap;
+ private final String returnedTypeName;
+ private final long timeGenerated;
+ private final Object clientData;
+
+ public CachedRebindResult(String resultTypeName, ArtifactSet artifacts,
+ Map<String, GeneratedUnit> generatedUnitMap,
+ long timeGenerated, Object clientData) {
+ this.returnedTypeName = resultTypeName;
+ this.artifacts = new ArtifactSet(artifacts);
+ this.generatedUnitMap = new HashMap<String, GeneratedUnit>(generatedUnitMap);
+ this.timeGenerated = timeGenerated;
+ this.clientData = clientData;
+ }
+
+ public CachedRebindResult(String resultTypeName, ArtifactSet artifacts,
+ Map<String, GeneratedUnit> generatedUnitMap, long timeGenerated) {
+ this(resultTypeName, artifacts, generatedUnitMap, timeGenerated, null);
+ }
+
+ public ArtifactSet getArtifacts() {
+ return artifacts;
+ }
+
+ public Object getClientData() {
+ return clientData;
+ }
+
+ public GeneratedUnit getGeneratedUnit(String typeName) {
+ return generatedUnitMap.get(typeName);
+ }
+
+ public Collection<GeneratedUnit> getGeneratedUnits() {
+ return generatedUnitMap.values();
+ }
+
+ public String getReturnedTypeName() {
+ return returnedTypeName;
+ }
+
+ public long getTimeGenerated() {
+ return timeGenerated;
+ }
+
+ public boolean isTypeCached(String typeName) {
+ return generatedUnitMap.containsKey(typeName);
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/javac/rebind/RebindCache.java b/dev/core/src/com/google/gwt/dev/javac/rebind/RebindCache.java
new file mode 100644
index 0000000..ae59a51
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/rebind/RebindCache.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.javac.rebind;
+
+import com.google.gwt.dev.cfg.Rule;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A cache for storing {@link CachedRebindResult} entries. Entries are keyed
+ * by rebind Rule and queryTypeName.
+ */
+public class RebindCache {
+
+ private final Map<String, Map<String, CachedRebindResult>> rebindResults;
+
+ public RebindCache() {
+ rebindResults = new HashMap<String, Map<String, CachedRebindResult>>();
+ }
+
+ public CachedRebindResult get(Rule rule, String queryTypeName) {
+ Map<String, CachedRebindResult> ruleResults;
+ ruleResults = rebindResults.get(rule.toString());
+ if (ruleResults != null) {
+ return ruleResults.get(queryTypeName);
+ }
+
+ return null;
+ }
+
+ public void invalidate() {
+ rebindResults.clear();
+ }
+
+ public void put(Rule rule, String queryTypeName, CachedRebindResult results) {
+ Map<String, CachedRebindResult> ruleResults = rebindResults.get(rule.toString());
+ if (ruleResults == null) {
+ ruleResults = new HashMap<String, CachedRebindResult>();
+ rebindResults.put(rule.toString(), ruleResults);
+ }
+ ruleResults.put(queryTypeName, results);
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/javac/rebind/RebindResult.java b/dev/core/src/com/google/gwt/dev/javac/rebind/RebindResult.java
new file mode 100644
index 0000000..5ec7df0
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/rebind/RebindResult.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.javac.rebind;
+
+/**
+ * A class for returning the result of a rebind operation.
+ */
+public class RebindResult {
+
+ private final RebindStatus resultStatus;
+ private final String returnedTypeName;
+ private final Object clientData;
+
+ public RebindResult(RebindStatus resultStatus,
+ String returnedType, Object clientData) {
+ this.resultStatus = resultStatus;
+ this.returnedTypeName = returnedType;
+ this.clientData = clientData;
+ }
+
+ public RebindResult(RebindStatus resultStatus, String returnedType) {
+ this(resultStatus, returnedType, null);
+ }
+
+ public Object getClientData() {
+ return clientData;
+ }
+
+ public RebindStatus getResultStatus() {
+ return resultStatus;
+ }
+
+ public String getReturnedTypeName() {
+ return returnedTypeName;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/rebind/RebindStatus.java b/dev/core/src/com/google/gwt/dev/javac/rebind/RebindStatus.java
new file mode 100644
index 0000000..3506723
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/rebind/RebindStatus.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.javac.rebind;
+
+/**
+ * The returned status for a rebind result. This status is used by the
+ * {@link com.google.gwt.dev.shell.StandardRebindOracle} implementation to
+ * determine how to integrate the result into the system. In the descriptions
+ * below, the "products" of a rebind result can include updated type
+ * information, newly generated artifacts, and newly generated compilation units.
+ */
+public enum RebindStatus {
+
+ /**
+ * Indicates nothing new was created, use pre-existing type information.
+ */
+ USE_EXISTING,
+
+ /**
+ * Indicates only newly generated products should be used.
+ */
+ USE_ALL_NEW,
+
+ /**
+ * Indicates only newly generated products should be used, and no results
+ * should be cached, such as in the case where no caching can be taken
+ * advantage of.
+ */
+ USE_ALL_NEW_WITH_NO_CACHING,
+
+ /**
+ * Indicates only previously cached products should be used.
+ */
+ USE_ALL_CACHED,
+
+ /**
+ * Indicates that a mixture of newly generated and previously cached products
+ * should be used.
+ */
+ USE_PARTIAL_CACHED
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
index 96226c3..7b2bd41 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -20,6 +20,7 @@
import com.google.gwt.dev.util.arg.OptionDisableCastChecking;
import com.google.gwt.dev.util.arg.OptionDisableClassMetadata;
import com.google.gwt.dev.util.arg.OptionEnableAssertions;
+import com.google.gwt.dev.util.arg.OptionEnableGeneratorResultCaching;
import com.google.gwt.dev.util.arg.OptionOptimize;
import com.google.gwt.dev.util.arg.OptionOptimizePrecompile;
import com.google.gwt.dev.util.arg.OptionRunAsyncEnabled;
@@ -33,7 +34,8 @@
*/
public interface JJSOptions extends OptionOptimize, OptionAggressivelyOptimize,
OptionDisableClassMetadata, OptionDisableCastChecking,
- OptionEnableAssertions, OptionRunAsyncEnabled, OptionScriptStyle,
+ OptionEnableAssertions, OptionEnableGeneratorResultCaching,
+ OptionRunAsyncEnabled, OptionScriptStyle,
OptionSoycEnabled, OptionSoycDetailed, OptionOptimizePrecompile,
OptionStrict, OptionCompilerMetricsEnabled {
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
index af63621..322915a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
@@ -28,6 +28,7 @@
private boolean disableCastChecking = false;
private boolean disableClassMetadata = false;
private boolean enableAssertions;
+ private boolean enableGeneratorResultCaching = false;
private boolean optimizePrecompile = false;
private JsOutputOption output = JsOutputOption.OBFUSCATED;
private boolean runAsyncEnabled = true;
@@ -45,11 +46,12 @@
}
public void copyFrom(JJSOptions other) {
+ setAggressivelyOptimize(other.isAggressivelyOptimize());
setCastCheckingDisabled(other.isCastCheckingDisabled());
setClassMetadataDisabled(other.isClassMetadataDisabled());
setCompilerMetricsEnabled(other.isCompilerMetricsEnabled());
setEnableAssertions(other.isEnableAssertions());
- setAggressivelyOptimize(other.isAggressivelyOptimize());
+ setGeneratorResultCachingEnabled(other.isGeneratorResultCachingEnabled());
setOptimizationLevel(other.getOptimizationLevel());
setOutput(other.getOutput());
setRunAsyncEnabled(other.isRunAsyncEnabled());
@@ -89,6 +91,10 @@
public boolean isEnableAssertions() {
return enableAssertions;
}
+
+ public boolean isGeneratorResultCachingEnabled() {
+ return enableGeneratorResultCaching;
+ }
public boolean isOptimizePrecompile() {
return optimizePrecompile;
@@ -129,6 +135,10 @@
public void setEnableAssertions(boolean enableAssertions) {
this.enableAssertions = enableAssertions;
}
+
+ public void setGeneratorResultCachingEnabled(boolean enabled) {
+ this.enableGeneratorResultCaching = enabled;
+ }
public void setOptimizationLevel(int level) {
optimizationLevel = level;
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
index 72d7305..2c1ddf4 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellModuleSpaceHost.java
@@ -22,6 +22,7 @@
import com.google.gwt.dev.cfg.Rules;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.javac.rebind.RebindCache;
import com.google.gwt.dev.util.log.speedtracer.DevModeEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
@@ -53,18 +54,20 @@
private ModuleSpace space;
+ private RebindCache rebindCache;
+
/**
* @param module the module associated with the hosted module space
- * @param saveJsni
*/
public ShellModuleSpaceHost(TreeLogger logger,
CompilationState compilationState, ModuleDef module, File genDir,
- ArtifactAcceptor artifactAcceptor) {
+ ArtifactAcceptor artifactAcceptor, RebindCache rebindCache) {
this.logger = logger;
this.compilationState = compilationState;
this.module = module;
this.genDir = genDir;
this.artifactAcceptor = artifactAcceptor;
+ this.rebindCache = rebindCache;
}
public CompilingClassLoader getClassLoader() {
@@ -98,7 +101,12 @@
Rules rules = module.getRules();
StandardGeneratorContext genCtx = new StandardGeneratorContext(
compilationState, module, genDir, new ArtifactSet());
+
+ // Only enable generator result caching if we have a valid rebindCache
+ genCtx.setGeneratorResultCachingEnabled((rebindCache != null));
+
rebindOracle = new StandardRebindOracle(propOracle, rules, genCtx);
+ rebindOracle.setRebindCache(rebindCache);
// Create a completely isolated class loader which owns all classes
// associated with a particular module. This effectively builds a
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 23ee903..5736eb5 100644
--- a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
@@ -22,6 +22,10 @@
import com.google.gwt.dev.cfg.Rule;
import com.google.gwt.dev.cfg.Rules;
import com.google.gwt.dev.javac.StandardGeneratorContext;
+import com.google.gwt.dev.javac.rebind.CachedRebindResult;
+import com.google.gwt.dev.javac.rebind.RebindCache;
+import com.google.gwt.dev.javac.rebind.RebindResult;
+import com.google.gwt.dev.javac.rebind.RebindStatus;
import com.google.gwt.dev.jdt.RebindOracle;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.log.speedtracer.DevModeEventType;
@@ -55,7 +59,27 @@
Event rebindEvent = SpeedTracerLogger.start(DevModeEventType.REBIND, "Type Name", typeName);
try {
genCtx.setPropertyOracle(propOracle);
- String result = tryRebind(logger, typeName);
+ Rule rule = getRebindRule(logger, typeName);
+
+ if (rule == null) {
+ return typeName;
+ }
+
+ CachedRebindResult cachedResult = rebindCacheGet(rule, typeName);
+ if (cachedResult != null) {
+ genCtx.setCachedRebindResult(cachedResult);
+ }
+
+ // realize the rule (call a generator, or do type replacement, etc.)
+ RebindResult result = rule.realize(logger, genCtx, typeName);
+
+ // handle rebind result caching (if enabled)
+ String resultTypeName = processCacheableResult(logger, rule, typeName,
+ cachedResult, result);
+
+ /*
+ * Finalize new artifacts from the generator context
+ */
if (artifactAcceptor != null) {
// Go ahead and call finish() to accept new artifacts.
ArtifactSet newlyGeneratedArtifacts = genCtx.finish(logger);
@@ -63,16 +87,15 @@
artifactAcceptor.accept(logger, newlyGeneratedArtifacts);
}
}
- if (result == null) {
- result = typeName;
- }
- return result;
+
+ assert (resultTypeName != null);
+ return resultTypeName;
} finally {
rebindEvent.end();
}
}
- private String tryRebind(TreeLogger logger, String typeName)
+ private Rule getRebindRule(TreeLogger logger, String typeName)
throws UnableToCompleteException {
if (usedTypeNames.contains(typeName)) {
// Found a cycle.
@@ -110,10 +133,7 @@
usedRules.add(rule);
Messages.TRACE_RULE_MATCHED.log(logger, null);
- // Invoke the rule.
- //
- return rule.realize(logger, genCtx, typeName);
-
+ return rule;
} else {
// We are skipping this rule because it has already been used
// in a previous iteration.
@@ -128,14 +148,84 @@
//
return null;
}
+
+ /*
+ * Decide how to handle integrating a previously cached result, and whether
+ * to cache the new result for the future.
+ */
+ private String processCacheableResult(TreeLogger logger, Rule rule,
+ String typeName, CachedRebindResult cachedResult, RebindResult newResult) {
+
+ String resultTypeName = newResult.getReturnedTypeName();
+
+ if (!genCtx.isGeneratorResultCachingEnabled()) {
+ return resultTypeName;
+ }
+
+ RebindStatus status = newResult.getResultStatus();
+ switch (status) {
+
+ case USE_EXISTING:
+ // in this case, no newly generated or cached types are needed
+ break;
+
+ case USE_ALL_NEW_WITH_NO_CACHING:
+ /*
+ * in this case, new artifacts have been generated, but no need to
+ * cache results (as the generator is probably not able to take
+ * advantage of caching).
+ */
+ break;
+
+ case USE_ALL_NEW:
+ // use all new results, add a new cache entry
+ cachedResult = new CachedRebindResult(newResult.getReturnedTypeName(),
+ genCtx.getArtifacts(), genCtx.getGeneratedUnitMap(),
+ System.currentTimeMillis(), newResult.getClientData());
+ rebindCachePut(rule, typeName, cachedResult);
+ break;
+
+ case USE_ALL_CACHED:
+ // use all cached results
+ assert (cachedResult != null);
+
+ genCtx.commitArtifactsFromCachedRebindResult(logger);
+ genCtx.addGeneratedUnitsFromCachedRebindResult();
+
+ // use cached type name
+ resultTypeName = cachedResult.getReturnedTypeName();
+ break;
+
+ case USE_PARTIAL_CACHED:
+ /*
+ * Add cached generated units marked for reuse to the context.
+ * TODO(jbrosenberg): add support for reusing artifacts as well
+ * as GeneratedUnits.
+ */
+ genCtx.addGeneratedUnitsMarkedForReuseFromCache();
+
+ /*
+ * Create a new cache entry using the composite set of new and
+ * reused cached results currently in genCtx.
+ */
+ cachedResult = new CachedRebindResult(newResult.getReturnedTypeName(),
+ genCtx.getArtifacts(), genCtx.getGeneratedUnitMap(),
+ System.currentTimeMillis(), newResult.getClientData());
+ rebindCachePut(rule, typeName, cachedResult);
+ break;
+ }
+ return resultTypeName;
+ }
}
- private final Map<String, String> cache = new HashMap<String, String>();
+ private final Map<String, String> typeNameBindingMap = new HashMap<String, String>();
private final StandardGeneratorContext genCtx;
private final PropertyOracle propOracle;
+ private RebindCache rebindCache = null;
+
private final Rules rules;
public StandardRebindOracle(PropertyOracle propOracle, Rules rules,
@@ -153,16 +243,33 @@
public String rebind(TreeLogger logger, String typeName,
ArtifactAcceptor artifactAcceptor) throws UnableToCompleteException {
- String result = cache.get(typeName);
- if (result == null) {
+ String resultTypeName = typeNameBindingMap.get(typeName);
+ if (resultTypeName == null) {
logger = Messages.TRACE_TOPLEVEL_REBIND.branch(logger, typeName, null);
Rebinder rebinder = new Rebinder();
- result = rebinder.rebind(logger, typeName, artifactAcceptor);
- cache.put(typeName, result);
+ resultTypeName = rebinder.rebind(logger, typeName, artifactAcceptor);
+ typeNameBindingMap.put(typeName, resultTypeName);
- Messages.TRACE_TOPLEVEL_REBIND_RESULT.log(logger, result, null);
+ Messages.TRACE_TOPLEVEL_REBIND_RESULT.log(logger, resultTypeName, null);
}
- return result;
+ return resultTypeName;
+ }
+
+ public void setRebindCache(RebindCache cache) {
+ this.rebindCache = cache;
+ }
+
+ private CachedRebindResult rebindCacheGet(Rule rule, String typeName) {
+ if (rebindCache != null) {
+ return rebindCache.get(rule, typeName);
+ }
+ return null;
+ }
+
+ private void rebindCachePut(Rule rule, String typeName, CachedRebindResult result) {
+ if (rebindCache != null) {
+ rebindCache.put(rule, typeName, result);
+ }
}
}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerEnableGeneratorResultCaching.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerEnableGeneratorResultCaching.java
new file mode 100644
index 0000000..b9b8f29
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerEnableGeneratorResultCaching.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util.arg;
+
+import com.google.gwt.util.tools.ArgHandlerFlag;
+
+/**
+ * An ArgHandler to provide the -XenableGeneratorResultCaching flag.
+ */
+public class ArgHandlerEnableGeneratorResultCaching extends ArgHandlerFlag {
+
+ private final OptionEnableGeneratorResultCaching option;
+
+ public ArgHandlerEnableGeneratorResultCaching(OptionEnableGeneratorResultCaching option) {
+ this.option = option;
+ }
+
+ @Override
+ public String getPurpose() {
+ return "EXPERIMENTAL: enables generator result caching, for those generators that implement it";
+ }
+
+ @Override
+ public String getTag() {
+ return "-XenableGeneratorResultCaching";
+ }
+
+ @Override
+ public boolean isUndocumented() {
+ return true;
+ }
+
+ @Override
+ public boolean setFlag() {
+ option.setGeneratorResultCachingEnabled(true);
+ return true;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionEnableGeneratorResultCaching.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionEnableGeneratorResultCaching.java
new file mode 100644
index 0000000..ef06ee4
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionEnableGeneratorResultCaching.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util.arg;
+
+/**
+ * Encapsulates a compiler option to enable generator result caching.
+ */
+public interface OptionEnableGeneratorResultCaching {
+ boolean isGeneratorResultCachingEnabled();
+ void setGeneratorResultCachingEnabled(boolean enabled);
+}
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java
index cc8f9b4..e692f0b 100644
--- a/user/src/com/google/gwt/junit/JUnitShell.java
+++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -40,6 +40,7 @@
import com.google.gwt.dev.util.arg.ArgHandlerDisableRunAsync;
import com.google.gwt.dev.util.arg.ArgHandlerDraftCompile;
import com.google.gwt.dev.util.arg.ArgHandlerEnableAssertions;
+import com.google.gwt.dev.util.arg.ArgHandlerEnableGeneratorResultCaching;
import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerLocalWorkers;
@@ -186,6 +187,7 @@
registerHandler(new ArgHandlerDisableCastChecking(options));
registerHandler(new ArgHandlerDisableRunAsync(options));
registerHandler(new ArgHandlerDraftCompile(options));
+ registerHandler(new ArgHandlerEnableGeneratorResultCaching(options));
registerHandler(new ArgHandlerMaxPermsPerPrecompile(options));
registerHandler(new ArgHandlerLocalWorkers(options));