Propagate compilation errors to our caller to make syntax errors easier to debug. bug 408412, r=shaver sr=brendan
☠☠ backed out by d194c529cba0 ☠ ☠
authorBlake Kaplan <mrbkap@gmail.com>
Mon, 04 Aug 2008 10:03:34 -0700
changeset 16357 90020c4ad44663d68be8cd2aa179c93b1718b9d7
parent 16356 438cea3432bff642d53f1550a797a219e8b93fc1
child 16358 f01bfd3595951b5e9e4a607ab56a71621e3e3cf0
child 16363 d194c529cba0de871ef7c70dd9881cd966c6d393
push idunknown
push userunknown
push dateunknown
reviewersshaver, brendan
bugs408412
milestone1.9.1a2pre
Propagate compilation errors to our caller to make syntax errors easier to debug. bug 408412, r=shaver sr=brendan
js/src/xpconnect/loader/mozJSComponentLoader.cpp
js/src/xpconnect/loader/mozJSComponentLoader.h
js/src/xpconnect/tests/unit/syntax_error.jsm
js/src/xpconnect/tests/unit/test_bug408412.js
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -632,17 +632,18 @@ mozJSComponentLoader::LoadModule(nsILoca
         NS_ADDREF(*aResult = mod->module);
         return NS_OK;
     }
 
     nsAutoPtr<ModuleEntry> entry(new ModuleEntry);
     if (!entry)
         return NS_ERROR_OUT_OF_MEMORY;
 
-    rv = GlobalForLocation(aComponentFile, &entry->global, &entry->location);
+    rv = GlobalForLocation(aComponentFile, &entry->global, &entry->location,
+                           nsnull);
     if (NS_FAILED(rv)) {
 #ifdef DEBUG_shaver
         fprintf(stderr, "GlobalForLocation failed!\n");
 #endif
         return rv;
     }
 
     nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
@@ -1055,17 +1056,18 @@ mozJSComponentLoader::WriteScript(nsIFas
     NS_ENSURE_SUCCESS(rv, rv);
 
     return flSvc->EndMuxedDocument(uri);
 }
 
 nsresult
 mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponent,
                                         JSObject **aGlobal,
-                                        char **aLocation)
+                                        char **aLocation,
+                                        jsval *exception)
 {
     nsresult rv;
 
     JSPrincipals* jsPrincipals = nsnull;
     JSCLContextHelper cx(this);
 
 #ifndef XPCONNECT_STANDALONE
     rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals);
