Fix UiBinder non determinism.
- Fixes one source of nondeterminism in UiBinder.
- Adds error reporting for compile failures in CompilerTests.
Change-Id: I44d4caa1aeaa279aa248f60e0deac731bdd1d364
diff --git a/dev/core/test/com/google/gwt/dev/CompilerTest.java b/dev/core/test/com/google/gwt/dev/CompilerTest.java
index c62e7a9..1efda99 100644
--- a/dev/core/test/com/google/gwt/dev/CompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/CompilerTest.java
@@ -27,6 +27,7 @@
import com.google.gwt.dev.jjs.JsOutputOption;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.SourceLevel;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
import com.google.gwt.thirdparty.guava.common.base.Charsets;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
@@ -437,6 +438,65 @@
"<entry-point class='com.foo.TestEntryPoint'/>",
"</module>");
+ private MockResource myWidgetUiXml =
+ JavaResourceBase.createMockResource("com/foo/MyWidget.ui.xml",
+ "<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'",
+ " xmlns:g='urn:import:com.google.gwt.user.client.ui'>",
+ "<g:HTMLPanel>",
+ " Hello, <g:ListBox ui:field='myListBox' visibleItemCount='1'/>.",
+ "</g:HTMLPanel>",
+ "</ui:UiBinder>");
+
+ private MockJavaResource myWidget =
+ JavaResourceBase.createMockJavaResource("com.foo.MyWidget",
+ "package com.foo;",
+ "import com.google.gwt.core.client.GWT;",
+ "import com.google.gwt.core.client.JavaScriptObject;",
+ "import com.google.gwt.uibinder.client.UiBinder;",
+ "import com.google.gwt.uibinder.client.UiField;",
+ "import com.google.gwt.user.client.ui.Composite;",
+ "import com.google.gwt.user.client.ui.ListBox;",
+ "import com.google.gwt.user.client.ui.Widget;",
+ "public class MyWidget extends Composite {",
+ " interface Binder extends UiBinder<Widget, MyWidget> {",
+ " }",
+ " private static final Binder binder = GWT.create(Binder.class);",
+ " @UiField ListBox myListBox;",
+ " public MyWidget() {",
+ " init();",
+ " }",
+ " protected void init() {",
+ " initWidget(binder.createAndBindUi(this));",
+ " myListBox.addItem(\"One\");",
+ " }",
+ "}");
+
+ private MockJavaResource uiBinderTestEntryPointResource =
+ JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint",
+ "package com.foo;",
+ "import com.google.gwt.core.client.EntryPoint;",
+ "import com.google.gwt.dom.client.Node;",
+ "import com.google.gwt.user.client.ui.RootPanel;",
+ "public class TestEntryPoint implements EntryPoint {",
+ " private Node node;",
+ " private MyWidget widget;",
+ " @Override",
+ " public void onModuleLoad() {",
+ " node = null; widget = new MyWidget();",
+ " RootPanel.get().add(widget);",
+ " }",
+ "}");
+
+ private MockResource uiBinderTestModuleResource =
+ JavaResourceBase.createMockResource("com/foo/UiBinderTestModule.gwt.xml",
+ "<module>",
+ " <inherits name='com.google.gwt.core.Core'/>",
+ " <inherits name='com.google.gwt.user.User' />",
+ " <source path=''/>",
+ " <set-property name='user.agent' value='safari'/>",
+ " <entry-point class='com.foo.TestEntryPoint'/>",
+ "</module>");
+
private Set<String> emptySet = stringSet();
public CompilerTest() {
@@ -578,6 +638,12 @@
checkPerFileRecompile_dateStampChange(JsOutputOption.DETAILED);
}
+ public void testPerFileRecompile_deterministicUiBinder() throws UnableToCompleteException, IOException,
+ InterruptedException {
+ checkPerFileRecompile_deterministicUiBinder(JsOutputOption.PRETTY);
+ checkPerFileRecompile_deterministicUiBinder(JsOutputOption.DETAILED);
+ }
+
public void testPerFileRecompile_unstableGeneratorReferencesModifiedType()
throws UnableToCompleteException, IOException, InterruptedException {
checkPerFileRecompile_unstableGeneratorReferencesModifiedType(JsOutputOption.PRETTY);
@@ -682,7 +748,7 @@
// Recompile with no changes, which should not trigger any Generator runs.
compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule",
- Lists.<MockResource> newArrayList(), relinkMinimalRebuildCache, emptySet, output);
+ Lists.<MockResource>newArrayList(), relinkMinimalRebuildCache, emptySet, output);
// Since there were no changes BarReferencesFoo Generator was not run again.
assertEquals(1, BarReferencesFooGenerator.runCount);
@@ -690,7 +756,7 @@
// Recompile with a modified Foo class, which should invalidate Bar which was generated by a
// GWT.create() call in the entry point.
compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule",
- Lists.<MockResource> newArrayList(fooResource), relinkMinimalRebuildCache,
+ Lists.<MockResource>newArrayList(fooResource), relinkMinimalRebuildCache,
stringSet("com.foo.TestEntryPoint", "com.foo.Foo", "com.foo.Bar"), output);
// BarReferencesFoo Generator was run again.
@@ -820,6 +886,17 @@
assertTrue(originalJs.equals(relinkedJs));
}
+ private void checkPerFileRecompile_deterministicUiBinder(JsOutputOption output)
+ throws UnableToCompleteException, IOException, InterruptedException {
+ CompilerOptions compilerOptions = new CompilerOptionsImpl();
+
+ checkRecompiledModifiedApp(compilerOptions, "com.foo.UiBinderTestModule", Lists.newArrayList(
+ uiBinderTestModuleResource, uiBinderTestEntryPointResource, myWidgetUiXml), myWidget,
+ myWidget, stringSet("com.foo.MyWidget", "com.foo.MyWidget_BinderImpl_GenBundle",
+ "com.foo.MyWidget$Binder", "com.foo.MyWidget_BinderImpl$Template", "com.foo.TestEntryPoint",
+ "com.foo.MyWidget_BinderImpl", "com.foo.MyWidget_BinderImpl$Widgets"), output);
+ }
+
private void checkPerFileRecompile_packagePrivateOverride(JsOutputOption output)
throws UnableToCompleteException, IOException, InterruptedException {
CompilerOptions compilerOptions = new CompilerOptionsImpl();
@@ -1047,8 +1124,8 @@
System.setProperty(GWT_PERSISTENTUNITCACHE, "false");
// Wait 1 second so that any new file modification times are actually different.
Thread.sleep(1001);
- TreeLogger logger = TreeLogger.NULL;
-
+ PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
+ logger.setMaxDetail(TreeLogger.ERROR);
// We might be reusing the same application dir but we want to make sure that the output dir is
// clean to avoid confusion when returning the output JS.
File outputDir = new File(applicationDir.getPath() + File.separator + moduleName);
@@ -1097,6 +1174,7 @@
}
}
}
+
assertNotNull(outputJsFile);
assertEquals(expectedStaleTypeNames, minimalRebuildCache.getStaleTypeNames());
return Files.toString(outputJsFile, Charsets.UTF_8);
diff --git a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
index f6f08ae..37f8fdc 100644
--- a/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
+++ b/user/src/com/google/gwt/uibinder/rebind/AbstractFieldWriter.java
@@ -45,12 +45,6 @@
+ " a @UiFactory method on the UiBinder's owner, or annotate a constructor of %2$s with"
+ " @UiConstructor.";
- private static int nextAttachVar;
-
- public static String getNextAttachVar() {
- return "attachRecord" + nextAttachVar++;
- }
-
private final FieldManager manager;
private final Set<FieldWriter> needs = new LinkedHashSet<FieldWriter>();
private final List<String> statements = new ArrayList<String>();
@@ -260,16 +254,12 @@
}
w.write("// Setup section.");
- for (String s : statements) {
- w.write(s);
- }
-
- String attachedVar = null;
+ writeStatements(w, statements);
if (attachStatements.size() > 0) {
w.newline();
- w.write("// Attach section.");
if (outputAttachDetachCallbacks) {
+ w.write("// Attach section.");
// TODO(rdcastro): This is too coupled with RenderablePanel.
// Make this nicer.
w.write("%s.wrapInitializationCallback = ", getName());
@@ -280,36 +270,39 @@
w.outdent();
w.write("@Override public void execute() {");
w.indent();
- } else {
- attachedVar = getNextAttachVar();
+ writeStatements(w, attachStatements);
+
+ w.outdent();
+ w.write("}");
+ w.outdent();
+ w.write("};");
+ } else {
+ String attachedVar = "__attachRecord__";
+
+ w.write("{");
+ w.indent();
+ w.write("// Attach section.");
String elementToAttach = getInstantiableType().isAssignableTo(getDomElement(typeOracle))
? name : name + ".getElement()";
w.write("UiBinderUtil.TempAttachment %s = UiBinderUtil.attachToDom(%s);",
attachedVar, elementToAttach);
- }
- for (String s : attachStatements) {
- w.write(s);
- }
+ w.newline();
- if (outputAttachDetachCallbacks) {
+ writeStatements(w, attachStatements);
+
+ w.newline();
+ // If we forced an attach, we should always detach, regardless of whether
+ // there are any detach statements.
+ w.write("// Detach section.");
+ w.write("%s.detach();", attachedVar);
w.outdent();
w.write("}");
- w.outdent();
- w.write("};");
}
}
- w.newline();
- // If we forced an attach, we should always detach, regardless of whether
- // there are any detach statements.
- if (attachedVar != null) {
- w.write("// Detach section.");
- w.write("%s.detach();", attachedVar);
- }
-
if (detachStatements.size() > 0) {
if (outputAttachDetachCallbacks) {
w.write("%s.detachedInitializationCallback = ", getName());
@@ -321,9 +314,7 @@
w.indent();
}
- for (String s : detachStatements) {
- w.write(s);
- }
+ writeStatements(w, detachStatements);
if (outputAttachDetachCallbacks) {
w.outdent();
@@ -406,4 +397,10 @@
}
return type;
}
+
+ private static void writeStatements(IndentedWriter w, Iterable<String> statements) {
+ for (String s : statements) {
+ w.write(s);
+ }
+ }
}