/* * Copyright 2009 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; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.dev.UnstableNestedAnonymousGenerator.OutputVersion; import com.google.gwt.dev.cfg.EntryMethodHolderGenerator; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.ModuleDefLoader; import com.google.gwt.dev.cfg.ResourceLoader; import com.google.gwt.dev.cfg.ResourceLoaders; import com.google.gwt.dev.javac.UnitCacheSingleton; import com.google.gwt.dev.javac.testing.impl.JavaResourceBase; import com.google.gwt.dev.javac.testing.impl.MockJavaResource; import com.google.gwt.dev.javac.testing.impl.MockResource; import com.google.gwt.dev.jjs.JsOutputOption; import com.google.gwt.dev.jjs.impl.JjsUtils; import com.google.gwt.dev.util.UnitTestTreeLogger; 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; import com.google.gwt.thirdparty.guava.common.collect.Sets; import com.google.gwt.thirdparty.guava.common.io.Files; import com.google.gwt.util.tools.Utility; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; /** * Test for {@link Compiler}. */ public class CompilerTest extends ArgProcessorTestBase { public static final String HELLO_MODULE = "com.google.gwt.sample.hello.Hello"; public static final String HELLO_MODULE_STACKMODE_STRIP = "com.google.gwt.sample.hello.Hello_stackMode_strip"; private MockJavaResource packagePrivateParentResource = JavaResourceBase.createMockJavaResource("com.foo.PackagePrivateParent", "package com.foo;", "public abstract class PackagePrivateParent {", " abstract void someFunction();", "}"); private MockJavaResource packagePrivateChildResource = JavaResourceBase.createMockJavaResource("com.foo.PackagePrivateChild", "package com.foo;", "public class PackagePrivateChild extends PackagePrivateParent {", " @Override", " void someFunction() {}", "}"); private MockJavaResource referencesParentResource = JavaResourceBase.createMockJavaResource("com.foo.ReferencesParent", "package com.foo;", "public class ReferencesParent {", " void run() {", " PackagePrivateParent packagePrivateParent = null;", " packagePrivateParent.someFunction();", " }", "}"); private MockJavaResource packagePrivateDispatchEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " ReferencesParent referencesParent = new ReferencesParent();", " PackagePrivateChild packagePrivateChild = new PackagePrivateChild();", " }", "}"); private MockJavaResource jsoOne = JavaResourceBase.createMockJavaResource("com.foo.JsoOne", "package com.foo;", "import com.google.gwt.core.client.JavaScriptObject;", "public class JsoOne extends JavaScriptObject {", " protected JsoOne() {", " }", "}"); private MockJavaResource jsoTwo_before = JavaResourceBase.createMockJavaResource("com.foo.JsoTwo", "package com.foo;", "public class JsoTwo {", " protected JsoTwo() {", " }", "}"); private MockJavaResource jsoTwo_after = JavaResourceBase.createMockJavaResource("com.foo.JsoTwo", "package com.foo;", "import com.google.gwt.core.client.JavaScriptObject;", "public class JsoTwo extends JavaScriptObject {", " protected JsoTwo() {", " }", "}"); private MockJavaResource someClassReferringToJsoOneArrays = JavaResourceBase.createMockJavaResource("com.foo.SomeClassReferringToJsoOneArrays", "package com.foo;", "public class SomeClassReferringToJsoOneArrays {", " public static Object createJsoOneArray() { return new JsoOne[30]; }", "}"); private MockJavaResource someClassReferringToJsoTwoArrays = JavaResourceBase.createMockJavaResource("com.foo.SomeClassReferringToJsoTwoArrays", "package com.foo;", "public class SomeClassReferringToJsoTwoArrays {", " public static Object createJsoTwoArray() { return new JsoTwo[30]; }", "}"); private MockJavaResource jsoArrayTestEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " Object o1 = SomeClassReferringToJsoOneArrays.createJsoOneArray();", " Object o2 = SomeClassReferringToJsoTwoArrays.createJsoTwoArray();", " }", "}"); private MockJavaResource simpleDialogResourceWithExport = JavaResourceBase.createMockJavaResource("com.foo.SimpleDialog", "package com.foo;", "import jsinterop.annotations.JsProperty;", "public class SimpleDialog {", " @JsProperty(namespace = \"ns\")", " public static int show;", "}"); private MockJavaResource complexDialogResourceWithExport = JavaResourceBase.createMockJavaResource("com.foo.ComplexDialog", "package com.foo;", "import jsinterop.annotations.JsProperty;", "public class ComplexDialog {", " @JsProperty(namespace = \"ns\")", " public static int show;", "}"); private MockJavaResource complexDialogResourceSansExport = JavaResourceBase.createMockJavaResource("com.foo.ComplexDialog", "package com.foo;", "public class ComplexDialog {", " public static void show() {}", "}"); private MockJavaResource simpleModelEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " SimpleModel simpleModel1 = new SimpleModel();", " SimpleModel simpleModel2 = new SimpleModel();", " simpleModel2.copyFrom(simpleModel1);", " }", "}"); private MockJavaResource simpleModelResource = JavaResourceBase.createMockJavaResource("com.foo.SimpleModel", "package com.foo;", "public class SimpleModel {", " private int value = Constants.CONSTANT;", " public void copyFrom(Object object) {}", "}"); private MockJavaResource modifiedFunctionSignatureSimpleModelResource = JavaResourceBase.createMockJavaResource("com.foo.SimpleModel", "package com.foo;", "public class SimpleModel {", " private int value = Constants.CONSTANT;", " public void copyFrom(SimpleModel that) {}", "}"); private MockResource simpleModuleResource = JavaResourceBase.createMockResource("com/foo/SimpleModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.TestEntryPoint'/>", "</module>"); private MockJavaResource constantsModelResource = JavaResourceBase.createMockJavaResource("com.foo.Constants", "package com.foo;", "public class Constants {", " public static final int CONSTANT = 0;", "}"); private MockJavaResource modifiedConstantsModelResource = JavaResourceBase.createMockJavaResource("com.foo.Constants", "package com.foo;", "public class Constants {", " public static final int CONSTANT = 2;", "}"); private MockResource unstableGeneratorModuleResource = JavaResourceBase.createMockResource( "com/foo/SimpleModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.TestEntryPoint'/>", "<generate-with class='com.google.gwt.dev.UnstableNestedAnonymousGenerator'>", " <when-type-is class='java.lang.Object' />", "</generate-with>", "</module>"); private MockResource resourceReadingGeneratorModuleResource = JavaResourceBase.createMockResource("com/foo/SimpleModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.TestEntryPoint'/>", "<generate-with class='com.google.gwt.dev.FooResourceGenerator'>", " <when-type-is class='java.lang.Object' />", "</generate-with>", "</module>"); private MockResource barReferencesFooGeneratorModuleResource = JavaResourceBase.createMockResource("com/foo/SimpleModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.TestEntryPoint'/>", "<generate-with class='com.google.gwt.dev.BarReferencesFooGenerator'>", " <when-type-is class='java.lang.Object' />", "</generate-with>", "</module>"); private MockResource cascadingGeneratorModuleResource = JavaResourceBase.createMockResource("com/foo/SimpleModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.TestEntryPoint'/>", "<generate-with class='com.google.gwt.dev.CauseStringRebindGenerator'>", " <when-type-is class='java.lang.Object' />", "</generate-with>", "<generate-with class='com.google.gwt.dev.CauseShortRebindGenerator'>", " <when-type-is class='java.lang.String' />", "</generate-with>", "<generate-with class='com.google.gwt.dev.FooResourceGenerator'>", " <when-type-is class='java.lang.Short' />", "</generate-with>", "</module>"); private MockResource multipleClassGeneratorModuleResource = JavaResourceBase.createMockResource("com/foo/SimpleModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.TestEntryPoint'/>", "<generate-with class='com.google.gwt.dev.MultipleClassGenerator'>", " <when-type-is class='java.lang.Object' />", "</generate-with>", "</module>"); private MockResource classNameToGenerateResource = JavaResourceBase.createMockResource("com/foo/generatedClassName.txt", "FooReplacementOne"); private MockResource modifiedClassNameToGenerateResource = JavaResourceBase.createMockResource("com/foo/generatedClassName.txt", "FooReplacementTwo"); private MockJavaResource generatorEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "import com.google.gwt.core.client.GWT;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " GWT.create(Object.class);", " }", "}"); private MockJavaResource referencesBarAndGeneratorEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "import com.google.gwt.core.client.GWT;", "public class TestEntryPoint implements EntryPoint {", " Bar bar = new Bar();", " @Override", " public void onModuleLoad() {", " GWT.create(Object.class);", " }", "}"); private MockJavaResource superClassOrderEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " SuperClass superClass = new SuperClass();", " ASubClass aSubClass = new ASubClass();", " }", "}"); private MockJavaResource referencedBySuperClassResource = JavaResourceBase.createMockJavaResource("com.foo.ReferencedBySuperClass", "package com.foo;", "public class ReferencedBySuperClass {}"); private MockJavaResource superClassResource = JavaResourceBase.createMockJavaResource("com.foo.SuperClass", "package com.foo;", "public class SuperClass {", " ReferencedBySuperClass referencedBySuperClass = new ReferencedBySuperClass();", "}"); private MockJavaResource aSubClassResource = JavaResourceBase.createMockJavaResource("com.foo.ASubClass", "package com.foo;", "public class ASubClass extends SuperClass {}"); private MockJavaResource modifiedSuperEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " Object d = new ModelD();", " if (d instanceof ModelA) {", " // ModelD extends ModelA;", " } else {", " // ModelD does not ModelA;", " }", " }", "}"); private MockJavaResource modelAResource = JavaResourceBase.createMockJavaResource("com.foo.ModelA", "package com.foo;", "public class ModelA {}"); private MockJavaResource modelBResource = JavaResourceBase.createMockJavaResource("com.foo.ModelB", "package com.foo;", "public class ModelB extends ModelA {}"); private MockJavaResource modelCResource = JavaResourceBase.createMockJavaResource("com.foo.ModelC", "package com.foo;", "public class ModelC {}"); private MockJavaResource modifiedSuperModelCResource = JavaResourceBase.createMockJavaResource("com.foo.ModelC", "package com.foo;", "public class ModelC extends ModelA {}"); private MockJavaResource modelDResource = JavaResourceBase.createMockJavaResource("com.foo.ModelD", "package com.foo;", "public class ModelD extends ModelC {}"); private MockJavaResource modifiedJsoIntfDispatchEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " Caller.call(this);", " }", "", " public void runTest(FooInterface fooInterface) {", " fooInterface.run();", " }", "}"); private MockJavaResource callerResource = JavaResourceBase.createMockJavaResource("com.foo.Caller", "package com.foo;", "public class Caller {", " public static void call(TestEntryPoint testEntryPoint) {", " testEntryPoint.runTest(Foo.createFoo());", " }", "}"); private MockJavaResource sameContentDifferentTimeFooResource = JavaResourceBase .createMockJavaResource("com.foo.Foo", "package com.foo;", "public class Foo {}"); private MockJavaResource fooInterfaceResource = JavaResourceBase.createMockJavaResource("com.foo.FooInterface", "package com.foo;", "public interface FooInterface {", " void run();", "}"); private MockJavaResource jsoFooResource = JavaResourceBase.createMockJavaResource("com.foo.Foo", "package com.foo;", "import com.google.gwt.core.client.JavaScriptObject;", "public final class Foo extends JavaScriptObject implements FooInterface {", " public static native Foo createFoo() /*-{", " return {};", " }-*/;", "", " protected Foo() {}", "", " @Override", " public void run() {}", "}"); private MockJavaResource nonJsoFooResource = JavaResourceBase.createMockJavaResource("com.foo.Foo", "package com.foo;", "public class Foo implements FooInterface {", " public static Foo createFoo() {", " return new Foo();", " }", "", " @Override", " public void run() {}", "}"); private MockJavaResource fooResource = JavaResourceBase.createMockJavaResource("com.foo.Foo", "package com.foo;", "public class Foo {}"); private MockJavaResource barReferencesFooResource = JavaResourceBase.createMockJavaResource("com.foo.Bar", "package com.foo;", "public class Bar {", " Foo foo = new Foo();", "}"); private MockJavaResource jsTypeBarResource = JavaResourceBase.createMockJavaResource( "com.foo.Bar", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType public class Bar {", " void doInstanceBar() {}", " public static void doStaticBaz() {}", "}"); private MockJavaResource nonCompilableFooResource = JavaResourceBase.createMockJavaResource("com.foo.Foo", "package com.foo;", "public class Foo {", " public void run() {", " // Not available in GWT.", " String.format(\"asdf\");", " }", "}"); private MockJavaResource emptyEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {}", "}"); MockJavaResource entryPointResourceForFoo = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "import com.foo.Foo;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() { new Foo(); }", "}"); private MockJavaResource gwtCreateEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "import com.google.gwt.core.client.GWT;", "import jsinterop.annotations.JsType;", "public class TestEntryPoint implements EntryPoint {", " @JsType public static class MyJsType {}", " @JsType public interface MyJsTypeInterface {}", " public static class MyTypeImplementsJsType implements MyJsTypeInterface {}", " @Override", " public void onModuleLoad() {", " GWT.create(MyJsType.class);", " GWT.create(MyTypeImplementsJsType.class);", " }", "}"); private MockJavaResource dialogEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " SimpleDialog simpleDialog = new SimpleDialog();", " ComplexDialog complexDialog = new ComplexDialog();", " }", "}"); private MockJavaResource brokenGwtCreateEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "import com.google.gwt.core.client.GWT;", "import com.google.gwt.core.client.JavaScriptObject;", "public class TestEntryPoint implements EntryPoint {", " public static class MyJso extends JavaScriptObject {", " protected MyJso() {}", " }", " @Override", " public void onModuleLoad() {", " GWT.create(MyJso.class);", " }", "}"); private MockJavaResource topResource = JavaResourceBase.createMockJavaResource("com.foo.top", "package com.foo;", "abstract class Top {", " Object run() {", " return \"\";", " }", "}"); private MockJavaResource middleResource = JavaResourceBase.createMockJavaResource("com.foo.Middle", "package com.foo;", "public abstract class Middle extends Top {", " abstract String run();", "}"); private MockJavaResource bottomResource = JavaResourceBase.createMockJavaResource("com.foo.Bottom", "package com.foo;", "public class Bottom extends Middle {", " // ", " private final class Value {}", " String run() {", " Value value = new Value();", " return \"\";", " }", "}"); private MockJavaResource overriddenMethodChainEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " Bottom bottom = new Bottom();", " ((Top)bottom).run();", " }", "}"); private MockJavaResource bazResource = JavaResourceBase.createMockJavaResource("com.foo.Baz", "package com.foo;", "public class Baz {}"); private MockJavaResource regularFooImplemetorResource = JavaResourceBase.createMockJavaResource("com.foo.FooImplementor", "package com.foo;", "public class FooImplementor implements FooInterface {", " @Override", " public void run() {}", "}"); private MockJavaResource simpleFactory = JavaResourceBase.createMockJavaResource("com.foo.SimpleFactory", "package com.foo;", "public class SimpleFactory {", " public static SimpleIntf getJso() {", " return getJsoImpl();", " };", " public static native SimpleJso getJsoImpl() /*-{", " return null;", " }-*/;", "}"); private MockJavaResource simpleJso = JavaResourceBase.createMockJavaResource("com.foo.SimpleJso", "package com.foo;", "import com.google.gwt.core.client.JavaScriptObject;", "public class SimpleJso extends JavaScriptObject implements SimpleIntf {", " protected SimpleJso() {", " }", " public final void method() {", " }", "}"); private MockJavaResource simpleIntf = JavaResourceBase.createMockJavaResource("com.foo.SimpleIntf", "package com.foo;", "import com.google.gwt.core.client.JavaScriptObject;", "public interface SimpleIntf {", " public void method();", "}"); private MockJavaResource jsoTestEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " SimpleFactory.getJso().method();", " }", "}"); private MockResource jsoTestModuleResource = JavaResourceBase.createMockResource("com/foo/SimpleModule.gwt.xml", "<module>", "<source path=''/>", "<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 MockResource myWidgetWithWhiteStyleUiXml = 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'>", " <ui:style>", " .title {", " background-color: white;", " }", " </ui:style>", " <g:HTMLPanel><g:ListBox ui:field='myListBox' visibleItemCount='1'/></g:HTMLPanel>", "</ui:UiBinder>"); private MockResource myWidgetWithGreyStyleUiXml = 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'>", " <ui:style>", " .title {", " background-color: grey;", " }", " </ui:style>", " <g:HTMLPanel><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 MockResource devirtualizeStringModuleResource = JavaResourceBase.createMockResource("com/foo/DevirtualizeStringModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.DevirtualizeStringEntryPoint'/>", "</module>"); private MockJavaResource devirtualizeStringEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.DevirtualizeStringEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class DevirtualizeStringEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " CharSequence seq = \"A\";", " seq.subSequence(0,1);", " }", "}"); private MockResource transitivelyFoldableConstantModuleResource = JavaResourceBase.createMockResource( "com/foo/TransitivelyFoldableConstantModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.TransitivelyFoldableConstantEntryPoint'/>", "</module>"); private MockJavaResource transitivelyFoldableConstantEntryPointResource = JavaResourceBase.createMockJavaResource( "com.foo.TransitivelyFoldableConstantEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TransitivelyFoldableConstantEntryPoint", " implements EntryPoint {", " static final int CONST = 1 + ClassOne.CONST;", " @Override", " public void onModuleLoad() {", " int c = CONST;", " }", "}"); private MockJavaResource classOneResource = JavaResourceBase.createMockJavaResource( "com.foo.ClassOne", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class ClassOne {", " static final int CONST = 1 + ClassTwo.CONST;", "}"); private MockJavaResource classTwoResource = JavaResourceBase.createMockJavaResource( "com.foo.ClassTwo", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class ClassTwo {", " static final int CONST = 1 + 3;", "}"); private MockResource superFromStaleInnerModuleResource = JavaResourceBase.createMockResource( "com/foo/SuperFromStaleInnerModule.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.SuperFromStaleInnerEntryPoint'/>", "</module>"); private MockJavaResource superFromStaleInnerEntryPointResource = JavaResourceBase.createMockJavaResource( "com.foo.SuperFromStaleInnerEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class SuperFromStaleInnerEntryPoint", " implements EntryPoint {", " static class A { int f() { return 1; } }", " static class B extends A {", " int f() { return 2; }", " void g() {", " new InterfaceOne() {", " public int m() { return 2 + B.super.f(); }", " }.m();", " }", " }", " @Override", " public void onModuleLoad() {", " new B().g();", " }", "}"); private MockJavaResource interfaceOneResource = JavaResourceBase.createMockJavaResource( "com.foo.InterfaceOne", "package com.foo;", "interface InterfaceOne {", " int m();", "}"); private MockResource helloModuleResource = JavaResourceBase.createMockResource( "com/foo/Hello.gwt.xml", "<module>", " <inherits name='com.google.gwt.user.User'/>", " <source path=''/>", " <set-property name='user.agent' value='safari'/>", " <entry-point class='com.foo.HelloEntryPoint'/>", "</module>"); private MockJavaResource helloEntryPointResource = JavaResourceBase.createMockJavaResource( "com.foo.HelloEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "import com.google.gwt.event.dom.client.ClickEvent;", "import com.google.gwt.event.dom.client.ClickHandler;", "import com.google.gwt.user.client.Window;", "import com.google.gwt.user.client.ui.Button;", "import com.google.gwt.user.client.ui.RootPanel;", "public class HelloEntryPoint implements EntryPoint {", " public void onModuleLoad() {", " Button b = new Button(\"Click me\", new ClickHandler() {", " public void onClick(ClickEvent event) {", " Window.alert(\"Hello, AJAX\");", " }", " });", " RootPanel.get().add(b);" , " }", "}"); private Set<String> emptySet = stringSet(); @Override protected void setUp() throws Exception { super.setUp(); FooResourceGenerator.runCount = 0; BarReferencesFooGenerator.runCount = 0; CauseStringRebindGenerator.runCount = 0; CauseShortRebindGenerator.runCount = 0; } public void testAllValidArgs() { CompilerOptionsImpl options = new CompilerOptionsImpl(); Compiler.ArgProcessor argProcessor = new Compiler.ArgProcessor(options); assertProcessSuccess(argProcessor, new String[] {"-logLevel", "DEBUG", "-style", "PRETTY", "-ea", "-gen", "myGen", "-war", "myWar", "-workDir", "myWork", "-extra", "myExtra", "-incremental", "-localWorkers", "2", "-sourceLevel", "1.8", "c.g.g.h.H", "my.Module"}); assertEquals(new File("myGen").getAbsoluteFile(), options.getGenDir().getAbsoluteFile()); assertEquals(new File("myWar"), options.getWarDir()); assertEquals(new File("myWork"), options.getWorkDir()); assertEquals(new File("myExtra"), options.getExtraDir()); assertEquals(2, options.getLocalWorkers()); assertEquals(TreeLogger.DEBUG, options.getLogLevel()); assertEquals(JsOutputOption.PRETTY, options.getOutput()); assertTrue(options.isEnableAssertions()); assertTrue(options.shouldClusterSimilarFunctions()); assertTrue(options.shouldInlineLiteralParameters()); assertTrue(options.shouldOptimizeDataflow()); assertTrue(options.shouldOrdinalizeEnums()); assertTrue(options.shouldRemoveDuplicateFunctions()); assertTrue(options.isIncrementalCompileEnabled()); assertEquals(SourceLevel.JAVA8, options.getSourceLevel()); assertEquals(2, options.getModuleNames().size()); assertEquals("c.g.g.h.H", options.getModuleNames().get(0)); assertEquals("my.Module", options.getModuleNames().get(1)); } public void testDefaultArgs() { CompilerOptionsImpl options = new CompilerOptionsImpl(); Compiler.ArgProcessor argProcessor = new Compiler.ArgProcessor(options); assertProcessSuccess(argProcessor, new String[]{"c.g.g.h.H"}); assertEquals(null, options.getGenDir()); assertEquals(new File("war").getAbsoluteFile(), options.getWarDir().getAbsoluteFile()); assertEquals(null, options.getWorkDir()); assertEquals(null, options.getExtraDir()); assertEquals(TreeLogger.INFO, options.getLogLevel()); assertEquals(JsOutputOption.OBFUSCATED, options.getOutput()); assertFalse(options.isEnableAssertions()); assertTrue(options.shouldClusterSimilarFunctions()); assertTrue(options.shouldInlineLiteralParameters()); assertTrue(options.shouldOptimizeDataflow()); assertTrue(options.shouldOrdinalizeEnums()); assertTrue(options.shouldRemoveDuplicateFunctions()); assertFalse(options.isIncrementalCompileEnabled()); assertEquals(1, options.getLocalWorkers()); assertEquals(1, options.getModuleNames().size()); assertEquals("c.g.g.h.H", options.getModuleNames().get(0)); } public void testForbiddenArgs() { CompilerOptionsImpl options = new CompilerOptionsImpl(); Compiler.ArgProcessor argProcessor = new Compiler.ArgProcessor(options); assertProcessFailure(argProcessor, "Unknown argument", new String[]{"-out", "www"}); assertProcessFailure(argProcessor, "Source level must be one of", new String[]{"-sourceLevel", "ssss"}); assertProcessFailure(argProcessor, "Source level must be one of", new String[]{"-sourceLevel", "1.5"}); } /** * Tests ordering for emum {@link SourceLevel}. */ public void testSourceLevelOrdering() { SourceLevel[] sourceLevels = SourceLevel.values(); SourceLevel previousSourceLevel = sourceLevels[0]; for (int i = 1; i < sourceLevels.length; i++) { assertTrue(Utility.versionCompare(previousSourceLevel.getStringValue(), sourceLevels[i].getStringValue()) < 0); previousSourceLevel = sourceLevels[i]; } } public void testSourceLevelSelection() { assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.4")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.5")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.6")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.6_26")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.7")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.8")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.9")); // not proper version strings => default to JAVA8. assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.6u3")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.6b3")); assertEquals(SourceLevel.JAVA8, SourceLevel.getBestMatchingVersion("1.7b3")); } /** * Verify that a compile with a @JsType at least compiles successfully. */ public void testGwtCreateJsTypeRebindResult() throws Exception { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compileToJs(compilerOptions, Files.createTempDir(), "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, gwtCreateEntryPointResource), new MinimalRebuildCache(), emptySet, JsOutputOption.OBFUSCATED); } /** * Test that some lightly referenced interface through a @JsFunction is included in the output. */ public void testReferenceThroughJsFunction() throws Exception { MockJavaResource someJsFunction = JavaResourceBase.createMockJavaResource( "com.foo.SomeJsFunction", "package com.foo;", "import jsinterop.annotations.JsFunction;", "@JsFunction", "public interface SomeJsFunction {", " void m();", "}"); MockJavaResource jsFunctionInterfaceImplementation = JavaResourceBase.createMockJavaResource( "com.foo.Impl", "package com.foo;", "public final class Impl implements SomeJsFunction {", " public void m() { SomeInterface.class.getName(); } ", "}"); MockJavaResource someInterface = JavaResourceBase.createMockJavaResource( "com.foo.SomeInterface", "package com.foo;", "public interface SomeInterface {", "}"); MockJavaResource testEntryPoint = JavaResourceBase.createMockJavaResource( "com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " private static native void f(SomeJsFunction f) /*-{}-*/;", " public void onModuleLoad() {", // Create Impl and pass it to JS but do not explicitly call m " f(new Impl());", " }", "}"); MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/TestEntryPoint.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.TestEntryPoint'/>", "</module>"); CompilerOptions compilerOptions = new CompilerOptionsImpl(); String js = compileToJs(compilerOptions, Files.createTempDir(), testEntryPoint.getTypeName(), Lists.newArrayList(moduleResource, testEntryPoint, someJsFunction, jsFunctionInterfaceImplementation, someInterface), new MinimalRebuildCache(), emptySet, JsOutputOption.DETAILED); // Make sure the referenced class literals ends up being included in the resulting JS. String classliteralHolderVarName = JjsUtils.mangleMemberName("com.google.gwt.lang.ClassLiteralHolder", JjsUtils.classLiteralFieldNameFromJavahTypeSignatureName( JjsUtils.javahSignatureFromName(someInterface.getTypeName()))); assertTrue(js.contains("var " + classliteralHolderVarName + " = ")); } /** * Tests that changing Js namespace name on an exported method comes out accurately. * * <p>An unrelated and non-updated @JsType is also included in each compile to verify that updated * exports do not forget non-edited items in a recompile. */ public void testChangeJsNamespaceOnMethod() throws Exception { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setGenerateJsInteropExports(true); MockJavaResource jsNamespaceFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsMethod;", "import jsinterop.annotations.JsType;", "@JsType public class Foo {", " @JsMethod(namespace=\"spazz\") public static void doStaticBar() {}", "}"); MockJavaResource regularFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType public class Foo {", " public static void doStaticBar() {}", "}"); checkRecompiledModifiedApp( compilerOptions, "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, emptyEntryPointResource, jsTypeBarResource), regularFooResource, jsNamespaceFooResource, stringSet("com.foo.Bar", "com.foo.Foo"), JsOutputOption.DETAILED); } /** * Tests that changing Js namespace on a class comes out accurately. * * <p>An unrelated and non-updated @JsType is also included in each compile to verify that updated * exports do not forget non-edited items in a recompile. */ public void testChangeJsNamespaceOnClass() throws Exception { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setGenerateJsInteropExports(true); MockJavaResource jsNamespaceFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType(namespace=\"spazz\") public class Foo {", " public static void doStaticBar() {}", "}"); MockJavaResource regularFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType public class Foo {", " public static void doStaticBar() {}", "}"); checkRecompiledModifiedApp( compilerOptions, "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, emptyEntryPointResource, jsTypeBarResource), regularFooResource, jsNamespaceFooResource, stringSet("com.foo.Bar", "com.foo.Foo"), JsOutputOption.DETAILED); } /** * Tests that changing @JsFunction name on an interface comes out accurately. * * <p>An unrelated and non-updated @JsType is also included in each compile to verify that updated * exports do not forget non-edited items in a recompile. */ public void testChangeJsFunction() throws Exception { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setGenerateJsInteropExports(true); MockJavaResource jsFunctionIFooResource = JavaResourceBase.createMockJavaResource( "com.foo.IFoo", "package com.foo;", "import jsinterop.annotations.JsFunction;", "@JsFunction public interface IFoo {", " int foo(int x);", "}"); MockJavaResource regularIFooResource = JavaResourceBase.createMockJavaResource( "com.foo.IFoo", "package com.foo;", "public interface IFoo {", " int foo(int x);", "}"); MockJavaResource fooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "public final class Foo implements IFoo {", " @Override public int foo(int x) { return 0; }", "}"); checkRecompiledModifiedApp( compilerOptions, "com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, entryPointResourceForFoo, fooResource, jsTypeBarResource), regularIFooResource, jsFunctionIFooResource, stringSet("com.foo.Bar", "com.foo.Foo", "com.foo.IFoo", "com.foo.TestEntryPoint"), JsOutputOption.DETAILED); } /** * Tests that toggling JsProperty methods in an interface comes out accurately. * * <p>An unrelated and non-updated @JsType is also included in each compile to verify that updated * exports do not forget non-edited items in a recompile. */ public void testChangeJsProperty() throws Exception { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setGenerateJsInteropExports(true); MockJavaResource jsPropertyIFooResource = JavaResourceBase.createMockJavaResource( "com.foo.IFoo", "package com.foo;", "import jsinterop.annotations.JsProperty;", "import jsinterop.annotations.JsType;", "@JsType public interface IFoo {", " @JsProperty int getX();", " @JsProperty int getY();", "}"); MockJavaResource regularIFooResource = JavaResourceBase.createMockJavaResource( "com.foo.IFoo", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType public interface IFoo {", " int getX();", " int getY();", "}"); MockJavaResource fooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "public class Foo implements IFoo {", " @Override public int getX() { return 0; }", " @Override public int getY() { return 0; }", "}"); checkRecompiledModifiedApp( compilerOptions, "com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, entryPointResourceForFoo, fooResource, jsTypeBarResource), regularIFooResource, jsPropertyIFooResource, stringSet("com.foo.Bar", "com.foo.Foo", "com.foo.IFoo", "com.foo.TestEntryPoint"), JsOutputOption.DETAILED); } /** * Tests that adding a @JsType annotation on a class comes out accurately and that removing it * comes out accurately as well. * * <p>An unrelated and non-updated @JsType is also included in each compile to verify that updated * exports do not forget non-edited items in a recompile. */ public void testChangeJsType() throws Exception { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setGenerateJsInteropExports(true); MockJavaResource jsTypeFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType public class Foo {", " void doInstanceBar() {}", " public static void doStaticBar() {}", "}"); MockJavaResource regularFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "public class Foo {}"); checkRecompiledModifiedApp( compilerOptions, "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, emptyEntryPointResource, jsTypeBarResource), regularFooResource, jsTypeFooResource, stringSet("com.foo.Bar", "com.foo.Foo"), JsOutputOption.DETAILED); } /** * Tests that changing a prototype on a @JsType annotated class comes out accurately. * * <p>An unrelated and non-updated @JsType is also included in each compile to verify that updated * exports do not forget non-edited items in a recompile. */ public void testChangeJsTypeNative() throws Exception { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setGenerateJsInteropExports(true); MockJavaResource nativeFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType(isNative=true) public class Foo {", " public static native void doStaticBar();", "}"); MockJavaResource regularFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType public class Foo {", " public static void doStaticBar() {}", "}"); checkRecompiledModifiedApp( compilerOptions, "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, emptyEntryPointResource, jsTypeBarResource), regularFooResource, nativeFooResource, stringSet("com.foo.Bar", "com.foo.Foo"), JsOutputOption.DETAILED); } /** * Tests that adding a @JsIgnore annotation on a method comes out accurately and that removing * it comes out accurately as well. * * <p>An unrelated and non-updated @JsType is also included in each compile to verify that updated * exports do not forget non-edited items in a recompile. */ public void testChangeJsIgnore() throws Exception { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setGenerateJsInteropExports(true); MockJavaResource jsIgnoreFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsIgnore;", "import jsinterop.annotations.JsType;", "@JsType public class Foo {", " @JsIgnore public static void doStaticBar() {}", "}"); MockJavaResource regularFooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType public class Foo {", " public static void doStaticBar() {}", "}"); checkRecompiledModifiedApp( compilerOptions, "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, emptyEntryPointResource, jsTypeBarResource), regularFooResource, jsIgnoreFooResource, stringSet("com.foo.Bar", "com.foo.Foo"), JsOutputOption.DETAILED); } public void testJsInteropNameCollision() throws Exception { MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache(); File applicationDir = Files.createTempDir(); CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setGenerateJsInteropExports(true); // Simple compile with one dialog.alert() export succeeds. compileToJs(compilerOptions, applicationDir, "com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, dialogEntryPointResource, simpleDialogResourceWithExport, complexDialogResourceSansExport), minimalRebuildCache, emptySet, JsOutputOption.OBFUSCATED); try { // Exporting a second dialog.alert() fails with an exported name collision. compileToJs(compilerOptions, applicationDir, "com.foo.SimpleModule", Lists.<MockResource> newArrayList(complexDialogResourceWithExport), minimalRebuildCache, emptySet, JsOutputOption.OBFUSCATED); fail("Compile should have failed"); } catch (UnableToCompleteException e) { // success } // Reverting to just a single dialog.alert() starts succeeding again. compileToJs(compilerOptions, applicationDir, "com.foo.SimpleModule", Lists.<MockResource>newArrayList(complexDialogResourceSansExport), minimalRebuildCache, stringSet("com.foo.SimpleDialog", "com.foo.ComplexDialog", "com.foo.TestEntryPoint"), JsOutputOption.OBFUSCATED); } public void testGwtCreateJsoRebindResult() throws Exception { try { compileToJs(Files.createTempDir(), "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, brokenGwtCreateEntryPointResource), new MinimalRebuildCache(), emptySet, JsOutputOption.OBFUSCATED); fail("Compile should have failed"); } catch (UnableToCompleteException e) { // success } } public void testNonZeroArgConstructorEntryPoint() throws Exception { MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/NonZeroArgConstructor.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.NonZeroArgConstructorEntryPoint'/>", "</module>"); MockJavaResource entryPointResource = JavaResourceBase.createMockJavaResource( "com.foo.NonZeroArgConstructorEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class NonZeroArgConstructorEntryPoint implements EntryPoint {", " public NonZeroArgConstructorEntryPoint(String s) {", " }", " public void onModuleLoad() {", " }", "}"); MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache(); File applicationDir = Files.createTempDir(); CompilerOptions compilerOptions = new CompilerOptionsImpl(); UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder(); builder.setLowestLogLevel(TreeLogger.ERROR); builder.expectError(Pattern.compile("Errors in .*"), null); builder.expectError("Line 3: Rebind result 'com.foo.NonZeroArgConstructorEntryPoint' " + "has no default (zero argument) constructors", null); UnitTestTreeLogger errorLogger = builder.createLogger(); try { // Simple compile with one dialog.alert() export succeeds. compileToJs(errorLogger, compilerOptions, applicationDir, "com.foo.NonZeroArgConstructor", Lists.newArrayList(moduleResource, entryPointResource), minimalRebuildCache, emptySet, JsOutputOption.OBFUSCATED); fail("Compile should have failed"); } catch (UnableToCompleteException expected) { errorLogger.assertCorrectLogEntries(); } } public void testDeterministicBuild_Draft_StackModeStrip() throws UnableToCompleteException, IOException { assertDeterministicBuild(HELLO_MODULE_STACKMODE_STRIP, 0); } public void testDeterministicBuild_Optimized_StackModeStrip() throws UnableToCompleteException, IOException { assertDeterministicBuild(HELLO_MODULE_STACKMODE_STRIP, 9); } public void testDeterministicBuild_Draft() throws UnableToCompleteException, IOException { assertDeterministicBuild(HELLO_MODULE, 0); } public void testDeterministicBuild_Optimized() throws UnableToCompleteException, IOException { assertDeterministicBuild(HELLO_MODULE, 9); } public void testSuccessfulCompile_jsoClassLiteralOrder() throws Exception { // Crafted resource to make sure the a native subclass is compiled before the JSO class, // In the case of native sublcasses the class hierarchy does not match the class literal // hierarchy. MockJavaResource nativeClassAndSubclass = JavaResourceBase.createMockJavaResource( "com.foo.MyNativeSubclass", "package com.foo;", "import jsinterop.annotations.JsType;", "@JsType(isNative=true)", "class NativeClass {", "}", "public class MyNativeSubclass extends NativeClass {", "}"); MockJavaResource testEntryPoint = JavaResourceBase.createMockJavaResource( "com.foo.MyEntryPoint", "package com.foo;", "import com.foo.MyNativeSubclass;", "public class MyEntryPoint extends MyNativeSubclass {", " public void onModuleLoad() {", " Object o = new Object();", " if (MyNativeSubclass.class.getName() == null) ", " o = new MyNativeSubclass();", // Make .clazz reachable so that class literals are emmitted with the respective // classses. " o.getClass();", " }", "}"); MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/MyEntryPoint.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.MyEntryPoint'/>", "</module>"); CompilerOptions compilerOptions = new CompilerOptionsImpl(); // Make sure it compiles successfully with no assertions compilerOptions.setEnableAssertions(true); compilerOptions.setGenerateJsInteropExports(true); compilerOptions.setOutput(JsOutputOption.PRETTY); compilerOptions.setOptimizationLevel(9); assertCompileSucceeds(compilerOptions, testEntryPoint.getTypeName(), Lists.newArrayList(moduleResource, nativeClassAndSubclass, testEntryPoint)); } // TODO(stalcup): add recompile tests for file deletion. public void testIncrementalRecompile_noop() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_noop(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_noop(JsOutputOption.DETAILED); } public void testIncrementalRecompile_dateStampChange() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_dateStampChange(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_dateStampChange(JsOutputOption.DETAILED); } // Repro for bug #9518 public void testIncrementalRecompile_jsPropertyConsistencyCheck() throws UnableToCompleteException, IOException, InterruptedException { // Supertype defines the getter. MockJavaResource superType = JavaResourceBase.createMockJavaResource( "com.foo.SuperType", "package com.foo;", "import jsinterop.annotations.JsProperty;", "public class SuperType {", " @JsProperty String getString() { return null; }", "}"); // Subtype defines the setter. MockJavaResource subType = JavaResourceBase.createMockJavaResource( "com.foo.SubType", "package com.foo;", "import jsinterop.annotations.JsProperty;", "public class SubType extends SuperType {", " @JsProperty void setString(String s) {}", "}"); MockJavaResource testEntryPoint = JavaResourceBase.createMockJavaResource( "com.foo.TestEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class TestEntryPoint implements EntryPoint {", " public void onModuleLoad() {", // Create Impl and pass it to JS but do not explicitly call m " new SubType();", " }", "}"); MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/TestModule.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.TestEntryPoint'/>", "</module>"); MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); // Perform a first compile. compileToJs(relinkApplicationDir, "com.foo.TestModule", Lists.newArrayList(moduleResource, testEntryPoint, subType, superType), relinkMinimalRebuildCache, null, JsOutputOption.OBFUSCATED); // Invalidate ONLY the subtype. The types referred by the parameters of supertype methods are // going to be reference only which means that the supertype property will have a reference // to a JClassType for "java.lang.String" that isExternal() and is different from the // (non external) reference in the supertype. relinkMinimalRebuildCache.markSourceFileStale("com/foo/SubType.java"); compileToJs(relinkApplicationDir, "com.foo.TestModule", Lists.<MockResource> newArrayList(), relinkMinimalRebuildCache, null, JsOutputOption.OBFUSCATED); } public void testIncrementalRecompile_invalidatePreamble() throws UnableToCompleteException, IOException, InterruptedException { MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); // Perform a first compile. compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, emptyEntryPointResource), relinkMinimalRebuildCache, null, JsOutputOption.OBFUSCATED); // On first compile nothing is explicitly stale, only implicitly stale. assertEquals(0, relinkMinimalRebuildCache.getStaleTypeNames().size()); // Recompile with a deep change that invalidates the preamble. relinkMinimalRebuildCache.markSourceFileStale("java/lang/Object.java"); compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource> newArrayList(), relinkMinimalRebuildCache, null, JsOutputOption.OBFUSCATED); // Show that preamble invalidation marks everything stale. assertTrue(relinkMinimalRebuildCache.getProcessedStaleTypeNames().size() > 100); // Recompile again with a tiny change. Prove that it's not stuck repeatedly invalidating the // whole world. compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource> newArrayList(emptyEntryPointResource), relinkMinimalRebuildCache, null, JsOutputOption.OBFUSCATED); // Show that only this little change is stale, not the whole world. assertEquals(2, getStaleTypeNames(relinkMinimalRebuildCache).size()); } public void testIncrementalRecompile_bridgeMethodOverrideChain() throws UnableToCompleteException, IOException, InterruptedException { MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); // Perform a first compile. compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, overriddenMethodChainEntryPointResource, topResource, middleResource, bottomResource), relinkMinimalRebuildCache, null, JsOutputOption.OBFUSCATED); // On first compile nothing is explicitly stale, only implicitly stale. assertEquals(0, relinkMinimalRebuildCache.getStaleTypeNames().size()); // Recompile with a change to Bottom. relinkMinimalRebuildCache.markSourceFileStale("com/foo/Bottom.java"); compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource> newArrayList(), relinkMinimalRebuildCache, null, JsOutputOption.OBFUSCATED); // Show that the third level bridge method override of Top.run() is seen to be live and thus // makes type com.foo.Bottom$Value live. assertTrue( relinkMinimalRebuildCache.getProcessedStaleTypeNames().contains("com.foo.Bottom$Value")); } public void testIncrementalRecompile_classLiteralNewReference() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_classLiteralNewReference(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_classLiteralNewReference(JsOutputOption.DETAILED); } public void testIncrementalRecompile_primitiveClassLiteralReference() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_primitiveClassLiteralReference(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_primitiveClassLiteralReference(JsOutputOption.DETAILED); } public void testIncrementalRecompile_superClassOrder() throws UnableToCompleteException, IOException, InterruptedException { // Linked output is sorted alphabetically except that super-classes come before sub-classes. If // on recompile a sub-class -> super-class relationship is lost then a sub-class with an // alphabetically earlier name might start linking out before the super-class. checkIncrementalRecompile_superClassOrder(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_superClassOrder(JsOutputOption.DETAILED); } public void testIncrementalRecompile_superFromStaleInner() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_superFromStaleInner(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_superFromStaleInner(JsOutputOption.DETAILED); } public void testIncrementalRecompile_deterministicUiBinder() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_deterministicUiBinder(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_deterministicUiBinder(JsOutputOption.DETAILED); } public void testIncrementalRecompile_uiBinderCssChange() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_uiBinderCssChange(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_uiBinderCssChange(JsOutputOption.DETAILED); } public void testIncrementalRecompile_unstableGeneratorReferencesModifiedType() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_unstableGeneratorReferencesModifiedType(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_unstableGeneratorReferencesModifiedType(JsOutputOption.DETAILED); } public void testIncrementalRecompile_withErrors() throws UnableToCompleteException, IOException, InterruptedException { MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/Errors.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.ErrorsEntryPoint'/>", "</module>"); MockJavaResource entryPointResource = JavaResourceBase.createMockJavaResource( "com.foo.ErrorsEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class ErrorsEntryPoint implements EntryPoint {", " public void onModuleLoad() {", " Foo.foo();", " }", "}"); MockJavaResource fooResource = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "public class Foo {", " public static void foo() {", " }", "}"); MockJavaResource fooResourceWithErrors = JavaResourceBase.createMockJavaResource( "com.foo.Foo", "package com.foo;", "public class Foo {", " public static void foo() {", " // x() method is not defined anywhere, should result in a compile error.", " x();", " }", "}"); MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache(); File applicationDir = Files.createTempDir(); CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setSourceLevel(SourceLevel.JAVA8); // Compile the application with no errors. compileToJs(TreeLogger.NULL, compilerOptions, applicationDir, "com.foo.Errors", Lists.newArrayList(moduleResource, entryPointResource, fooResource), minimalRebuildCache, emptySet, JsOutputOption.OBFUSCATED); // Recompile and expect error reporting. UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder(); builder.setLowestLogLevel(TreeLogger.ERROR); builder.expectError("Line 5: The method x() is undefined for the type Foo", null); UnitTestTreeLogger errorLogger = builder.createLogger(); try { // Recompile but now the changed file has an error compileToJs(errorLogger, compilerOptions, applicationDir, "com.foo.Errors", Lists.newArrayList(moduleResource, entryPointResource, fooResourceWithErrors), minimalRebuildCache, emptySet, JsOutputOption.OBFUSCATED); fail("Compile should have failed"); } catch (UnableToCompleteException expected) { errorLogger.assertLogEntriesContainExpected(); } } public void testIncrementalRecompile_representedAsNative() throws UnableToCompleteException, IOException, InterruptedException { MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/RepresentedAsNative.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.RepresentedAsNativeEntryPoint'/>", "</module>"); MockResource entryPointResource = JavaResourceBase.createMockJavaResource( "com.foo.RepresentedAsNativeEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class RepresentedAsNativeEntryPoint implements EntryPoint {", " public void onModuleLoad() {", " Double d = new Double(1d);", " }", "}"); MockResource modifiedEntryPointResource = JavaResourceBase.createMockJavaResource( "com.foo.RepresentedAsNativeEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class RepresentedAsNativeEntryPoint implements EntryPoint {", " public void onModuleLoad() {", " Double d = new Double(\"1\");", " }", "}"); PrintWriterTreeLogger logger = new PrintWriterTreeLogger(); logger.setMaxDetail(TreeLogger.ERROR); MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache(); File applicationDir = Files.createTempDir(); CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setSourceLevel(SourceLevel.JAVA8); compilerOptions.setGenerateJsInteropExports(false); // Compile the application with no errors. compileToJs(logger, compilerOptions, applicationDir, "com.foo.RepresentedAsNative", Lists.newArrayList(moduleResource, entryPointResource), minimalRebuildCache, emptySet, JsOutputOption.OBFUSCATED); // Recompile but now the changed file has an error compileToJs(logger, compilerOptions, applicationDir, "com.foo.RepresentedAsNative", Lists.newArrayList(modifiedEntryPointResource), minimalRebuildCache, stringSet( "com.foo.RepresentedAsNativeEntryPoint", getEntryMethodHolderTypeName("com.foo.RepresentedAsNative")), JsOutputOption.OBFUSCATED); } public void testIncrementalRecompile_functionSignatureChange() throws UnableToCompleteException, IOException, InterruptedException { // Not testing recompile equality with Pretty/Obfuscated output since the JsIncrementalNamer's // behavior is order dependent, and while still correct, will come out different in a recompile // with this change versus a from scratch compile with this change. checkIncrementalRecompile_functionSignatureChange(JsOutputOption.DETAILED); } public void testIncrementalRecompile_compileTimeConstantChange() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_compileTimeConstantChange(JsOutputOption.DETAILED); checkIncrementalRecompile_compileTimeConstantChange(JsOutputOption.OBFUSCATED); } public void testIncrementalRecompile_transitivelyFoldableConstant() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_transitivelyFoldableConstant(JsOutputOption.DETAILED); checkIncrementalRecompile_transitivelyFoldableConstant(JsOutputOption.OBFUSCATED); } public void testIncrementalRecompile_packagePrivateDispatch() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_packagePrivateOverride(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_packagePrivateOverride(JsOutputOption.DETAILED); } public void testIncrementalRecompile_prettyOutput() throws UnableToCompleteException, IOException, InterruptedException { // Nominal tests for pretty output. Pretty and Obfuscated output share most of the same code // paths. checkIncrementalRecompile_typeHierarchyChange(JsOutputOption.PRETTY); checkIncrementalRecompile_unreachableIncompatibleChange(JsOutputOption.PRETTY); } public void testIncrementalRecompile_regularClassMadeIntoJsoClass() throws UnableToCompleteException, IOException, InterruptedException { // Not testing recompile equality with Pretty/Obfuscated output since the JsIncrementalNamer's // behavior is order dependent, and while still correct, will come out different in a recompile // with this change versus a from scratch compile with this change. checkIncrementalRecompile_regularClassMadeIntoJsoClass(JsOutputOption.DETAILED); } public void testIncrementalRecompile_unreachableIncompatibleChange() throws UnableToCompleteException, IOException, InterruptedException { // Not testing recompile equality with Pretty/Obfuscated output since the JsIncrementalNamer's // behavior is order dependent, and while still correct, will come out different in a recompile // with this change versus a from scratch compile with this change. checkIncrementalRecompile_unreachableIncompatibleChange(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_unreachableIncompatibleChange(JsOutputOption.DETAILED); } public void testIncrementalRecompile_typeHierarchyChange() throws UnableToCompleteException, IOException, InterruptedException { checkIncrementalRecompile_typeHierarchyChange(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_typeHierarchyChange(JsOutputOption.DETAILED); } public void testIncrementalRecompile_defaultMethod() throws UnableToCompleteException, IOException, InterruptedException { // Tests that default method on superclasses are correctly constructed checkIncrementalRecompile_defaultMethod(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_defaultMethod(JsOutputOption.DETAILED); } public void testIncrementalRecompile_devirtualizeUnchangedJso() throws UnableToCompleteException, IOException, InterruptedException { // Tests that a JSO calls through interfaces are correctly devirtualized when compiling per file // and the JSOs nor their single impl interfaces are not stale. checkIncrementalRecompile_devirtualizeUnchangedJso(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_devirtualizeUnchangedJso(JsOutputOption.DETAILED); } public void testIncrementalRecompile_devirtualizeString() throws UnableToCompleteException, IOException, InterruptedException { // Tests that String calls through interfaces are correctly devirtualized when compiling per // file and neither String nor CharSequence interface are stale. checkIncrementalRecompile_devirtualizeString(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_devirtualizeString(JsOutputOption.DETAILED); } public void testIncrementalRecompile_devirtualizeComparable() throws UnableToCompleteException, IOException, InterruptedException { // Tests that Doublecalls through interfaces are correctly devirtualized when compiling per // file and neither String nor CharSequence interface are stale. checkIncrementalRecompile_devirtualizeComparable(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_devirtualizeComparable(JsOutputOption.DETAILED); } public void testIncrementalRecompile_multipleClassGenerator() throws UnableToCompleteException, IOException, InterruptedException { // Tests that a Generated type that is not directly referenced from the rebound GWT.create() // call is still marked stale, regenerated, retraversed and output as JS. checkIncrementalRecompile_multipleClassGenerator(JsOutputOption.OBFUSCATED); checkIncrementalRecompile_multipleClassGenerator(JsOutputOption.DETAILED); } public void testIncrementalRecompile_singleJsoIntfDispatchChange() throws UnableToCompleteException, IOException, InterruptedException { // Not testing recompile equality with Pretty/Obfuscated output since the JsIncrementalNamer's // behavior is order dependent, and while still correct, will come out different in a recompile // with this change versus a from scratch compile with this change. checkIncrementalRecompile_singleJsoIntfDispatchChange(JsOutputOption.DETAILED); } public void testIncrementalRecompile_dualJsoIntfDispatchChange() throws UnableToCompleteException, IOException, InterruptedException { // Not testing recompile equality with Pretty/Obfuscated output since the JsIncrementalNamer's // behavior is order dependent, and while still correct, will come out different in a recompile // with this change versus a from scratch compile with this change. checkIncrementalRecompile_dualJsoIntfDispatchChange(JsOutputOption.DETAILED); } public void testIncrementalRecompile_generatorInputResourceChange() throws IOException, UnableToCompleteException, InterruptedException { // Not testing recompile equality with Pretty/Obfuscated output since the JsIncrementalNamer's // behavior is order dependent, and while still correct, will come out different in a recompile // with this change versus a from scratch compile with this change. checkIncrementalRecompile_generatorInputResourceChange(JsOutputOption.DETAILED); } public void testIncrementalRecompile_invalidatedGeneratorOutputRerunsGenerator() throws UnableToCompleteException, IOException, InterruptedException { // BarReferencesFoo Generator hasn't run yet. assertEquals(0, BarReferencesFooGenerator.runCount); CompilerOptions compilerOptions = new CompilerOptionsImpl(); List<MockResource> sharedResources = Lists.newArrayList(barReferencesFooGeneratorModuleResource, generatorEntryPointResource); JsOutputOption output = JsOutputOption.OBFUSCATED; List<MockResource> originalResources = Lists.newArrayList(sharedResources); originalResources.add(fooResource); // Compile the app with original files, modify a file and do a per-file recompile. MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule", originalResources, relinkMinimalRebuildCache, emptySet, output); // BarReferencesFoo Generator has now been run once. assertEquals(1, BarReferencesFooGenerator.runCount); // Recompile with no changes, which should not trigger any Generator runs. compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource>newArrayList(), relinkMinimalRebuildCache, emptySet, output); // Since there were no changes BarReferencesFoo Generator was not run again. assertEquals(1, BarReferencesFooGenerator.runCount); // 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, stringSet("com.foo.TestEntryPoint", "com.foo.Foo", "com.foo.Bar"), output); // BarReferencesFoo Generator was run again. assertEquals(2, BarReferencesFooGenerator.runCount); } public void testIncrementalRecompile_invalidatedGeneratorOutputRerunsCascadedGenerators() throws UnableToCompleteException, IOException, InterruptedException { // Generators haven't run yet. assertEquals(0, CauseStringRebindGenerator.runCount); assertEquals(0, CauseShortRebindGenerator.runCount); assertEquals(0, FooResourceGenerator.runCount); CompilerOptions compilerOptions = new CompilerOptionsImpl(); List<MockResource> sharedResources = Lists.newArrayList(cascadingGeneratorModuleResource, generatorEntryPointResource, classNameToGenerateResource); JsOutputOption output = JsOutputOption.OBFUSCATED; List<MockResource> originalResources = Lists.newArrayList(sharedResources); // Compile the app with original files. MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule", originalResources, relinkMinimalRebuildCache, emptySet, output); // Generators have now been run once. assertEquals(1, CauseStringRebindGenerator.runCount); assertEquals(1, CauseShortRebindGenerator.runCount); assertEquals(1, FooResourceGenerator.runCount); // Recompile with no changes, which should not trigger any Generator runs. compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource>newArrayList(), relinkMinimalRebuildCache, emptySet, output); // Since there were no changes Generators were not run again. assertEquals(1, CauseStringRebindGenerator.runCount); assertEquals(1, CauseShortRebindGenerator.runCount); assertEquals(1, FooResourceGenerator.runCount); // Recompile with a modified resource, which should invalidate the output of the // FooResourceGenerator and cascade the invalidate the Generators that triggered // FooResourceGenerator. compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource>newArrayList(modifiedClassNameToGenerateResource), relinkMinimalRebuildCache, stringSet("com.foo.TestEntryPoint", "com.foo.Baz$InnerBaz", "com.foo.Bar", "com.foo.HasCustomContent", "com.foo.FooReplacementTwo"), output); // Generators were run again. assertEquals(2, CauseStringRebindGenerator.runCount); assertEquals(2, CauseShortRebindGenerator.runCount); assertEquals(2, FooResourceGenerator.runCount); } public void testIncrementalRecompile_carriesOverGeneratorArtifacts() throws UnableToCompleteException, IOException, InterruptedException { // Foo Generator hasn't run yet. assertEquals(0, FooResourceGenerator.runCount); CompilerOptions compilerOptions = new CompilerOptionsImpl(); List<MockResource> sharedResources = Lists.newArrayList(resourceReadingGeneratorModuleResource, referencesBarAndGeneratorEntryPointResource, classNameToGenerateResource, barReferencesFooResource); JsOutputOption output = JsOutputOption.OBFUSCATED; List<MockResource> originalResources = Lists.newArrayList(sharedResources); originalResources.add(fooResource); // Compile the app with original files. MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule", originalResources, relinkMinimalRebuildCache, emptySet, output); // Foo Generator has now been run once. assertEquals(1, FooResourceGenerator.runCount); // The bar.txt artifact was output. File barFile = new File(relinkApplicationDir.getPath() + File.separator + "com.foo.SimpleModule" + File.separator + "bar.txt"); assertTrue(barFile.exists()); // Recompile with just 1 file change, which should not trigger any Generator runs. compileToJs(compilerOptions, relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource> newArrayList(fooResource), relinkMinimalRebuildCache, stringSet("com.foo.Foo", "com.foo.Bar"), output); // Foo Generator was not run again. assertEquals(1, FooResourceGenerator.runCount); // But the bar.txt artifact was still output. barFile = new File(relinkApplicationDir.getPath() + File.separator + "com.foo.SimpleModule" + File.separator + "bar.txt"); assertTrue(barFile.exists()); } /** * Regression test for UnifyAST assertion failure problem in incremental SDM. */ public void testIncrementalRecompile_unifyASTAssertionRegression() throws UnableToCompleteException, IOException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); List<MockResource> originalResources = Lists.newArrayList(helloEntryPointResource, helloModuleResource); JsOutputOption output = JsOutputOption.OBFUSCATED; // Compile the app with original files. MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); compileToJs(compilerOptions, relinkApplicationDir, "com.foo.Hello", originalResources, relinkMinimalRebuildCache, emptySet, output); } private void checkIncrementalRecompile_noop(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); String originalJs = compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists .newArrayList(simpleModuleResource, simpleModelEntryPointResource, simpleModelResource, constantsModelResource), relinkMinimalRebuildCache, emptySet, output); // Compile again with absolutely no file changes and reusing the minimalRebuildCache. String relinkedJs = compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource>newArrayList(), relinkMinimalRebuildCache, emptySet, output); assertTrue(originalJs.equals(relinkedJs)); } private void checkIncrementalRecompile_defaultMethod(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { // Tests that when a superclass has a "inherits" a default method, MockJavaResource interfaceWithDefaultMethod = JavaResourceBase.createMockJavaResource( "com.foo.InterfaceWithDefaultMethod", "package com.foo;", "interface InterfaceWithDefaultMethod {", " default String m() {return null; }", "}"); MockJavaResource classImplementingInterfaceWithDefaultMethod = JavaResourceBase.createMockJavaResource( "com.foo.classImplementingInterfaceWithDefaultMethod", "package com.foo;", "public class classImplementingInterfaceWithDefaultMethod", " implements InterfaceWithDefaultMethod {", "}"); MockJavaResource aSubclass = JavaResourceBase.createMockJavaResource( "com.foo.ASubclass", "package com.foo;", "public class ASubclass extends classImplementingInterfaceWithDefaultMethod {", " public String someMethod() {return super.m(); }", "}"); MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/DefaultMethod.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.DefaultMethodEntryPoint'/>", "</module>"); MockJavaResource entryPointResource = JavaResourceBase.createMockJavaResource( "com.foo.DefaultMethodEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class DefaultMethodEntryPoint implements EntryPoint {", " public void onModuleLoad() {", " new ASubclass().someMethod();", " }", "}"); CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); compilerOptions.setSourceLevel(SourceLevel.JAVA8); checkRecompiledModifiedApp(compilerOptions, "com.foo.DefaultMethod", Lists.newArrayList(moduleResource, entryPointResource, aSubclass, classImplementingInterfaceWithDefaultMethod, interfaceWithDefaultMethod), aSubclass, aSubclass, stringSet("com.foo.ASubclass", "com.foo.DefaultMethodEntryPoint"), output); } public void checkIncrementalRecompile_classLiteralNewReference(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { MockJavaResource interfaceA = JavaResourceBase.createMockJavaResource( "com.foo.A", "package com.foo;", "interface A {", " static String b = \"\";", "}"); MockJavaResource classBSansLiteralReference = JavaResourceBase.createMockJavaResource( "com.foo.B", "package com.foo;", "public class B {", "}"); MockJavaResource classBWithLiteralReference = JavaResourceBase.createMockJavaResource( "com.foo.B", "package com.foo;", "public class B {", " public B() { Class c = A.class; }", "}"); MockJavaResource classC = JavaResourceBase.createMockJavaResource( "com.foo.C", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class C implements EntryPoint {", " public void onModuleLoad() {", " if (A.b == null) ", " new B();", " }", "}"); MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/ClassLiteralReference.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.C'/>", "</module>"); checkRecompiledModifiedApp("com.foo.ClassLiteralReference", Lists.newArrayList( moduleResource, interfaceA, classC), classBSansLiteralReference, classBWithLiteralReference, stringSet("com.foo.B", "com.foo.C"), output); } public void checkIncrementalRecompile_primitiveClassLiteralReference(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { MockJavaResource classA = JavaResourceBase.createMockJavaResource( "com.foo.A", "package com.foo;", "public class A {", " public A() { ", " Class c = void.class; ", " c = int.class; ", " c = boolean.class;", " c = short.class;", " c = byte.class;", " c = long.class;", " c = float.class;", " c = double.class;", " }", "}"); MockJavaResource classB = JavaResourceBase.createMockJavaResource( "com.foo.B", "package com.foo;", "public class B {", " public B() { }", " public void m () { new A(); }", "}"); MockJavaResource classC = JavaResourceBase.createMockJavaResource( "com.foo.C", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class C implements EntryPoint {", " public void onModuleLoad() {", " new B().m();", " }", "}"); MockResource moduleResource = JavaResourceBase.createMockResource( "com/foo/PrimitiveClassLiteralReference.gwt.xml", "<module>", " <source path=''/>", " <entry-point class='com.foo.C'/>", "</module>"); checkRecompiledModifiedApp("com.foo.PrimitiveClassLiteralReference", Lists.newArrayList( moduleResource, classA, classB), classC, classC, stringSet(getEntryMethodHolderTypeName("com.foo.PrimitiveClassLiteralReference"), "com.foo.C"), output); } private void checkIncrementalRecompile_dateStampChange(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); String originalJs = compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, simpleModelEntryPointResource, simpleModelResource, constantsModelResource), relinkMinimalRebuildCache, emptySet, output); // Compile again with the same source but a new date stamp on SimpleModel and reusing the // minimalRebuildCache. String relinkedJs = compileToJs(relinkApplicationDir, "com.foo.SimpleModule", Lists.<MockResource>newArrayList(simpleModelResource), relinkMinimalRebuildCache, stringSet("com.foo.TestEntryPoint", "com.foo.SimpleModel"), output); assertTrue(originalJs.equals(relinkedJs)); } private void checkIncrementalRecompile_superClassOrder(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { // Mark ReferencedBySuperClassResource modified, so that SuperClass becomes stale. This will // result in SuperClass's indexing being rebuilt but NOT ASubClasses's. If there is a bug in the // index updates then the link output ordering will change. checkRecompiledModifiedApp("com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, superClassOrderEntryPointResource, referencedBySuperClassResource, superClassResource, aSubClassResource), referencedBySuperClassResource, referencedBySuperClassResource, stringSet("com.foo.SuperClass", "com.foo.ReferencedBySuperClass"), output); } private void checkIncrementalRecompile_superFromStaleInner(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { // Makes sure that type ids for (effectively static) super dispatches, i.e. of the form // super.m() or X.this.m(), are computed also for classes only referenced by stale classes. checkRecompiledModifiedApp("com.foo.SuperFromStaleInnerModule", Lists.newArrayList(superFromStaleInnerModuleResource, superFromStaleInnerEntryPointResource), interfaceOneResource, interfaceOneResource, stringSet(interfaceOneResource.getTypeName(), "com.foo.SuperFromStaleInnerEntryPoint$B", "com.foo.SuperFromStaleInnerEntryPoint$B$1"), output); } private void checkIncrementalRecompile_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$Binder", "com.foo.TestEntryPoint", "com.foo.MyWidget_BinderImpl", "com.foo.MyWidget_BinderImpl$Widgets"), output); } private void checkIncrementalRecompile_uiBinderCssChange(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { // Switches from a white styled widget to a grey styled widget in the CSS in the style tag // nested in the .ui.xml template file. String binderImpl = "com.foo.MyWidget_BinderImpl"; checkRecompiledModifiedApp("com.foo.UiBinderTestModule", Lists.newArrayList(uiBinderTestModuleResource, uiBinderTestEntryPointResource, myWidget), myWidgetWithWhiteStyleUiXml, myWidgetWithGreyStyleUiXml, stringSet("com.foo.MyWidget", binderImpl, binderImpl + "_GenBundle_default_InlineClientBundleGenerator", binderImpl + "_GenBundle_default_InlineClientBundleGenerator$1", binderImpl + "_GenBundle_default_InlineClientBundleGenerator$styleInitializer", binderImpl + "_TemplateImpl", binderImpl + "$Widgets", binderImpl + "$Template"), output); } private void checkIncrementalRecompile_packagePrivateOverride(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, packagePrivateDispatchEntryPointResource, packagePrivateParentResource, referencesParentResource), packagePrivateChildResource, packagePrivateChildResource, stringSet("com.foo.PackagePrivateChild", "com.foo.TestEntryPoint"), output); } private void checkIncrementalRecompile_regularClassMadeIntoJsoClass(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, jsoArrayTestEntryPointResource, someClassReferringToJsoOneArrays, someClassReferringToJsoTwoArrays, jsoOne), jsoTwo_before, jsoTwo_after, stringSet("com.foo.JsoTwo", "com.foo.SomeClassReferringToJsoTwoArrays"), output); } private void checkIncrementalRecompile_unstableGeneratorReferencesModifiedType( JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { // Make sure that the recompile with changes and the full compile with changes have the same // output from the unstable generator, so that it is safe to make byte for byte output // comparisons. UnstableNestedAnonymousGenerator.outputVersionOrder.addAll(Lists.newArrayList( // first compile OutputVersion.A, // recompile with changes OutputVersion.B, // full compile with changes OutputVersion.B)); checkRecompiledModifiedApp("com.foo.SimpleModule", Lists.newArrayList(generatorEntryPointResource, unstableGeneratorModuleResource), fooResource, sameContentDifferentTimeFooResource, stringSet( "com.foo.NestedAnonymousClasses$1", "com.foo.NestedAnonymousClasses", "com.foo.NestedAnonymousClasses$ClassTwo", "com.foo.NestedAnonymousClasses$ClassOne", "com.foo.TestEntryPoint", "com.foo.NestedAnonymousClasses$1$1", "com.foo.Foo"), output); } private void checkIncrementalRecompile_functionSignatureChange(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { checkRecompiledModifiedApp("com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, simpleModelEntryPointResource, constantsModelResource), simpleModelResource, modifiedFunctionSignatureSimpleModelResource, stringSet("com.foo.TestEntryPoint", "com.foo.SimpleModel"), output); } private void checkIncrementalRecompile_compileTimeConstantChange(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { checkRecompiledModifiedApp("com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, simpleModelEntryPointResource, simpleModelResource), constantsModelResource, modifiedConstantsModelResource, stringSet("com.foo.SimpleModel", "com.foo.Constants"), output); } private void checkIncrementalRecompile_transitivelyFoldableConstant(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { // Tests that constants that are provided by types are only referenced by reference only types // (hence not traversed) are still available for constant propagation. checkRecompiledModifiedApp("com.foo.TransitivelyFoldableConstantModule", Lists.newArrayList( transitivelyFoldableConstantModuleResource, classOneResource, classTwoResource), transitivelyFoldableConstantEntryPointResource, transitivelyFoldableConstantEntryPointResource, stringSet("com.foo.TransitivelyFoldableConstantEntryPoint", getEntryMethodHolderTypeName("com.foo.TransitivelyFoldableConstantModule")), output); } private void checkIncrementalRecompile_unreachableIncompatibleChange(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { checkRecompiledModifiedApp("com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, emptyEntryPointResource), nonCompilableFooResource, nonCompilableFooResource, stringSet(), output); } private void checkIncrementalRecompile_typeHierarchyChange(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { checkRecompiledModifiedApp("com.foo.SimpleModule", Lists.newArrayList(simpleModuleResource, modifiedSuperEntryPointResource, modelAResource, modelBResource, modelDResource), modelCResource, modifiedSuperModelCResource, stringSet("com.foo.TestEntryPoint", "com.foo.ModelC", "com.foo.ModelD"), output); } private void checkIncrementalRecompile_singleJsoIntfDispatchChange(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, modifiedJsoIntfDispatchEntryPointResource, callerResource, fooInterfaceResource), nonJsoFooResource, jsoFooResource, stringSet("com.foo.TestEntryPoint", "com.foo.Foo", "com.foo.Caller"), output); } private void checkIncrementalRecompile_devirtualizeUnchangedJso(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.SimpleModule", Lists.newArrayList(jsoTestModuleResource, simpleFactory, simpleIntf, simpleJso), jsoTestEntryPointResource, jsoTestEntryPointResource, stringSet("com.foo.TestEntryPoint", getEntryMethodHolderTypeName("com.foo.SimpleModule")), output); } private void checkIncrementalRecompile_devirtualizeString(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.DevirtualizeStringModule", Lists.newArrayList(devirtualizeStringModuleResource), devirtualizeStringEntryPointResource, devirtualizeStringEntryPointResource, stringSet("com.foo.DevirtualizeStringEntryPoint", getEntryMethodHolderTypeName("com.foo.DevirtualizeStringModule")), output); } private void checkIncrementalRecompile_devirtualizeComparable(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { final MockResource devirtualizeComparableModuleResource = JavaResourceBase.createMockResource("com/foo/DevirtualizeComparableModule.gwt.xml", "<module>", "<source path=''/>", "<entry-point class='com.foo.DevirtualizeComparableEntryPoint'/>", "</module>"); final MockJavaResource devirtualizeComparableEntryPointResource = JavaResourceBase.createMockJavaResource("com.foo.DevirtualizeComparableEntryPoint", "package com.foo;", "import com.google.gwt.core.client.EntryPoint;", "public class DevirtualizeComparableEntryPoint implements EntryPoint {", " @Override", " public void onModuleLoad() {", " Comparable c = (Double) 0.1;", " c.compareTo(c);", " }", "}"); CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.DevirtualizeComparableModule", Lists.newArrayList(devirtualizeComparableModuleResource), devirtualizeComparableEntryPointResource, devirtualizeComparableEntryPointResource, stringSet("com.foo.DevirtualizeComparableEntryPoint", getEntryMethodHolderTypeName("com.foo.DevirtualizeComparableModule")), output); } private void checkIncrementalRecompile_multipleClassGenerator(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.SimpleModule", Lists.newArrayList(multipleClassGeneratorModuleResource, generatorEntryPointResource), bazResource, bazResource, stringSet("com.foo.Baz", "com.foo.TestEntryPoint", "com.foo.Bar"), output); } private void checkIncrementalRecompile_dualJsoIntfDispatchChange(JsOutputOption output) throws UnableToCompleteException, IOException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.SimpleModule", Lists.newArrayList( simpleModuleResource, modifiedJsoIntfDispatchEntryPointResource, callerResource, fooInterfaceResource, regularFooImplemetorResource), nonJsoFooResource, jsoFooResource, stringSet("com.foo.TestEntryPoint", "com.foo.Foo", "com.foo.Caller"), output); } private void checkIncrementalRecompile_generatorInputResourceChange(JsOutputOption outputOption) throws IOException, UnableToCompleteException, InterruptedException { CompilerOptions compilerOptions = new CompilerOptionsImpl(); compilerOptions.setUseDetailedTypeIds(true); checkRecompiledModifiedApp(compilerOptions, "com.foo.SimpleModule", Lists.newArrayList( resourceReadingGeneratorModuleResource, generatorEntryPointResource, fooInterfaceResource, nonJsoFooResource), classNameToGenerateResource, modifiedClassNameToGenerateResource, stringSet("com.foo.TestEntryPoint", "com.foo.HasCustomContent", "com.foo.FooReplacementTwo"), outputOption); } private void assertCompileSucceeds(CompilerOptions options, String moduleName, List<MockResource> applicationResources) throws Exception { File compileWorkDir = Utility.makeTemporaryDirectory(null, moduleName); final CompilerOptionsImpl compilerOptions = new CompilerOptionsImpl(options); PrintWriterTreeLogger logger = new PrintWriterTreeLogger(); logger.setMaxDetail(TreeLogger.ERROR); String oldPersistentUnitCacheValue = System.setProperty(UnitCacheSingleton.GWT_PERSISTENTUNITCACHE, "false"); try { File applicationDir = Files.createTempDir(); Thread.sleep(1001); // 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); if (outputDir.exists()) { Util.recursiveDelete(outputDir, true); } // Fake out the resource loader to read resources both from the normal classpath as well as // this new application directory. ResourceLoader resourceLoader = ResourceLoaders.forClassLoader(Thread.currentThread()); resourceLoader = ResourceLoaders.forPathAndFallback(ImmutableList.of(applicationDir), resourceLoader); // Setup options to perform a per-file compile, output to this new application directory and // compile the given module. compilerOptions.setGenerateJsInteropExports(true); compilerOptions.setWarDir(applicationDir); compilerOptions.setModuleNames(ImmutableList.of(moduleName)); // Write the Java/XML/etc resources that make up the test application. for (MockResource applicationResource : applicationResources) { writeResourceTo(applicationResource, applicationDir); } // Cause the module to be cached with a reference to the prefixed resource loader so that the // compile process will see those resources. ModuleDefLoader.clearModuleCache(); ModuleDefLoader.loadFromResources(logger, moduleName, resourceLoader, true); options.addModuleName(moduleName); options.setWarDir(new File(compileWorkDir, "war")); options.setExtraDir(new File(compileWorkDir, "extra")); // Run the compiler once here. Compiler.compile(logger, options); } finally { Util.recursiveDelete(compileWorkDir, false); if (oldPersistentUnitCacheValue == null) { System.clearProperty(UnitCacheSingleton.GWT_PERSISTENTUNITCACHE); } else { System.setProperty(UnitCacheSingleton.GWT_PERSISTENTUNITCACHE, oldPersistentUnitCacheValue); } } } private void assertDeterministicBuild(String topLevelModule, int optimizationLevel) throws UnableToCompleteException, IOException { final CompilerOptionsImpl options = new CompilerOptionsImpl(); options.setOptimizationLevel(optimizationLevel); File firstCompileWorkDir = Utility.makeTemporaryDirectory(null, "hellowork"); File secondCompileWorkDir = Utility.makeTemporaryDirectory(null, "hellowork"); String oldPersistentUnitCacheValue = System.setProperty(UnitCacheSingleton.GWT_PERSISTENTUNITCACHE, "false"); try { options.addModuleName(topLevelModule); options.setWarDir(new File(firstCompileWorkDir, "war")); options.setExtraDir(new File(firstCompileWorkDir, "extra")); TreeLogger logger = TreeLogger.NULL; // Run the compiler once here. Compiler.compile(logger, options); Set<String> firstTimeOutput = Sets.newHashSet(new File(options.getWarDir() + "/hello").list()); options.setWarDir(new File(secondCompileWorkDir, "war")); options.setExtraDir(new File(secondCompileWorkDir, "extra")); // Run the compiler for a second time here. Compiler.compile(logger, options); Set<String> secondTimeOutput = Sets.newHashSet(new File(options.getWarDir() + "/hello").list()); // It is only necessary to check that the filenames in the output directory are the same // because the names of the files for the JavaScript outputs are the hash of its contents. assertEquals("First and second compile produced different outputs", firstTimeOutput, secondTimeOutput); } finally { if (oldPersistentUnitCacheValue == null) { System.clearProperty(UnitCacheSingleton.GWT_PERSISTENTUNITCACHE); } else { System.setProperty(UnitCacheSingleton.GWT_PERSISTENTUNITCACHE, oldPersistentUnitCacheValue); } Util.recursiveDelete(firstCompileWorkDir, false); Util.recursiveDelete(secondCompileWorkDir, false); } } /** * Compiles an initial application with version 1 of file Foo, then recompiles using version 2 of * file Foo. Lastly it performs a final from scratch compile using version 2 of file Foo and * verifies that the recompile and the full compile (both of which used version 2 of file Foo) * come out the same. */ private void checkRecompiledModifiedApp( String moduleName, List<MockResource> sharedResources, MockResource originalResource, MockResource modifiedResource, Set<String> expectedStaleTypeNamesOnModify, JsOutputOption output) throws IOException, UnableToCompleteException, InterruptedException { checkRecompiledModifiedApp(new CompilerOptionsImpl(), moduleName, sharedResources, originalResource, modifiedResource, expectedStaleTypeNamesOnModify, output); } /** * Compiles an initial application with version 1 of file Foo, then recompiles using version 2 of * file Foo. Lastly it performs a final from scratch compile using version 2 of file Foo and * verifies that the recompile and the full compile (both of which used version 2 of file Foo) * come out the same. */ private void checkRecompiledModifiedApp( CompilerOptions compilerOptions, String moduleName, List<MockResource> sharedResources, MockResource originalResource, MockResource modifiedResource, Set<String> expectedStaleTypeNamesOnModify, JsOutputOption output) throws IOException, UnableToCompleteException, InterruptedException { List<MockResource> originalResources = Lists.newArrayList(sharedResources); originalResources.add(originalResource); List<MockResource> modifiedResources = Lists.newArrayList(sharedResources); modifiedResources.add(modifiedResource); // Compile the app with original files, modify a file and do a per-file recompile. MinimalRebuildCache relinkMinimalRebuildCache = new MinimalRebuildCache(); File relinkApplicationDir = Files.createTempDir(); String originalAppFromScratchJs = compileToJs(compilerOptions, relinkApplicationDir, moduleName, originalResources, relinkMinimalRebuildCache, emptySet, output); String modifiedAppRelinkedJs = compileToJs(compilerOptions, relinkApplicationDir, moduleName, Lists.<MockResource> newArrayList(modifiedResource), relinkMinimalRebuildCache, expectedStaleTypeNamesOnModify, output); // Compile the app from scratch with the modified file. MinimalRebuildCache fromScratchMinimalRebuildCache = new MinimalRebuildCache(); File fromScratchApplicationDir = Files.createTempDir(); String modifiedAppFromScratchJs = compileToJs(compilerOptions, fromScratchApplicationDir, moduleName, modifiedResources, fromScratchMinimalRebuildCache, emptySet, output); // If a resource contents were changed between the original compile and the relink compile // check that the output JS has also changed. If all resources have the same content (their // timestamps might have changed) then outputs should be the same. boolean wasNotModified = modifiedResource == originalResource; assertTrue("Resource " + modifiedResource.getPath() + " was " + (wasNotModified ? "NOT " : "") + "modified but the output was " + (wasNotModified ? "" : "NOT ") + "different.", wasNotModified == originalAppFromScratchJs.equals(modifiedAppRelinkedJs)); // If per-file compiles properly avoids global-knowledge dependencies and correctly invalidates // referencing types when a type changes, then the relinked and from scratch JS will be // identical. assertTrue(modifiedAppRelinkedJs.equals(modifiedAppFromScratchJs)); } private String compileToJs(File applicationDir, String moduleName, List<MockResource> applicationResources, MinimalRebuildCache minimalRebuildCache, Set<String> expectedStaleTypeNames, JsOutputOption output) throws IOException, UnableToCompleteException, InterruptedException { return compileToJs(new CompilerOptionsImpl(), applicationDir, moduleName, applicationResources, minimalRebuildCache, expectedStaleTypeNames, output); } private String compileToJs(CompilerOptions compilerOptions, File applicationDir, String moduleName, List<MockResource> applicationResources, MinimalRebuildCache minimalRebuildCache, Set<String> expectedProcessedStaleTypeNames, JsOutputOption output) throws IOException, UnableToCompleteException, InterruptedException { PrintWriterTreeLogger logger = new PrintWriterTreeLogger(); logger.setMaxDetail(TreeLogger.ERROR); return compileToJs(logger, compilerOptions, applicationDir, moduleName, applicationResources, minimalRebuildCache, expectedProcessedStaleTypeNames, output); } private String compileToJs(TreeLogger logger, CompilerOptions compilerOptions, File applicationDir, String moduleName, List<MockResource> applicationResources, MinimalRebuildCache minimalRebuildCache, Set<String> expectedProcessedStaleTypeNames, JsOutputOption output) throws IOException, UnableToCompleteException, InterruptedException { // Make sure we're using a MemoryUnitCache. System.setProperty(UnitCacheSingleton.GWT_PERSISTENTUNITCACHE, "false"); // Wait 1 second so that any new file modification times are actually different. Thread.sleep(1001); // 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); if (outputDir.exists()) { Util.recursiveDelete(outputDir, true); } // Fake out the resource loader to read resources both from the normal classpath as well as this // new application directory. ResourceLoader resourceLoader = ResourceLoaders.forClassLoader(Thread.currentThread()); resourceLoader = ResourceLoaders.forPathAndFallback(ImmutableList.of(applicationDir), resourceLoader); // Setup options to perform a per-file compile, output to this new application directory and // compile the given module. compilerOptions.setIncrementalCompileEnabled(true); compilerOptions.setWarDir(applicationDir); compilerOptions.setModuleNames(ImmutableList.of(moduleName)); compilerOptions.setOutput(output); // Write the Java/XML/etc resources that make up the test application. for (MockResource applicationResource : applicationResources) { writeResourceTo(applicationResource, applicationDir); } // Cause the module to be cached with a reference to the prefixed resource loader so that the // compile process will see those resources. ModuleDefLoader.clearModuleCache(); ModuleDef moduleDef = ModuleDefLoader.loadFromResources(logger, moduleName, resourceLoader, true); // Run the compile. Compiler.compile(logger, compilerOptions, minimalRebuildCache, moduleDef); // Find, read and return the created JS. File outputJsFile = null; outputDir = new File(applicationDir.getPath() + File.separator + moduleName); if (outputDir.exists()) { for (File outputFile : outputDir.listFiles()) { if (outputFile.getPath().endsWith(".cache.js")) { outputJsFile = outputFile; break; } } } if (outputJsFile == null) { throw new UnableToCompleteException(); } if (expectedProcessedStaleTypeNames != null) { assertEquals(expectedProcessedStaleTypeNames, getStaleTypeNames(minimalRebuildCache)); } return Files.toString(outputJsFile, Charsets.UTF_8); } private Set<String> getStaleTypeNames(MinimalRebuildCache relinkMinimalRebuildCache) { Set<String> staleTypeNames = new HashSet<>(relinkMinimalRebuildCache.getProcessedStaleTypeNames()); // List of JRE types that provide JsInterop entry points and jre native JsTypes. These are // always traversed fully and polute the tests, so they will be removed from stale type // comparisons. staleTypeNames.removeAll(Arrays.asList( "java.lang.Boolean", "java.lang.CharSequence", "java.lang.Comparable", "java.lang.Double", "java.lang.HasCharSequenceTypeMarker", "java.lang.HasComparableTypeMarker", "java.lang.Number", "java.lang.String", "java.lang.String$NativeFunction", "java.lang.String$NativeString", "java.lang.Throwable", "java.lang.Throwable$NativeError", "java.lang.Throwable$NativeTypeError", "javaemul.internal.NativeRegExp")); return staleTypeNames; } private String getEntryMethodHolderTypeName(String typeName) { return "com.google.gwt.lang." + EntryMethodHolderGenerator.getEntryMethodHolderTypeName(typeName); } private Set<String> stringSet(String... strings) { return Sets.newHashSet(strings); } private void writeResourceTo(MockResource mockResource, File applicationDir) throws IOException { File resourceFile = new File(applicationDir.getAbsolutePath() + File.separator + mockResource.getPath()); resourceFile.getParentFile().mkdirs(); Files.write(mockResource.getContent(), resourceFile, Charsets.UTF_8); } }