@@ -1185,16 +1187,25 @@ mozJSComponentLoader::GlobalForLocation(
         }
     }
 
 
     if (!script || NS_FAILED(rv)) {
         // The script wasn't in the fastload cache, so compile it now.
         LOG(("Slow loading %s\n", nativePath.get()));
 
+        // If |exception| is non-null, then our caller wants to propagate any
+        // exceptions out to our caller. Ensure that the engine doesn't
+        // eagerly report the exception.
+        uint32 oldopts = 0;
+        if (exception) {
+            oldopts = JS_GetOptions(cx);
+            JS_SetOptions(cx, oldopts | JSOPTION_DONT_REPORT_UNCAUGHT);
+        }
+
 #ifdef HAVE_PR_MEMMAP
         PRInt64 fileSize;
         rv = aComponent->GetFileSize(&fileSize);
         if (NS_FAILED(rv))
             return rv;
 
         PRInt64 maxSize;
         LL_UI2L(maxSize, PR_UINT32_MAX);
@@ -1246,18 +1257,29 @@ mozJSComponentLoader::GlobalForLocation(
         rv = aComponent->OpenANSIFileDesc("r", &fileHandle);
         NS_ENSURE_SUCCESS(rv, rv);
 
         script = JS_CompileFileHandleForPrincipals(cx, global,
                                                    nativePath.get(),
                                                    fileHandle, jsPrincipals);
 
         /* JS will close the filehandle after compilation is complete. */
+#endif /* HAVE_PR_MEMMAP */
 
-#endif /* HAVE_PR_MEMMAP */
+        // Propagate the exception, if one exists. Also, don't leave the stale
+        // exception on this context.
+        // NB: The caller must stick exception into a rooted slot (probably on
+        // its context) as soon as possible to avoid GC hazards.
+        if (exception) {
+            JS_SetOptions(cx, oldopts);
+            if (!script) {
+                JS_GetPendingException(cx, exception);
+                JS_ClearPendingException(cx);
+            }
+        }
     }
 
     if (!script) {
 #ifdef DEBUG_shaver_off
         fprintf(stderr, "mJCL: script compilation of %s FAILED\n",
                 nativePath.get());
 #endif
         return NS_ERROR_FAILURE;
@@ -1469,24 +1491,37 @@ mozJSComponentLoader::ImportInto(const n
 
     ModuleEntry* mod;
     nsAutoPtr<ModuleEntry> newEntry;
     if (!mImports.Get(lfhash, &mod) && !mInProgressImports.Get(lfhash, &mod)) {
         newEntry = new ModuleEntry;
         if (!newEntry || !mInProgressImports.Put(lfhash, newEntry))
             return NS_ERROR_OUT_OF_MEMORY;
 
+        jsval exception = JSVAL_VOID;
         rv = GlobalForLocation(componentFile, &newEntry->global,
-                               &newEntry->location);
+                               &newEntry->location, &exception);
 
         mInProgressImports.Remove(lfhash);
 
         if (NS_FAILED(rv)) {
             *_retval = nsnull;
-            return NS_ERROR_FILE_NOT_FOUND;
+
+            if (!JSVAL_IS_VOID(exception)) {
+                // An exception was thrown during compilation. Propagate it
+                // out to our caller so they can report it.
+                JSContext *callercx;
+                cc->GetJSContext(&callercx);
+                JS_SetPendingException(callercx, exception);
+                cc->SetExceptionWasThrown(PR_TRUE);
+                return NS_OK;
+            }
+
+            // Something failed, but we don't know what it is, guess.
+            return rv;
         }
 
         mod = newEntry;
     }
 
     NS_ASSERTION(mod->global, "Import table contains entry with no global");
     *_retval = mod->global;
 
--- a/js/src/xpconnect/loader/mozJSComponentLoader.h
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.h
@@ -105,17 +105,18 @@ class mozJSComponentLoader : public nsIM
  protected:
     static mozJSComponentLoader* sSelf;
 
     nsresult ReallyInit();
     void UnloadModules();
 
     nsresult GlobalForLocation(nsILocalFile *aComponent,
                                JSObject **aGlobal,
-                               char **location);
+                               char **location,
+                               jsval *exception);
 
     nsresult StartFastLoad(nsIFastLoadService *flSvc);
     nsresult ReadScript(nsIFastLoadService *flSvc, const char *nativePath,
                         nsIURI *uri, JSContext *cx, JSScript **script);
     nsresult WriteScript(nsIFastLoadService *flSvc, JSScript *script,
                          nsIFile *component, const char *nativePath,
                          nsIURI *uri, JSContext *cx);
     static void CloseFastLoad(nsITimer *timer, void *closure);
new file mode 100644
--- /dev/null
+++ b/js/src/xpconnect/tests/unit/syntax_error.jsm
@@ -0,0 +1,1 @@
+bogusjs)(
new file mode 100644
--- /dev/null
+++ b/js/src/xpconnect/tests/unit/test_bug408412.js
@@ -0,0 +1,51 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *    Dave Townsend <dtownsend@oxymoronical.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+ 
+function run_test() {
+  var file = do_get_file("js/src/xpconnect/tests/unit/syntax_error.jsm");
+  var ios = Components.classes["@mozilla.org/network/io-service;1"]
+                      .getService(Components.interfaces.nsIIOService);
+  var uri = ios.newFileURI(file);
+
+  try {
+    Components.utils.import(uri.spec);
+    do_throw("Failed to report any error at all");
+  } catch (e) {
+    do_check_neq(/^SyntaxError:/(e + ''), null);
+  }
+}