Revision: 6400 Author: rj...@google.com Date: Fri Oct 16 14:04:54 2009 Log: Add $entry magic function to guarantee correct entry and exit of GWT code from ex
Patch by: bobv Review by: scottb, bruce, rjrjr, jgw http://code.google.com/p/google-web-toolkit/source/detail?r=6400 Modified: /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html /trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java /trunk/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java /trunk/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java /trunk/user/src/com/google/gwt/core/client/impl/Impl.java ======================================= --- /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html Thu Oct 15 08:36:29 2009 +++ /trunk/dev/core/src/com/google/gwt/core/ext/linker/impl/hosted.html Fri Oct 16 14:04:54 2009 @@ -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) { ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java Tue Oct 13 16:57:19 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java Fri Oct 16 14:04:54 2009 @@ -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(); ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java Fri Aug 21 10:53:16 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java Fri Oct 16 14:04:54 2009 @@ -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"); ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java Tue Jul 28 09:27:08 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java Fri Oct 16 14:04:54 2009 @@ -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", ======================================= --- /trunk/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java Tue Oct 13 16:57:19 2009 +++ /trunk/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java Fri Oct 16 14:04:54 2009 @@ -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"); ======================================= --- /trunk/user/src/com/google/gwt/core/client/impl/Impl.java Mon Jul 6 14:45:31 2009 +++ /trunk/user/src/com/google/gwt/core/client/impl/Impl.java Fri Oct 16 14:04:54 2009 @@ -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,8 +24,39 @@ */ 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 @@ -94,6 +126,76 @@ public static native String getPermutationStrongName() /*-{ return $strongName; }-*/; + + /** + * 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: @@ -105,4 +207,9 @@ private static int getNextHashId() { return ++sNextHashId; } -} + + private static native Object undefined() /*-{ + // Intentionally not returning a value + return; + }-*/; +} --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---