Allow naming a runAsync call using a class literal. There are now two overloads of GWT.runAsync, and the new overload takes an extra argument supplying the class literal. Review by: kprobst (SplitPointRecorder), scottb (the bulk of it) git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6097 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java index 7826574..597ad33 100644 --- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java +++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java
@@ -80,7 +80,7 @@ htmlOut.printRaw(curLine); htmlOut.newline(); } - + if (!jprogram.getSplitPointInitialSequence().isEmpty()) { curLine = "<initialseq>"; htmlOut.printRaw(curLine); @@ -92,7 +92,7 @@ htmlOut.printRaw(curLine); htmlOut.newline(); } - + htmlOut.indentOut(); curLine = "</initialseq>"; htmlOut.printRaw(curLine); @@ -127,13 +127,19 @@ Map<String, Integer> counts = new HashMap<String, Integer>(); for (RunAsyncReplacement replacement : program.getRunAsyncReplacements().values()) { int entryNumber = replacement.getNumber(); - String methodDescription = fullMethodDescription(replacement.getEnclosingMethod()); - if (counts.containsKey(methodDescription)) { - counts.put(methodDescription, counts.get(methodDescription) + 1); - methodDescription += "#" - + Integer.toString(counts.get(methodDescription)); + String methodDescription; + if (replacement.getName() != null) { + methodDescription = replacement.getName(); } else { - counts.put(methodDescription, 1); + methodDescription = "@" + + fullMethodDescription(replacement.getEnclosingMethod()); + if (counts.containsKey(methodDescription)) { + counts.put(methodDescription, counts.get(methodDescription) + 1); + methodDescription += "#" + + Integer.toString(counts.get(methodDescription)); + } else { + counts.put(methodDescription, 1); + } } names.put(entryNumber, methodDescription);
diff --git a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java index e4baf2c..62e035b 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java +++ b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
@@ -59,8 +59,7 @@ public static final String REBIND_MAGIC_METHOD = "create"; public static final String ASYNC_MAGIC_METHOD = "runAsync"; - public static void reportRebindProblem(MessageSendSite site, - String message) { + public static void reportRebindProblem(MessageSendSite site, String message) { MessageSend messageSend = site.messageSend; Scope scope = site.scope; // Safe since CUS.referenceContext is set in its constructor. @@ -111,11 +110,18 @@ } } else { assert asyncMagicMethod; - if (args.length != 1) { + if (args.length != 1 && args.length != 2) { reportRebindProblem(site, - "GWT.runAsync() should take exactly one argument"); + "GWT.runAsync() should take one or two arguments"); return; } + if (args.length == 2) { + if (!(args[0] instanceof ClassLiteralAccess)) { + reportRebindProblem(site, + "Only class literals may be used to name a call to GWT.runAsync()"); + return; + } + } } if (asyncMagicMethod) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java index 8b8a4a1..0c0d090 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
@@ -260,6 +260,74 @@ dependencyRecorder.close(); } + /** + * Find a split point as designated in the {@link #PROP_INITIAL_SEQUENCE} + * configuration property. + */ + public static int findSplitPoint(String refString, JProgram program, + TreeLogger branch) throws UnableToCompleteException { + Map<JMethod, List<Integer>> methodToSplitPoint = reverseByEnclosingMethod(program.getRunAsyncReplacements()); + Map<String, List<Integer>> nameToSplitPoint = reverseByName(program.getRunAsyncReplacements()); + + if (refString.startsWith("@")) { + JsniRef jsniRef = JsniRef.parse(refString); + if (jsniRef == null) { + branch.log(TreeLogger.ERROR, "Badly formatted JSNI reference in " + + PROP_INITIAL_SEQUENCE + ": " + refString); + throw new UnableToCompleteException(); + } + final String lookupErrorHolder[] = new String[1]; + HasEnclosingType referent = JsniRefLookup.findJsniRefTarget(jsniRef, + program, new JsniRefLookup.ErrorReporter() { + public void reportError(String error) { + lookupErrorHolder[0] = error; + } + }); + if (referent == null) { + TreeLogger resolveLogger = branch.branch(TreeLogger.ERROR, + "Could not resolve JSNI reference: " + jsniRef); + resolveLogger.log(TreeLogger.ERROR, lookupErrorHolder[0]); + throw new UnableToCompleteException(); + } + + if (!(referent instanceof JMethod)) { + branch.log(TreeLogger.ERROR, "Not a method: " + referent); + throw new UnableToCompleteException(); + } + + JMethod method = (JMethod) referent; + List<Integer> splitPoints = methodToSplitPoint.get(method); + if (splitPoints == null) { + branch.log(TreeLogger.ERROR, + "Method does not enclose a runAsync call: " + jsniRef); + throw new UnableToCompleteException(); + } + if (splitPoints.size() > 1) { + branch.log(TreeLogger.ERROR, + "Method includes multiple runAsync calls, " + + "so it's ambiguous which one is meant: " + jsniRef); + throw new UnableToCompleteException(); + } + + return splitPoints.get(0); + } + + // Assume it's a raw class name + List<Integer> splitPoints = nameToSplitPoint.get(refString); + if (splitPoints == null || splitPoints.size() == 0) { + branch.log(TreeLogger.ERROR, "No runAsync call is labelled with class " + + refString); + throw new UnableToCompleteException(); + } + if (splitPoints.size() > 1) { + branch.log(TreeLogger.ERROR, + "More than one runAsync call is labelled with class " + refString); + throw new UnableToCompleteException(); + } + + return splitPoints.get(0); + } + public static int getExclusiveFragmentNumber(int splitPoint, int numSplitPoints) { return splitPoint; @@ -295,8 +363,6 @@ JProgram program, Properties properties) throws UnableToCompleteException { TreeLogger branch = logger.branch(TreeLogger.TRACE, "Looking up initial load sequence for split points"); - Map<JMethod, List<Integer>> reversedRunAsyncMap = reverse(program.getRunAsyncReplacements()); - LinkedHashSet<Integer> initialLoadSequence = new LinkedHashSet<Integer>(); ConfigurationProperty prop; @@ -314,8 +380,7 @@ } for (String refString : prop.getValues()) { - int splitPoint = findSplitPoint(refString, program, branch, - reversedRunAsyncMap); + int splitPoint = findSplitPoint(refString, program, branch); if (initialLoadSequence.contains(splitPoint)) { branch.log(TreeLogger.ERROR, "Split point specified more than once: " + refString); @@ -323,8 +388,6 @@ initialLoadSequence.add(splitPoint); } - // TODO(spoon) create an artifact in the aux dir describing the choice, so - // that SOYC can use it logInitialLoadSequence(logger, initialLoadSequence); installInitialLoadSequenceField(program, initialLoadSequence); program.setSplitPointInitialSequence(new ArrayList<Integer>( @@ -395,63 +458,6 @@ return cfa; } - /** - * Find a split point as designated in the {@link #PROP_INITIAL_SEQUENCE} - * configuration property. - * - * TODO(spoon) accept a labeled runAsync call, once runAsyncs can be labeled - */ - private static int findSplitPoint(String refString, JProgram program, - TreeLogger branch, Map<JMethod, List<Integer>> reversedRunAsyncMap) - throws UnableToCompleteException { - if (refString.startsWith("@")) { - JsniRef jsniRef = JsniRef.parse(refString); - if (jsniRef == null) { - branch.log(TreeLogger.ERROR, "Badly formatted JSNI reference in " - + PROP_INITIAL_SEQUENCE + ": " + refString); - throw new UnableToCompleteException(); - } - final String lookupErrorHolder[] = new String[1]; - HasEnclosingType referent = JsniRefLookup.findJsniRefTarget(jsniRef, - program, new JsniRefLookup.ErrorReporter() { - public void reportError(String error) { - lookupErrorHolder[0] = error; - } - }); - if (referent == null) { - TreeLogger resolveLogger = branch.branch(TreeLogger.ERROR, - "Could not resolve JSNI reference: " + jsniRef); - resolveLogger.log(TreeLogger.ERROR, lookupErrorHolder[0]); - throw new UnableToCompleteException(); - } - - if (!(referent instanceof JMethod)) { - branch.log(TreeLogger.ERROR, "Not a method: " + referent); - throw new UnableToCompleteException(); - } - - JMethod method = (JMethod) referent; - List<Integer> splitPoints = reversedRunAsyncMap.get(method); - if (splitPoints == null) { - branch.log(TreeLogger.ERROR, - "Method does not enclose a runAsync call: " + jsniRef); - throw new UnableToCompleteException(); - } - if (splitPoints.size() != 1) { - branch.log(TreeLogger.ERROR, - "Method includes multiple runAsync calls, " - + "so it's ambiguous which one is meant: " + jsniRef); - throw new UnableToCompleteException(); - } - - return splitPoints.get(0); - } - - branch.log(TreeLogger.ERROR, "Unrecognized designation of a split point: " - + refString); - throw new UnableToCompleteException(); - } - private static String fullNameString(JField field) { return field.getEnclosingType().getName() + "." + field.getName(); } @@ -527,7 +533,7 @@ * Reverses a runAsync map, returning a map from methods to the split point * numbers invoked from within that method. */ - private static Map<JMethod, List<Integer>> reverse( + private static Map<JMethod, List<Integer>> reverseByEnclosingMethod( Map<Integer, RunAsyncReplacement> runAsyncMap) { Map<JMethod, List<Integer>> revmap = new HashMap<JMethod, List<Integer>>(); for (RunAsyncReplacement replacement : runAsyncMap.values()) { @@ -542,6 +548,23 @@ return revmap; } + private static Map<String, List<Integer>> reverseByName( + Map<Integer, RunAsyncReplacement> runAsyncReplacements) { + Map<String, List<Integer>> revmap = new HashMap<String, List<Integer>>(); + for (RunAsyncReplacement replacement : runAsyncReplacements.values()) { + String name = replacement.getName(); + if (name != null) { + List<Integer> list = revmap.get(name); + if (list == null) { + list = new ArrayList<Integer>(); + revmap.put(name, list); + } + list.add(replacement.getNumber()); + } + } + return revmap; + } + /** * Traverse all code in the program that is reachable via split point * <code>splitPoint</code>.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java index 8c341da..f47edb6 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java
@@ -16,7 +16,9 @@ package com.google.gwt.dev.jjs.impl; import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.jjs.InternalCompilerException; import com.google.gwt.dev.jjs.ast.Context; +import com.google.gwt.dev.jjs.ast.JClassLiteral; import com.google.gwt.dev.jjs.ast.JClassType; import com.google.gwt.dev.jjs.ast.JExpression; import com.google.gwt.dev.jjs.ast.JField; @@ -34,6 +36,8 @@ /** * Replaces calls to * {@link com.google.gwt.core.client.GWT#runAsync(com.google.gwt.core.client.RunAsyncCallback)} + * and + * {@link com.google.gwt.core.client.GWT#runAsync(Class, com.google.gwt.core.client.RunAsyncCallback) * by calls to a fragment loader. */ public class ReplaceRunAsyncs { @@ -44,12 +48,15 @@ public static class RunAsyncReplacement implements Serializable { private final JMethod enclosingMethod; private final JMethod loadMethod; + private final String name; private final int number; - RunAsyncReplacement(int number, JMethod enclosingMethod, JMethod loadMethod) { + RunAsyncReplacement(int number, JMethod enclosingMethod, + JMethod loadMethod, String name) { this.number = number; this.enclosingMethod = enclosingMethod; this.loadMethod = loadMethod; + this.name = name; } /** @@ -68,6 +75,15 @@ } /** + * Return the name of this runAsync, which is specified by a class literal + * in the two-argument version of runAsync(). Returns <code>null</code> if + * there is no name for the call. + */ + public String getName() { + return name; + } + + /** * The index of this runAsync, numbered from 1 to n. */ public int getNumber() { @@ -87,16 +103,29 @@ @Override public void endVisit(JMethodCall x, Context ctx) { JMethod method = x.getTarget(); - if (method == program.getIndexedMethod("GWT.runAsync")) { - assert (x.getArgs().size() == 1); - JExpression asyncCallback = x.getArgs().get(0); + if (isRunAsyncMethod(method)) { + JExpression asyncCallback; + String name; + switch (x.getArgs().size()) { + case 1: + name = null; + asyncCallback = x.getArgs().get(0); + break; + case 2: + name = ((JClassLiteral) x.getArgs().get(0)).getRefType().getName(); + asyncCallback = x.getArgs().get(1); + break; + default: + throw new InternalCompilerException( + "runAsync call found with neither 1 nor 2 arguments: " + x); + } int entryNumber = entryCount++; JClassType loader = getFragmentLoader(entryNumber); JMethod loadMethod = getRunAsyncMethod(loader); assert loadMethod != null; runAsyncReplacements.put(entryNumber, new RunAsyncReplacement( - entryNumber, currentMethod, loadMethod)); + entryNumber, currentMethod, loadMethod, name)); JMethodCall methodCall = new JMethodCall(x.getSourceInfo(), null, loadMethod); @@ -113,6 +142,14 @@ currentMethod = x; return true; } + + private boolean isRunAsyncMethod(JMethod method) { + /* + * The method is overloaded, so check the enclosing type plus the name. + */ + return method.getEnclosingType() == program.getIndexedType("GWT") + && method.getName().equals("runAsync"); + } } public static void exec(TreeLogger logger, JProgram program) { @@ -133,6 +170,7 @@ } private JProgram program; + private Map<Integer, RunAsyncReplacement> runAsyncReplacements = new HashMap<Integer, RunAsyncReplacement>(); private ReplaceRunAsyncs(JProgram program) {
diff --git a/dev/core/test/com/google/gwt/dev/javac/RunAsyncNameTest.java b/dev/core/test/com/google/gwt/dev/javac/RunAsyncNameTest.java new file mode 100644 index 0000000..ad433e5 --- /dev/null +++ b/dev/core/test/com/google/gwt/dev/javac/RunAsyncNameTest.java
@@ -0,0 +1,81 @@ +/* + * 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.javac; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.dev.javac.impl.MockJavaResource; +import com.google.gwt.dev.jjs.impl.OptimizerTestBase; +import com.google.gwt.dev.util.UnitTestTreeLogger; + +/** + * This class tests naming of runAsync calls. Mostly it tests names that are + * invalid. + */ +public class RunAsyncNameTest extends OptimizerTestBase { + @Override + public void setUp() { + sourceOracle.addOrReplace(new MockJavaResource("test.CallRunAsync") { + @Override + protected CharSequence getContent() { + StringBuffer code = new StringBuffer(); + code.append("package test;\n"); + code.append("import com.google.gwt.core.client.GWT;\n"); + code.append("public class CallRunAsync {\n"); + code.append(" public static int notAmethod;"); + code.append(" public static void call0() { }\n"); + code.append(" public static void call1() {\n"); + code.append(" GWT.runAsync(null);\n"); + code.append(" }\n"); + code.append(" public static void call2() {\n"); + code.append(" GWT.runAsync(null);\n"); + code.append(" GWT.runAsync(null);\n"); + code.append(" }\n"); + code.append("}\n"); + return code; + } + }); + } + + /** + * Tests that it's an error to call the 2-argument version of GWT.runAsync + * with anything but a class literal. + */ + public void testNonLiteralInCall() throws UnableToCompleteException { + UnitTestTreeLogger logger; + { + UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder(); + builder.setLowestLogLevel(TreeLogger.ERROR); + builder.expectError("Errors in /mock/test/EntryPoint.java", null); + builder.expectError( + "Line 5: Only class literals may be used to name a call to GWT.runAsync()", + null); + builder.expectError("Cannot proceed due to previous errors", null); + logger = builder.createLogger(); + this.logger = logger; + } + + addSnippetImport("com.google.gwt.core.client.GWT"); + try { + compileSnippet("void", "GWT.runAsync((new Object()).getClass(), null);"); + fail("Expected compilation to fail"); + } catch (UnableToCompleteException e) { + // expected + } + + logger.assertCorrectLogEntries(); + } +}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java index 2684691..8a6e49e 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java +++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -21,6 +21,7 @@ import com.google.gwt.dev.javac.impl.JavaResourceBase; import com.google.gwt.dev.javac.impl.MockJavaResource; import com.google.gwt.dev.jdt.BasicWebModeCompiler; +import com.google.gwt.dev.jdt.FindDeferredBindingSitesVisitor; import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory; import com.google.gwt.dev.jjs.ast.JDeclaredType; import com.google.gwt.dev.jjs.ast.JMethod; @@ -98,6 +99,8 @@ code.append("public final class GWT {\n"); code.append(" public boolean isClient() { return true; };\n"); code.append(" public boolean isScript() { return true; };\n"); + code.append(" public static void runAsync(Object callback) { }\n"); + code.append(" public static void runAsync(Class<?> name, Object callback) { }\n"); code.append("}\n"); return code; } @@ -131,6 +134,16 @@ // JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, false); + /* + * FindDeferredBindingSitesVisitor detects errors in usage of magic methods + * in the GWT class. + */ + for (CompilationUnitDeclaration jdtCud : goldenCuds) { + jdtCud.traverse(new FindDeferredBindingSitesVisitor(), jdtCud.scope); + } + + JavaToJavaScriptCompiler.checkForErrors(logger, goldenCuds, true); + CorrelationFactory correlator = new DummyCorrelationFactory(); JProgram jprogram = new JProgram(correlator); JsProgram jsProgram = new JsProgram(correlator);
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java b/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java index 9ef05dc..50cf016 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java
@@ -77,8 +77,8 @@ /** * Finds a type by name. The type name may be short, e.g. <code>"Foo"</code>, - * or fully-qualified, e.g. <code>"com.google.example.Foo"</code>. If a - * short name is used, it must be unambiguous. + * or fully-qualified, e.g. <code>"com.google.example.Foo"</code>. If a short + * name is used, it must be unambiguous. */ public static JDeclaredType findType(JProgram program, String typeName) { JDeclaredType type = program.getFromTypeMap(typeName); @@ -117,9 +117,9 @@ return TreeLogger.NULL; } - protected final MockResourceOracle sourceOracle = new MockResourceOracle(); + protected TreeLogger logger = createTreeLogger(); - private final TreeLogger logger = createTreeLogger(); + protected final MockResourceOracle sourceOracle = new MockResourceOracle(); private final Set<String> snippetClassDecls = new TreeSet<String>(); @@ -133,6 +133,15 @@ } /** + * Adds a snippet of code, for example a field declaration, to the class that + * encloses the snippet subsequently passed to + * {@link #compileSnippet(String, String)}. + */ + protected void addSnippetClassDecl(String fieldDecl) { + snippetClassDecls.add(fieldDecl); + } + + /** * Adds an import statement for any code subsequently passed to * {@link #compileSnippet(String, String)}. */ @@ -141,14 +150,6 @@ } /** - * Adds a local field declaration for code subsequently passed to - * {@link #compileSnippet(String, String)}. - */ - protected void addSnippetClassDecl(String fieldDecl) { - snippetClassDecls.add(fieldDecl); - } - - /** * Returns the program that results from compiling the specified code snippet * as the body of an entry point method. *
diff --git a/user/src/com/google/gwt/core/client/GWT.java b/user/src/com/google/gwt/core/client/GWT.java index f58ce85..2028019 100644 --- a/user/src/com/google/gwt/core/client/GWT.java +++ b/user/src/com/google/gwt/core/client/GWT.java
@@ -204,34 +204,20 @@ } /** + * The same as {@link #runAsync(RunAsyncCallback)}, except with an extra + * parameter to provide a name for the call. The name parameter should be + * supplied with a class literal. No two runAsync calls in the same program + * should use the same name. + */ + public static void runAsync(Class<?> name, RunAsyncCallback callback) { + runAsyncWithoutCodeSplitting(callback); + } + + /** * Run the specified callback once the necessary code for it has been loaded. */ public static void runAsync(RunAsyncCallback callback) { - /* - * By default, just call the callback. This allows using - * <code>runAsync</code> in code that might or might not run in a web - * browser. - */ - if (isScript()) { - /* - * It's possible that the code splitter does not run, even for a - * production build. Signal a lightweight event, anyway, just so that - * there isn't a complete lack of lightweight events for runAsync. - */ - AsyncFragmentLoader.BROWSER_LOADER.logEventProgress("noDownloadNeeded", "begin"); - AsyncFragmentLoader.BROWSER_LOADER.logEventProgress("noDownloadNeeded", "end"); - } - - UncaughtExceptionHandler handler = sUncaughtExceptionHandler; - if (handler == null) { - callback.onSuccess(); - } else { - try { - callback.onSuccess(); - } catch (Throwable e) { - handler.onUncaughtException(e); - } - } + runAsyncWithoutCodeSplitting(callback); } /** @@ -261,4 +247,36 @@ private static native String getVersion0() /*-{ return $gwt_version; }-*/; + + /** + * This implementation of runAsync simply calls the callback. It is only used + * when no code splitting has occurred. + */ + private static void runAsyncWithoutCodeSplitting(RunAsyncCallback callback) { + /* + * By default, just call the callback. This allows using + * <code>runAsync</code> in code that might or might not run in a web + * browser. + */ + if (isScript()) { + /* + * It's possible that the code splitter does not run, even for a + * production build. Signal a lightweight event, anyway, just so that + * there isn't a complete lack of lightweight events for runAsync. + */ + AsyncFragmentLoader.BROWSER_LOADER.logEventProgress("noDownloadNeeded", "begin"); + AsyncFragmentLoader.BROWSER_LOADER.logEventProgress("noDownloadNeeded", "end"); + } + + UncaughtExceptionHandler handler = sUncaughtExceptionHandler; + if (handler == null) { + callback.onSuccess(); + } else { + try { + callback.onSuccess(); + } catch (Throwable e) { + handler.onUncaughtException(e); + } + } + } }
diff --git a/user/test/com/google/gwt/dev/jjs/InitialLoadSequence.gwt.xml b/user/test/com/google/gwt/dev/jjs/InitialLoadSequence.gwt.xml index 5c8a752..7dcb746 100644 --- a/user/test/com/google/gwt/dev/jjs/InitialLoadSequence.gwt.xml +++ b/user/test/com/google/gwt/dev/jjs/InitialLoadSequence.gwt.xml
@@ -21,5 +21,5 @@ <extend-configuration-property name="compiler.splitpoint.initial.sequence" value="@com.google.gwt.dev.jjs.test.InitialLoadSequenceTest::callback1()" /> <extend-configuration-property name="compiler.splitpoint.initial.sequence" - value="@com.google.gwt.dev.jjs.test.InitialLoadSequenceTest::callback2()" /> + value="com.google.gwt.dev.jjs.test.InitialLoadSequenceTest$Callback2Marker" /> </module>
diff --git a/user/test/com/google/gwt/dev/jjs/test/InitialLoadSequenceTest.java b/user/test/com/google/gwt/dev/jjs/test/InitialLoadSequenceTest.java index 478727e..39a2ff7 100644 --- a/user/test/com/google/gwt/dev/jjs/test/InitialLoadSequenceTest.java +++ b/user/test/com/google/gwt/dev/jjs/test/InitialLoadSequenceTest.java
@@ -26,6 +26,13 @@ */ public class InitialLoadSequenceTest extends GWTTestCase { private static final int TIMEOUT = 10000; + + /** + * This class is used to mark the second runAsync call. + */ + public static class Callback2Marker { + } + /** * The number of callbacks outstanding. When this gets to zero, the test * finishes. @@ -58,7 +65,7 @@ } private void callback2() { - GWT.runAsync(new RunAsyncCallback() { + GWT.runAsync(Callback2Marker.class, new RunAsyncCallback() { public void onFailure(Throwable reason) { fail(reason.toString()); }