Fixes issue #2379; unstable generators should not crash the compiler or hosted mode.
- StandardRebindOracle is retrofitted to always cache rebind results, so that the same input type is never run twice in the same compilation.
- Doc on PropertyOracle updated to insist on stable results (StaticPropertyOracle does this now, but used to violate this; it would not have been possible to make StandardRebindOracle cache before).
- CompilationRebindOracle is removed since the caching behavior is now handled by StandardRebindOracle
- Unit test added to make sure multiple requests of the same type return a stable result.
This may also speed up hosted mode startup slightly in cases where the same type is requested many times through GWT.create().
Review by: spoon
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3560 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/PropertyOracle.java b/dev/core/src/com/google/gwt/core/ext/PropertyOracle.java
index 8f07ad4..188cc5a 100644
--- a/dev/core/src/com/google/gwt/core/ext/PropertyOracle.java
+++ b/dev/core/src/com/google/gwt/core/ext/PropertyOracle.java
@@ -23,7 +23,8 @@
/**
* Attempts to get a named deferred binding property. Throws
* <code>BadPropertyValueException</code> if the property is either
- * undefined or has a value that is unsupported.
+ * undefined or has a value that is unsupported. The result of invoking this
+ * method with the same <code>propertyName</code> must be stable.
*
* @param logger the current logger
* @param propertyName the name of the property
@@ -35,7 +36,8 @@
/**
* Attempts to get a named deferred binding property and returns the list of
* possible values. Throws <code>BadPropertyValueException</code> if the
- * property is undefined.
+ * property is undefined. The result of invoking this method with the same
+ * <code>propertyName</code> must be stable.
*
* @param logger the current logger
* @param propertyName the name of the property
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index 0a38598..beb2f7f 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -28,6 +28,7 @@
import com.google.gwt.dev.cfg.StaticPropertyOracle;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.CompilationUnit;
+import com.google.gwt.dev.jdt.RebindOracle;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
import com.google.gwt.dev.jjs.JJSOptions;
@@ -51,9 +52,7 @@
import com.google.gwt.util.tools.ToolBase;
import java.io.File;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Map;
import java.util.Set;
/**
@@ -104,64 +103,24 @@
}
}
- /**
- * Used to smartly deal with rebind across the production of an entire
- * permutation, including cache checking and recording the inputs and outputs
- * into a {@link Compilation}.
- */
- private class CompilationRebindOracle extends StandardRebindOracle {
-
- private final Map<String, String> cache = new HashMap<String, String>();
- private final StaticPropertyOracle propOracle;
-
- 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 cache answers.
- */
- @Override
- public String rebind(TreeLogger logger, String in)
- throws UnableToCompleteException {
- String out = cache.get(in);
- if (out == null) {
- // Actually do the work, then cache it.
- //
- out = super.rebind(logger, in);
- cache.put(in, out);
- } else {
- // Was cached.
- //
- String msg = "Rebind answer for '" + in + "' found in cache " + out;
- logger.log(TreeLogger.DEBUG, msg, null);
- }
- return out;
- }
- }
-
private class DistillerRebindPermutationOracle implements
RebindPermutationOracle {
- private CompilationRebindOracle[] rebindOracles;
+ private StaticPropertyOracle[] propertyOracles;
+ private RebindOracle[] rebindOracles;
public DistillerRebindPermutationOracle(ArtifactSet generatorArtifacts,
PropertyPermutations perms) {
- rebindOracles = new CompilationRebindOracle[perms.size()];
+ propertyOracles = new StaticPropertyOracle[perms.size()];
+ rebindOracles = new RebindOracle[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);
+ propertyOracles[i] = new StaticPropertyOracle(orderedProps,
+ orderedPropValues);
+ rebindOracles[i] = new StandardRebindOracle(compilationState,
+ propertyOracles[i], module, rules, genDir, generatorResourcesDir,
+ generatorArtifacts);
}
}
@@ -174,7 +133,7 @@
Set<String> answers = new HashSet<String>();
- for (CompilationRebindOracle rebindOracle : rebindOracles) {
+ for (RebindOracle rebindOracle : rebindOracles) {
String resultTypeName = rebindOracle.rebind(logger, requestTypeName);
answers.add(resultTypeName);
}
@@ -185,7 +144,11 @@
return rebindOracles.length;
}
- public CompilationRebindOracle getRebindOracle(int permNumber) {
+ public StaticPropertyOracle getPropertyOracle(int permNumber) {
+ return propertyOracles[permNumber];
+ }
+
+ public RebindOracle getRebindOracle(int permNumber) {
return rebindOracles[permNumber];
}
}
@@ -416,9 +379,8 @@
PerfLogger.start("Compiling " + permCount + " permutations");
Permutation[] perms = new Permutation[permCount];
for (int i = 0; i < permCount; ++i) {
- CompilationRebindOracle rebindOracle = rpo.getRebindOracle(i);
- perms[i] = new Permutation(i, rebindOracle,
- rebindOracle.getPropertyOracle());
+ perms[i] = new Permutation(i, rpo.getRebindOracle(i),
+ rpo.getPropertyOracle(i));
}
PermutationCompiler permCompiler = new PermutationCompiler(logger, jjs,
perms);
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 0ee638c..3cf0c7f 100644
--- a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
@@ -28,9 +28,11 @@
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;
/**
@@ -125,6 +127,8 @@
private final ArtifactSet artifactSet;
+ private final Map<String, String> cache = new HashMap<String, String>();
+
private final CompilationState compilationState;
private final File genDir;
@@ -152,13 +156,16 @@
public String rebind(TreeLogger logger, String typeName)
throws UnableToCompleteException {
- logger = Messages.TRACE_TOPLEVEL_REBIND.branch(logger, typeName, null);
+ String result = cache.get(typeName);
+ if (result == null) {
+ logger = Messages.TRACE_TOPLEVEL_REBIND.branch(logger, typeName, null);
- Rebinder rebinder = new Rebinder();
- String result = rebinder.rebind(logger, typeName);
+ Rebinder rebinder = new Rebinder();
+ result = rebinder.rebind(logger, typeName);
+ cache.put(typeName, result);
- Messages.TRACE_TOPLEVEL_REBIND_RESULT.log(logger, result, null);
-
+ Messages.TRACE_TOPLEVEL_REBIND_RESULT.log(logger, result, null);
+ }
return result;
}
}
diff --git a/user/test/com/google/gwt/dev/jjs/CompilerSuite.gwt.xml b/user/test/com/google/gwt/dev/jjs/CompilerSuite.gwt.xml
index c78dea1..da8fd47 100644
--- a/user/test/com/google/gwt/dev/jjs/CompilerSuite.gwt.xml
+++ b/user/test/com/google/gwt/dev/jjs/CompilerSuite.gwt.xml
@@ -13,5 +13,9 @@
<!-- limitations under the License. -->
<module>
- <source path='test'/>
+ <source path='test' />
+ <generate-with class="com.google.gwt.dev.jjs.UnstableGenerator">
+ <when-type-assignable
+ class="com.google.gwt.dev.jjs.test.UnstableGeneratorTest.UnstableResult" />
+ </generate-with>
</module>
diff --git a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
index 9cec9d4..79e0b26 100644
--- a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
+++ b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
@@ -38,6 +38,7 @@
import com.google.gwt.dev.jjs.test.MiscellaneousTest;
import com.google.gwt.dev.jjs.test.NativeLongTest;
import com.google.gwt.dev.jjs.test.ObjectIdentityTest;
+import com.google.gwt.dev.jjs.test.UnstableGeneratorTest;
import com.google.gwt.dev.jjs.test.VarargsTest;
import com.google.gwt.junit.tools.GWTTestSuite;
@@ -75,6 +76,7 @@
suite.addTestSuite(MiscellaneousTest.class);
suite.addTestSuite(NativeLongTest.class);
suite.addTestSuite(ObjectIdentityTest.class);
+ suite.addTestSuite(UnstableGeneratorTest.class);
suite.addTestSuite(VarargsTest.class);
// $JUnit-END$
diff --git a/user/test/com/google/gwt/dev/jjs/UnstableGenerator.java b/user/test/com/google/gwt/dev/jjs/UnstableGenerator.java
new file mode 100644
index 0000000..4ab21dd
--- /dev/null
+++ b/user/test/com/google/gwt/dev/jjs/UnstableGenerator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs;
+
+import com.google.gwt.core.ext.Generator;
+import com.google.gwt.core.ext.GeneratorContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.NotFoundException;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+import com.google.gwt.dev.jjs.test.UnstableGeneratorTest;
+
+import java.io.PrintWriter;
+
+/**
+ * Generates unstable results than change on each invocation.
+ */
+public class UnstableGenerator extends Generator {
+
+ private static int counter = 0;
+
+ @Override
+ public String generate(TreeLogger logger, GeneratorContext context,
+ String typeName) throws UnableToCompleteException {
+ try {
+ ++counter;
+ TypeOracle typeOracle = context.getTypeOracle();
+ JClassType inputType = typeOracle.getType(typeName);
+ JClassType intfType = typeOracle.getType(UnstableGeneratorTest.UnstableResult.class.getName().replace(
+ '$', '.'));
+ String packageName = inputType.getPackage().getName();
+ String className = inputType.getName().replace('.', '_') + "Impl"
+ + counter;
+ PrintWriter writer = context.tryCreate(logger, packageName, className);
+ if (writer == null) {
+ return null;
+ }
+ writer.println("package " + packageName + ";");
+ writer.println("public class " + className + " implements "
+ + intfType.getQualifiedSourceName() + " {");
+ writer.println(" public String get() { return \"foo" + counter + "\"; }");
+ writer.println("}");
+ context.commit(logger, writer);
+ return (packageName.length() == 0) ? className
+ : (packageName + "." + className);
+ } catch (NotFoundException e) {
+ logger.log(TreeLogger.ERROR, "Unable to find required client type", e);
+ throw new UnableToCompleteException();
+ }
+ }
+}
diff --git a/user/test/com/google/gwt/dev/jjs/test/UnstableGeneratorTest.java b/user/test/com/google/gwt/dev/jjs/test/UnstableGeneratorTest.java
new file mode 100644
index 0000000..b3b7e54
--- /dev/null
+++ b/user/test/com/google/gwt/dev/jjs/test/UnstableGeneratorTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.test;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests that we get a consistent result for a single compilation even in the
+ * face of an unstable generator.
+ */
+public class UnstableGeneratorTest extends GWTTestCase {
+
+ /**
+ * Marker interface for generating an unstable class.
+ */
+ public interface UnstableResult {
+ String get();
+ }
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.dev.jjs.CompilerSuite";
+ }
+
+ public void testMultipleRebindsWithUnstableGenerator() {
+ String firstResult = GWT.<UnstableResult> create(UnstableResult.class).get();
+ assertEquals(firstResult, GWT.<UnstableResult> create(UnstableResult.class).get());
+ assertEquals(firstResult, GWT.<UnstableResult> create(UnstableResult.class).get());
+ assertEquals(firstResult, GWT.<UnstableResult> create(UnstableResult.class).get());
+ assertEquals(firstResult, GWT.<UnstableResult> create(UnstableResult.class).get());
+ }
+}