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;
+ }-*/;
}