Add $entry magic function to guarantee correct entry and exit of GWT code from ex Patch by: bobv Review by: scottb, bruce, rjrjr, jgw git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6400 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html b/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html index 8436325..9b312e0 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html
@@ -4,7 +4,7 @@ // separate functions. var $wnd = parent; var $doc = $wnd.document; -var $moduleName, $moduleBase +var $moduleName, $moduleBase, $entry ,$stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent(a);} : null; // Lightweight metrics if ($stats) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java index 3f83f66..7d80596 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java +++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -760,6 +760,11 @@ JMethodBody body = (JMethodBody) bootStrapMethod.getBody(); JBlock block = body.getBlock(); + + // Also remember $entry, which we'll handle specially in GenerateJsAst + JMethod registerEntry = program.getIndexedMethod("Impl.registerEntry"); + program.addEntryMethod(registerEntry); + for (String mainClassName : mainClassNames) { block.addStmt(makeStatsCalls(program, mainClassName)); JDeclaredType mainType = program.getFromTypeMap(mainClassName); @@ -986,10 +991,12 @@ } } catch (ParserConfigurationException e) { throw new InternalCompilerException( - "Error reading compile report information that was just generated", e); + "Error reading compile report information that was just generated", + e); } catch (SAXException e) { throw new InternalCompilerException( - "Error reading compile report information that was just generated", e); + "Error reading compile report information that was just generated", + e); } dashboard.generateForOnePermutation(); reportArtifacts = outDir.getArtifacts();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java index a42463c..a107a0d 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -1453,23 +1453,37 @@ List<JsStatement> globalStmts) { /** * <pre> + * var $entry = Impl.registerEntry(); * function gwtOnLoad(errFn, modName, modBase){ * $moduleName = modName; * $moduleBase = modBase; * if (errFn) { * try { - * init(); + * $entry(init)(); * } catch(e) { * errFn(modName); * } * } else { - * init(); + * $entry(init)(); * } * } * </pre> */ SourceInfo sourceInfo = program.createSourceInfoSynthetic( GenerateJavaScriptAST.class, "gwtOnLoad"); + + JsName entryName = topScope.findExistingName("$entry"); + entryName.setObfuscatable(true); + JsVar entryVar = new JsVar(sourceInfo, entryName); + JsInvocation registerEntryCall = new JsInvocation(sourceInfo); + JsFunction registerEntryFunction = indexedFunctions.get("Impl.registerEntry"); + registerEntryCall.setQualifier(registerEntryFunction.getName().makeRef( + sourceInfo)); + entryVar.setInitExpr(registerEntryCall); + JsVars entryVars = new JsVars(sourceInfo); + entryVars.add(entryVar); + globalStmts.add(entryVars); + JsName gwtOnLoadName = topScope.declareName("gwtOnLoad"); gwtOnLoadName.setObfuscatable(false); JsFunction gwtOnLoad = new JsFunction(sourceInfo, topScope, @@ -1501,10 +1515,15 @@ jsIf.setElseStmt(callBlock); jsTry.setTryBlock(callBlock); for (JsFunction func : entryFuncs) { - if (func != null) { + if (func == registerEntryFunction) { + continue; + } else if (func != null) { JsInvocation call = new JsInvocation(sourceInfo); - call.setQualifier(func.getName().makeRef(sourceInfo)); - callBlock.getStatements().add(call.makeStmt()); + call.setQualifier(entryName.makeRef(sourceInfo)); + call.getArguments().add(func.getName().makeRef(sourceInfo)); + JsInvocation entryCall = new JsInvocation(sourceInfo); + entryCall.setQualifier(call); + callBlock.getStatements().add(entryCall.makeStmt()); } } JsCatch jsCatch = new JsCatch(sourceInfo, fnScope, "e");
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java index 815115f..cadf257 100644 --- a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java +++ b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
@@ -110,7 +110,7 @@ "JavaArray", "JavaMember", // GWT-defined identifiers - "$wnd", "$doc", "$moduleName", "$moduleBase", "$gwt_version", + "$wnd", "$doc", "$entry", "$moduleName", "$moduleBase", "$gwt_version", // Identifiers used by JsStackEmulator; later set to obfuscatable "$stack", "$stackDepth", "$location",
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java index 7b1f50f..47f8f0e 100644 --- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java +++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
@@ -100,7 +100,7 @@ try { Class<?> javaScriptExceptionClass = Class.forName( "com.google.gwt.core.client.JavaScriptException", true, cl); - + if (!javaScriptExceptionClass.isInstance(javaScriptException)) { // Not a JavaScriptException return null; @@ -341,11 +341,16 @@ // String entryPointTypeName = null; try { + // Set up GWT-entry code + Class<?> clazz = loadClassFromSourceName("com.google.gwt.core.client.impl.Impl"); + Method registerEntry = clazz.getMethod("registerEntry"); + registerEntry.invoke(null); + String[] entryPoints = host.getEntryPointTypeNames(); if (entryPoints.length > 0) { for (int i = 0; i < entryPoints.length; i++) { entryPointTypeName = entryPoints[i]; - Class<?> clazz = loadClassFromSourceName(entryPointTypeName); + clazz = loadClassFromSourceName(entryPointTypeName); Method onModuleLoad = null; try { onModuleLoad = clazz.getMethod("onModuleLoad");
diff --git a/user/src/com/google/gwt/core/client/impl/Impl.java b/user/src/com/google/gwt/core/client/impl/Impl.java index d93e52e..458c7dd 100644 --- a/user/src/com/google/gwt/core/client/impl/Impl.java +++ b/user/src/com/google/gwt/core/client/impl/Impl.java
@@ -16,6 +16,7 @@ package com.google.gwt.core.client.impl; import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.JavaScriptObject; /** * Private implementation class for GWT core. This API is should not be @@ -23,9 +24,40 @@ */ public final class Impl { + /** + * Used by {@link #entry0(Object, Object)} to handle reentrancy. + */ + private static int entryDepth = 0; private static int sNextHashId = 0; /** + * This method should be used whenever GWT code is entered from a JS context + * and there is no GWT code in the same module on the call stack. Examples + * include event handlers, exported methods, and module initialization. + * <p> + * The GWT compiler and hosted mode will provide a module-scoped variable, + * <code>$entry</code>, which is an alias for this method. + * <p> + * This method can be called reentrantly, which will simply delegate to the + * function. + * <p> + * The function passed to this method will be invoked via + * <code>Function.apply()</code> with the current <code>this</code> value and + * the invocation arguments passed to <code>$entry</code>. + * + * @param jsFunction a JS function to invoke, which is typically a JSNI + * reference to a static Java method + * @return the value returned when <code>jsFunction</code> is invoked, or + * <code>undefined</code> if the UncaughtExceptionHandler catches an + * exception raised by <code>jsFunction</code> + */ + public static native JavaScriptObject entry(JavaScriptObject jsFunction) /*-{ + return function() { + return @com.google.gwt.core.client.impl.Impl::entry0(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)(jsFunction, this, arguments); + }; + }-*/; + + /** * Gets an identity-based hash code on the passed-in Object by adding an * expando. This method should not be used with <code>null</code> or any * String. The former will crash and the later will produce unstable results @@ -96,6 +128,76 @@ }-*/; /** + * Implicitly called by JavaToJavaScriptCompiler.findEntryPoints(). + */ + public static native JavaScriptObject registerEntry() /*-{ + if (@com.google.gwt.core.client.GWT::isScript()()) { + // Assignment to $entry is done by the compiler + return @com.google.gwt.core.client.impl.Impl::entry(Lcom/google/gwt/core/client/JavaScriptObject;); + } else { + // But we have to do in in hosted mode + return $entry = @com.google.gwt.core.client.impl.Impl::entry(Lcom/google/gwt/core/client/JavaScriptObject;); + } + }-*/; + + private static native Object apply(Object jsFunction, Object thisObj, + Object arguments) /*-{ + if (@com.google.gwt.core.client.GWT::isScript()()) { + return jsFunction.apply(thisObj, arguments); + } else { + _ = jsFunction.apply(thisObj, arguments); + if (_ != null) { + // Wrap for hosted mode + _ = Object(_); + } + return _; + } + }-*/; + + /** + * Implements {@link #entry(JavaScriptObject)}. + */ + @SuppressWarnings("unused") + private static Object entry0(Object jsFunction, Object thisObj, + Object arguments) throws Throwable { + assert entryDepth >= 0 : "Negative entryDepth value at entry " + entryDepth; + + // We want to disable some actions in the reentrant case + boolean initialEntry = entryDepth++ == 0; + + try { + /* + * Always invoke the UCE if we have one so that the exception never + * percolates up to the browser's event loop, even in a reentrant + * situation. + */ + if (GWT.getUncaughtExceptionHandler() != null) { + /* + * This try block is guarded by the if statement so that we don't molest + * the exception object traveling up the stack unless we're capable of + * doing something useful with it. + */ + try { + return apply(jsFunction, thisObj, arguments); + } catch (Throwable t) { + GWT.getUncaughtExceptionHandler().onUncaughtException(t); + return undefined(); + } + } else { + // Can't handle any exceptions, let them percolate normally + return apply(jsFunction, thisObj, arguments); + } + } finally { + if (initialEntry) { + // TODO(bobv) FinallyCommand.flush() goes here + } + entryDepth--; + assert entryDepth >= 0 : "Negative entryDepth value at exit " + + entryDepth; + } + } + + /** * Called from JSNI. Do not change this implementation without updating: * <ul> * <li>{@link com.google.gwt.user.client.rpc.impl.SerializerBase}</li> @@ -105,4 +207,9 @@ private static int getNextHashId() { return ++sNextHashId; } + + private static native Object undefined() /*-{ + // Intentionally not returning a value + return; + }-*/; }