Revision: 6097 Author: [email protected] Date: Tue Sep 8 11:07:39 2009 Log: 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) http://code.google.com/p/google-web-toolkit/source/detail?r=6097 Added: /trunk/dev/core/test/com/google/gwt/dev/javac/RunAsyncNameTest.java Modified: /trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java /trunk/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java /trunk/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java /trunk/user/src/com/google/gwt/core/client/GWT.java /trunk/user/test/com/google/gwt/dev/jjs/InitialLoadSequence.gwt.xml /trunk/user/test/com/google/gwt/dev/jjs/test/InitialLoadSequenceTest.java ======================================= --- /dev/null +++ /trunk/dev/core/test/com/google/gwt/dev/javac/RunAsyncNameTest.java Tue Sep 8 11:07:39 2009 @@ -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(); + } +} ======================================= --- /trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java Mon Jul 6 15:03:31 2009 +++ /trunk/dev/core/src/com/google/gwt/core/ext/soyc/impl/SplitPointRecorder.java Tue Sep 8 11:07:39 2009 @@ -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); ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java Thu Oct 30 09:17:17 2008 +++ /trunk/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java Tue Sep 8 11:07:39 2009 @@ -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) { ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java Tue Jul 28 12:00:07 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java Tue Sep 8 11:07:39 2009 @@ -259,6 +259,74 @@ new CodeSplitter(logger, jprogram, jsprogram, map, dependencyRecorder).execImpl(); 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) { @@ -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>( @@ -394,63 +457,6 @@ dependencyRecorder.endDependencyGraph(); 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()) { @@ -541,6 +547,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 ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java Thu Jul 16 16:35:08 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRunAsyncs.java Tue Sep 8 11:07:39 2009 @@ -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; } /** @@ -66,6 +73,15 @@ public JMethod getLoadMethod() { return loadMethod; } + + /** + * 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. @@ -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) { ======================================= --- /trunk/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java Tue Aug 11 12:45:07 2009 +++ /trunk/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java Tue Sep 8 11:07:39 2009 @@ -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); ======================================= --- /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java Tue Aug 11 12:45:07 2009 +++ /trunk/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java Tue Sep 8 11:07:39 2009 @@ -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); @@ -116,11 +116,11 @@ } return TreeLogger.NULL; } + + protected TreeLogger logger = createTreeLogger(); protected final MockResourceOracle sourceOracle = new MockResourceOracle(); - private final TreeLogger logger = createTreeLogger(); - private final Set<String> snippetClassDecls = new TreeSet<String>(); private final Set<String> snippetImports = new TreeSet<String>(); @@ -133,19 +133,20 @@ } /** - * Adds an import statement for any code subsequently passed to + * 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 addSnippetImport(String typeName) { - snippetImports.add(typeName); + protected void addSnippetClassDecl(String fieldDecl) { + snippetClassDecls.add(fieldDecl); } /** - * Adds a local field declaration for code subsequently passed to + * Adds an import statement for any code subsequently passed to * {...@link #compileSnippet(String, String)}. */ - protected void addSnippetClassDecl(String fieldDecl) { - snippetClassDecls.add(fieldDecl); + protected void addSnippetImport(String typeName) { + snippetImports.add(typeName); } /** ======================================= --- /trunk/user/src/com/google/gwt/core/client/GWT.java Thu Aug 20 11:38:19 2009 +++ /trunk/user/src/com/google/gwt/core/client/GWT.java Tue Sep 8 11:07:39 2009 @@ -202,36 +202,22 @@ sGWTBridge.log(message, e); } } + + /** + * 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); + } + } + } +} ======================================= --- /trunk/user/test/com/google/gwt/dev/jjs/InitialLoadSequence.gwt.xml Fri Jun 19 09:06:11 2009 +++ /trunk/user/test/com/google/gwt/dev/jjs/InitialLoadSequence.gwt.xml Tue Sep 8 11:07:39 2009 @@ -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> ======================================= --- /trunk/user/test/com/google/gwt/dev/jjs/test/InitialLoadSequenceTest.java Fri Jun 19 09:06:11 2009 +++ /trunk/user/test/com/google/gwt/dev/jjs/test/InitialLoadSequenceTest.java Tue Sep 8 11:07:39 2009 @@ -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()); } --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
