Bug 648125 - Allow caching JS loaded with loadSubScript, r=jst
authorMichael Wu <mwu@mozilla.com>
Sat, 09 Jul 2011 20:21:16 -0700
changeset 72536 cb5d55ec3e5d7cb08ba1695c67fd31905c14304c
parent 72535 1b07d9f68d1934237f9d718b924c14329e689d31
child 72579 cc0bc9946386b9bdf41f755fe877218db5ea136a
push id468
push usermwu@mozilla.com
push dateSun, 10 Jul 2011 03:24:17 +0000
treeherdermozilla-inbound@cb5d55ec3e5d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst
bugs648125
milestone8.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 648125 - Allow caching JS loaded with loadSubScript, r=jst
js/src/xpconnect/loader/Makefile.in
js/src/xpconnect/loader/mozJSComponentLoader.cpp
js/src/xpconnect/loader/mozJSComponentLoader.h
js/src/xpconnect/loader/mozJSLoaderUtils.cpp
js/src/xpconnect/loader/mozJSLoaderUtils.h
js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
js/src/xpconnect/loader/mozJSSubScriptLoader.h
--- a/js/src/xpconnect/loader/Makefile.in
+++ b/js/src/xpconnect/loader/Makefile.in
@@ -42,15 +42,15 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= jsloader
 LIBRARY_NAME	= jsloader_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
 LOCAL_INCLUDES += -I$(srcdir)/../src
 
-CPPSRCS		= mozJSComponentLoader.cpp mozJSSubScriptLoader.cpp
+CPPSRCS		= mozJSComponentLoader.cpp mozJSSubScriptLoader.cpp mozJSLoaderUtils.cpp
 
 EXTRA_JS_MODULES = XPCOMUtils.jsm ISO8601DateUtils.jsm
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES		+= -DJSFILE -DJS_THREADSAFE
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -53,16 +53,17 @@
 #include "nsAutoPtr.h"
 #include "nsICategoryManager.h"
 #include "nsIComponentManager.h"
 #include "mozilla/Module.h"
 #include "nsILocalFile.h"
 #include "nsIServiceManager.h"
 #include "nsISupports.h"
 #include "mozJSComponentLoader.h"
+#include "mozJSLoaderUtils.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIJSContextStack.h"
 #include "nsIXPConnect.h"
 #include "nsCRT.h"
 #include "nsMemory.h"
 #include "nsIObserverService.h"
 #include "nsIXPCScriptable.h"
 #include "nsString.h"
@@ -381,118 +382,16 @@ ReportOnCaller(JSCLContextHelper &helper
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     helper.reportErrorAfterPop(buf);
 
     return NS_OK;
 }
 
-static nsresult
-ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
-                     JSObject **scriptObj)
-{
-    *scriptObj = nsnull;
-
-    PRUint32 size;
-    nsresult rv = stream->Read32(&size);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    char *data;
-    rv = stream->ReadBytes(size, &data);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
-    NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
-
-    xdr->userdata = stream;
-    JS_XDRMemSetData(xdr, data, size);
-
-    if (!JS_XDRScriptObject(xdr, scriptObj)) {
-        rv = NS_ERROR_FAILURE;
-    }
-
-    // Update data in case ::JS_XDRScript called back into C++ code to
-    // read an XPCOM object.
-    //
-    // In that case, the serialization process must have flushed a run
-    // of counted bytes containing JS data at the point where the XPCOM
-    // object starts, after which an encoding C++ callback from the JS
-    // XDR code must have written the XPCOM object directly into the
-    // nsIObjectOutputStream.
-    //
-    // The deserialization process will XDR-decode counted bytes up to
-    // but not including the XPCOM object, then call back into C++ to
-    // read the object, then read more counted bytes and hand them off
-    // to the JSXDRState, so more JS data can be decoded.
-    //
-    // This interleaving of JS XDR data and XPCOM object data may occur
-    // several times beneath the call to ::JS_XDRScript, above.  At the
-    // end of the day, we need to free (via nsMemory) the data owned by
-    // the JSXDRState.  So we steal it back, nulling xdr's buffer so it
-    // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
-
-    uint32 length;
-    data = static_cast<char*>(JS_XDRMemGetData(xdr, &length));
-    if (data) {
-        JS_XDRMemSetData(xdr, nsnull, 0);
-    }
-    JS_XDRDestroy(xdr);
-
-    // If data is null now, it must have been freed while deserializing an
-    // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
-    if (data) {
-        nsMemory::Free(data);
-    }
-
-    return rv;
-}
-
-static nsresult
-WriteScriptToStream(JSContext *cx, JSObject *scriptObj,
-                    nsIObjectOutputStream *stream)
-{
-    JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
-    NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
-
-    xdr->userdata = stream;
-    nsresult rv = NS_OK;
-
-    if (JS_XDRScriptObject(xdr, &scriptObj)) {
-        // Get the encoded JSXDRState data and write it.  The JSXDRState owns
-        // this buffer memory and will free it beneath ::JS_XDRDestroy.
-        //
-        // If an XPCOM object needs to be written in the midst of the JS XDR
-        // encoding process, the C++ code called back from the JS engine (e.g.,
-        // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
-        // from the JSXDRState to aStream, then write the object, then return
-        // to JS XDR code with xdr reset so new JS data is encoded at the front
-        // of the xdr's data buffer.
-        //
-        // However many XPCOM objects are interleaved with JS XDR data in the
-        // stream, when control returns here from ::JS_XDRScript, we'll have
-        // one last buffer of data to write to aStream.
-
-        uint32 size;
-        const char* data = reinterpret_cast<const char*>
-                                           (JS_XDRMemGetData(xdr, &size));
-        NS_ASSERTION(data, "no decoded JSXDRState data!");
-
-        rv = stream->Write32(size);
-        if (NS_SUCCEEDED(rv)) {
-            rv = stream->WriteBytes(data, size);
-        }
-    } else {
-        rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
-    }
-
-    JS_XDRDestroy(xdr);
-    return rv;
-}
-
 mozJSComponentLoader::mozJSComponentLoader()
     : mRuntime(nsnull),
       mContext(nsnull),
       mInitialized(PR_FALSE)
 {
     NS_ASSERTION(!sSelf, "mozJSComponentLoader should be a singleton");
 
 #ifdef PR_LOGGING
@@ -844,76 +743,16 @@ class JSPrincipalsHolder
     JSPrincipalsHolder(JSContext *cx, JSPrincipals *principals)
         : mCx(cx), mPrincipals(principals) {}
     ~JSPrincipalsHolder() { JSPRINCIPALS_DROP(mCx, mPrincipals); }
  private:
     JSContext *mCx;
     JSPrincipals *mPrincipals;
 };
 
-/* static */
-nsresult
-mozJSComponentLoader::ReadScript(StartupCache* cache, nsIURI *uri,
-                                 JSContext *cx, JSObject **scriptObj)
-{
-    nsresult rv;
-    
-    nsCAutoString spec(kJSCachePrefix);
-    rv = NS_PathifyURI(uri, spec);
-    NS_ENSURE_SUCCESS(rv, rv);
-    
-    nsAutoArrayPtr<char> buf;   
-    PRUint32 len;
-    rv = cache->GetBuffer(spec.get(), getter_Transfers(buf), 
-                          &len);
-    if (NS_FAILED(rv)) {
-        return rv; // don't warn since NOT_AVAILABLE is an ok error
-    }
-
-    LOG(("Found %s in startupcache\n", spec.get()));
-    nsCOMPtr<nsIObjectInputStream> ois;
-    rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
-    NS_ENSURE_SUCCESS(rv, rv);
-    buf.forget();
-
-    return ReadScriptFromStream(cx, ois, scriptObj);
-}
-
-nsresult
-mozJSComponentLoader::WriteScript(StartupCache* cache, JSObject *scriptObj,
-                                  nsIFile *component, nsIURI *uri, JSContext *cx)
-{
-    nsresult rv;
-
-    nsCAutoString spec(kJSCachePrefix);
-    rv = NS_PathifyURI(uri, spec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    LOG(("Writing %s to startupcache\n", spec.get()));
-    nsCOMPtr<nsIObjectOutputStream> oos;
-    nsCOMPtr<nsIStorageStream> storageStream; 
-    rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
-                                                getter_AddRefs(storageStream),
-                                                true);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = WriteScriptToStream(cx, scriptObj, oos);
-    oos->Close();
-    NS_ENSURE_SUCCESS(rv, rv);
- 
-    nsAutoArrayPtr<char> buf;
-    PRUint32 len;
-    rv = NS_NewBufferFromStorageStream(storageStream, getter_Transfers(buf), 
-                                       &len);
-    NS_ENSURE_SUCCESS(rv, rv);
- 
-    rv = cache->PutBuffer(spec.get(), buf, len);
-    return rv;
-}
-
 nsresult
 mozJSComponentLoader::GlobalForLocation(nsILocalFile *aComponentFile,
                                         nsIURI *aURI,
                                         JSObject **aGlobal,
                                         char **aLocation,
                                         jsval *exception)
 {
     nsresult rv;
@@ -1010,18 +849,22 @@ mozJSComponentLoader::GlobalForLocation(
 
     // Before compiling the script, first check to see if we have it in
     // the startupcache.  Note: as a rule, startupcache errors are not fatal
     // to loading the script, since we can always slow-load.
     
     PRBool writeToCache = PR_FALSE;
     StartupCache* cache = StartupCache::GetSingleton();
 
+    nsCAutoString cachePath(kJSCachePrefix);
+    rv = NS_PathifyURI(aURI, cachePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     if (cache) {
-        rv = ReadScript(cache, aURI, cx, &scriptObj);
+        rv = ReadCachedScript(cache, cachePath, cx, &scriptObj);
         if (NS_SUCCEEDED(rv)) {
             LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
         } else {
             // This is ok, it just means the script is not yet in the
             // cache. Could mean that the cache was corrupted and got removed,
             // but either way we're going to write this out.
             writeToCache = PR_TRUE;
         }
@@ -1165,17 +1008,17 @@ mozJSComponentLoader::GlobalForLocation(
 
 #ifdef DEBUG_shaver_off
     fprintf(stderr, "mJCL: compiled JS component %s\n",
             nativePath.get());
 #endif
 
     if (writeToCache) {
         // We successfully compiled the script, so cache it. 
-        rv = WriteScript(cache, scriptObj, aComponentFile, aURI, cx);
+        rv = WriteCachedScript(cache, cachePath, cx, scriptObj);
 
         // Don't treat failure to write as fatal, since we might be working
         // with a read-only cache.
         if (NS_SUCCEEDED(rv)) {
             LOG(("Successfully wrote to cache\n"));
         } else {
             LOG(("Failed to write to cache\n"));
         }
--- a/js/src/xpconnect/loader/mozJSComponentLoader.h
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.h
@@ -101,21 +101,16 @@ class mozJSComponentLoader : public mozi
                                           nsIURI* aComponentURI);
 
     nsresult GlobalForLocation(nsILocalFile* aComponentFile,
                                nsIURI *aComponent,
                                JSObject **aGlobal,
                                char **location,
                                jsval *exception);
 
-    nsresult ReadScript(StartupCache *cache, nsIURI *uri, 
-                        JSContext *cx, JSObject **scriptObj);
-    nsresult WriteScript(StartupCache *cache, JSObject *scriptObj,
-                         nsIFile *component, nsIURI *uri, JSContext *cx);
-
     nsCOMPtr<nsIComponentManager> mCompMgr;
     nsCOMPtr<nsIJSRuntimeService> mRuntimeService;
     nsCOMPtr<nsIThreadJSContextStack> mContextStack;
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
     JSRuntime *mRuntime;
     JSContext *mContext;
 
     class ModuleEntry : public mozilla::Module
new file mode 100644
--- /dev/null
+++ b/js/src/xpconnect/loader/mozJSLoaderUtils.cpp
@@ -0,0 +1,196 @@
+/* ***** 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.org
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010-2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Michael Wu <mwu@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#if !defined(XPCONNECT_STANDALONE)
+
+#include "nsAutoPtr.h"
+#include "nsScriptLoader.h"
+
+#include "jsapi.h"
+#include "jsxdrapi.h"
+
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/scache/StartupCacheUtils.h"
+
+using namespace mozilla::scache;
+
+static nsresult
+ReadScriptFromStream(JSContext *cx, nsIObjectInputStream *stream,
+                     JSObject **scriptObj)
+{
+    *scriptObj = nsnull;
+
+    PRUint32 size;
+    nsresult rv = stream->Read32(&size);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    char *data;
+    rv = stream->ReadBytes(size, &data);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
+    NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
+
+    xdr->userdata = stream;
+    JS_XDRMemSetData(xdr, data, size);
+
+    if (!JS_XDRScriptObject(xdr, scriptObj)) {
+        rv = NS_ERROR_FAILURE;
+    }
+
+    // Update data in case ::JS_XDRScript called back into C++ code to
+    // read an XPCOM object.
+    //
+    // In that case, the serialization process must have flushed a run
+    // of counted bytes containing JS data at the point where the XPCOM
+    // object starts, after which an encoding C++ callback from the JS
+    // XDR code must have written the XPCOM object directly into the
+    // nsIObjectOutputStream.
+    //
+    // The deserialization process will XDR-decode counted bytes up to
+    // but not including the XPCOM object, then call back into C++ to
+    // read the object, then read more counted bytes and hand them off
+    // to the JSXDRState, so more JS data can be decoded.
+    //
+    // This interleaving of JS XDR data and XPCOM object data may occur
+    // several times beneath the call to ::JS_XDRScript, above.  At the
+    // end of the day, we need to free (via nsMemory) the data owned by
+    // the JSXDRState.  So we steal it back, nulling xdr's buffer so it
+    // doesn't get passed to ::JS_free by ::JS_XDRDestroy.
+
+    uint32 length;
+    data = static_cast<char*>(JS_XDRMemGetData(xdr, &length));
+    JS_XDRMemSetData(xdr, nsnull, 0);
+    JS_XDRDestroy(xdr);
+
+    // If data is null now, it must have been freed while deserializing an
+    // XPCOM object (e.g., a principal) beneath ::JS_XDRScript.
+    nsMemory::Free(data);
+
+    return rv;
+}
+
+static nsresult
+WriteScriptToStream(JSContext *cx, JSObject *scriptObj,
+                    nsIObjectOutputStream *stream)
+{
+    JSXDRState *xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
+    NS_ENSURE_TRUE(xdr, NS_ERROR_OUT_OF_MEMORY);
+
+    xdr->userdata = stream;
+    nsresult rv = NS_OK;
+
+    if (JS_XDRScriptObject(xdr, &scriptObj)) {
+        // Get the encoded JSXDRState data and write it.  The JSXDRState owns
+        // this buffer memory and will free it beneath ::JS_XDRDestroy.
+        //
+        // If an XPCOM object needs to be written in the midst of the JS XDR
+        // encoding process, the C++ code called back from the JS engine (e.g.,
+        // nsEncodeJSPrincipals in caps/src/nsJSPrincipals.cpp) will flush data
+        // from the JSXDRState to aStream, then write the object, then return
+        // to JS XDR code with xdr reset so new JS data is encoded at the front
+        // of the xdr's data buffer.
+        //
+        // However many XPCOM objects are interleaved with JS XDR data in the
+        // stream, when control returns here from ::JS_XDRScript, we'll have
+        // one last buffer of data to write to aStream.
+
+        uint32 size;
+        const char* data = reinterpret_cast<const char*>
+                                           (JS_XDRMemGetData(xdr, &size));
+        NS_ASSERTION(data, "no decoded JSXDRState data!");
+
+        rv = stream->Write32(size);
+        if (NS_SUCCEEDED(rv)) {
+            rv = stream->WriteBytes(data, size);
+        }
+    } else {
+        rv = NS_ERROR_FAILURE; // likely to be a principals serialization error
+    }
+
+    JS_XDRDestroy(xdr);
+    return rv;
+}
+
+nsresult
+ReadCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSObject **scriptObj)
+{
+    nsresult rv;
+
+    nsAutoArrayPtr<char> buf;
+    PRUint32 len;
+    rv = cache->GetBuffer(PromiseFlatCString(uri).get(), getter_Transfers(buf),
+                          &len);
+    if (NS_FAILED(rv)) {
+        return rv; // don't warn since NOT_AVAILABLE is an ok error
+    }
+
+    nsCOMPtr<nsIObjectInputStream> ois;
+    rv = NS_NewObjectInputStreamFromBuffer(buf, len, getter_AddRefs(ois));
+    NS_ENSURE_SUCCESS(rv, rv);
+    buf.forget();
+
+    return ReadScriptFromStream(cx, ois, scriptObj);
+}
+
+nsresult
+WriteCachedScript(StartupCache* cache, nsACString &uri, JSContext *cx, JSObject *scriptObj)
+{
+    nsresult rv;
+
+    nsCOMPtr<nsIObjectOutputStream> oos;
+    nsCOMPtr<nsIStorageStream> storageStream;
+    rv = NS_NewObjectOutputWrappedStorageStream(getter_AddRefs(oos),
+                                                getter_AddRefs(storageStream),
+                                                true);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = WriteScriptToStream(cx, scriptObj, oos);
+    oos->Close();
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoArrayPtr<char> buf;
+    PRUint32 len;
+    rv = NS_NewBufferFromStorageStream(storageStream, getter_Transfers(buf),
+                                       &len);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = cache->PutBuffer(PromiseFlatCString(uri).get(), buf, len);
+    return rv;
+}
+
+#endif /* XPCONNECT_STANDALONE */
new file mode 100644
--- /dev/null
+++ b/js/src/xpconnect/loader/mozJSLoaderUtils.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** 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.org.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Michael Wu <mwu@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+#ifndef mozJSLoaderUtils_h
+#define mozJSLoaderUtils_h
+
+#include "nsString.h"
+#include "jsapi.h"
+
+class nsIURI;
+namespace mozilla {
+namespace scache {
+class StartupCache;
+}
+}
+
+nsresult
+ReadCachedScript(mozilla::scache::StartupCache* cache, nsACString &uri,
+                 JSContext *cx, JSObject **scriptObj);
+
+nsresult
+WriteCachedScript(mozilla::scache::StartupCache* cache, nsACString &uri,
+                  JSContext *cx, JSObject *scriptObj);
+#endif /* mozJSLoaderUtils_h */
--- a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -36,16 +36,17 @@
  * 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 ***** */
 
 #include "mozJSSubScriptLoader.h"
+#include "mozJSLoaderUtils.h"
 
 #include "nsIServiceManager.h"
 #include "nsIXPConnect.h"
 
 #include "nsIURI.h"
 #include "nsIIOService.h"
 #include "nsIChannel.h"
 #include "nsIInputStream.h"
@@ -53,20 +54,25 @@
 #include "nsDependentString.h"
 #include "nsAutoPtr.h"
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "nsIFileURL.h"
 #include "nsScriptLoader.h"
 
 #include "jsapi.h"
+#include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jsfriendapi.h"
 
 #include "mozilla/FunctionTimer.h"
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/scache/StartupCacheUtils.h"
+
+using namespace mozilla::scache;
 
 /* load() error msgs, XXX localize? */
 #define LOAD_ERROR_NOSERVICE "Error creating IO Service."
 #define LOAD_ERROR_NOURI "Error creating URI (invalid URL scheme?)"
 #define LOAD_ERROR_NOSCHEME "Failed to get URI scheme.  This is bad."
 #define LOAD_ERROR_URI_NOT_LOCAL "Trying to load a non-local URI."
 #define LOAD_ERROR_NOSTREAM  "Error opening input stream (invalid filename?)"
 #define LOAD_ERROR_NOCONTENT "ContentLength not available (not a local URL?)"
@@ -86,16 +92,99 @@ mozJSSubScriptLoader::mozJSSubScriptLoad
 
 mozJSSubScriptLoader::~mozJSSubScriptLoader()    
 {
     /* empty */
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(mozJSSubScriptLoader, mozIJSSubScriptLoader)
 
+static nsresult
+ReportError(JSContext *cx, const char *msg)
+{
+    JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, msg)));
+    return NS_OK;
+}
+
+nsresult
+mozJSSubScriptLoader::ReadScript(nsIURI *uri, JSContext *cx, JSObject *target_obj,
+                                 jschar *charset, const char *uriStr,
+                                 nsIIOService *serv, JSObject **scriptObjp)
+{
+    nsCOMPtr<nsIChannel>     chan;
+    nsCOMPtr<nsIInputStream> instream;
+    JSPrincipals    *jsPrincipals;
+    JSErrorReporter  er;
+
+    nsresult rv;
+    // Instead of calling NS_OpenURI, we create the channel ourselves and call
+    // SetContentType, to avoid expensive MIME type lookups (bug 632490).
+    rv = NS_NewChannel(getter_AddRefs(chan), uri, serv,
+                       nsnull, nsnull, nsIRequest::LOAD_NORMAL);
+    if (NS_SUCCEEDED(rv)) {
+        chan->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
+        rv = chan->Open(getter_AddRefs(instream));
+    }
+
+    if (NS_FAILED(rv)) {
+        return ReportError(cx, LOAD_ERROR_NOSTREAM);
+    }
+    
+    PRInt32 len = -1;
+
+    rv = chan->GetContentLength(&len);
+    if (NS_FAILED(rv) || len == -1) {
+        return ReportError(cx, LOAD_ERROR_NOCONTENT);
+    }
+
+    nsCString buf;
+    rv = NS_ReadInputStreamToString(instream, buf, len);
+    if (NS_FAILED(rv))
+        return rv;
+
+    /* we can't hold onto jsPrincipals as a module var because the
+     * JSPRINCIPALS_DROP macro takes a JSContext, which we won't have in the
+     * destructor */
+    rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals);
+    if (NS_FAILED(rv) || !jsPrincipals) {
+        return ReportError(cx, LOAD_ERROR_NOPRINCIPALS);
+    }
+
+    /* set our own error reporter so we can report any bad things as catchable
+     * exceptions, including the source/line number */
+    er = JS_SetErrorReporter(cx, mozJSLoaderErrorReporter);
+
+    if (charset) {
+        nsString script;
+        rv = nsScriptLoader::ConvertToUTF16(
+                nsnull, reinterpret_cast<const PRUint8*>(buf.get()), len,
+                nsDependentString(reinterpret_cast<PRUnichar*>(charset)), nsnull, script);
+
+        if (NS_FAILED(rv)) {
+            JSPRINCIPALS_DROP(cx, jsPrincipals);
+            return ReportError(cx, LOAD_ERROR_BADCHARSET);
+        }
+
+        *scriptObjp =
+            JS_CompileUCScriptForPrincipals(cx, target_obj, jsPrincipals,
+                                            reinterpret_cast<const jschar*>(script.get()),
+                                            script.Length(), uriStr, 1);
+    } else {
+        *scriptObjp = JS_CompileScriptForPrincipals(cx, target_obj, jsPrincipals, buf.get(),
+                                                         len, uriStr, 1);
+    }
+
+    JSPRINCIPALS_DROP(cx, jsPrincipals);
+
+    /* repent for our evil deeds */
+    JS_SetErrorReporter(cx, er);
+
+    return NS_OK;
+}
+
 NS_IMETHODIMP /* args and return value are delt with using XPConnect and JSAPI */
 mozJSSubScriptLoader::LoadSubScript (const PRUnichar * aURL
                                      /* [, JSObject *target_obj] */)
 {
     /*
      * Loads a local url and evals it into the current cx
      * Synchronous (an async version would be cool too.)
      *   url: The url to load.  Must be local so that it can be loaded
@@ -222,27 +311,16 @@ mozJSSubScriptLoader::LoadSubScript (con
 #endif
 
     JSAutoEnterCompartment ac;
     if (!ac.enter(cx, target_obj))
         return NS_ERROR_UNEXPECTED;
 
     /* load up the url.  From here on, failures are reflected as ``custom''
      * js exceptions */
-    PRInt32   len = -1;
-    PRUint32  readcount = 0;  // Total amount of data read
-    PRUint32  lastReadCount = 0;  // Amount of data read in last Read() call
-    nsAutoArrayPtr<char> buf;
-    
-    JSString        *errmsg;
-    JSErrorReporter  er;
-    JSPrincipals    *jsPrincipals;
-    
-    nsCOMPtr<nsIChannel>     chan;
-    nsCOMPtr<nsIInputStream> instream;
     nsCOMPtr<nsIURI> uri;
     nsCAutoString uriStr;
     nsCAutoString scheme;
 
     JSStackFrame* frame = nsnull;
     JSScript* script = nsnull;
 
     // Figure out who's calling us
@@ -256,157 +334,84 @@ mozJSSubScriptLoader::LoadSubScript (con
 
     if (!script)
     {
         // No script means we don't know who's calling, bail.
 
         return NS_ERROR_FAILURE;
     }
 
+    StartupCache* cache = StartupCache::GetSingleton();
     nsCOMPtr<nsIIOService> serv = do_GetService(NS_IOSERVICE_CONTRACTID);
-    if (!serv)
-    {
-        errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSERVICE);
-        goto return_exception;
+    if (!serv) {
+        return ReportError(cx, LOAD_ERROR_NOSERVICE);
     }
 
     // Make sure to explicitly create the URI, since we'll need the
     // canonicalized spec.
     rv = NS_NewURI(getter_AddRefs(uri), urlbytes.ptr(), nsnull, serv);
     if (NS_FAILED(rv)) {
-        errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOURI);
-        goto return_exception;
+        return ReportError(cx, LOAD_ERROR_NOURI);
     }
 
     rv = uri->GetSpec(uriStr);
     if (NS_FAILED(rv)) {
-        errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSPEC);
-        goto return_exception;
+        return ReportError(cx, LOAD_ERROR_NOSPEC);
     }    
 
     rv = uri->GetScheme(scheme);
-    if (NS_FAILED(rv))
-    {
-        errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSCHEME);
-        goto return_exception;
+    if (NS_FAILED(rv)) {
+        return ReportError(cx, LOAD_ERROR_NOSCHEME);
     }
 
     if (!scheme.EqualsLiteral("chrome"))
     {
         // This might be a URI to a local file, though!
         nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
         nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(innerURI);
-        if (!fileURL)
-        {
-            errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_URI_NOT_LOCAL);
-            goto return_exception;
+        if (!fileURL) {
+            return ReportError(cx, LOAD_ERROR_URI_NOT_LOCAL);
         }
 
         // For file URIs prepend the filename with the filename of the
         // calling script, and " -> ". See bug 418356.
         nsCAutoString tmp(JS_GetScriptFilename(cx, script));
         tmp.AppendLiteral(" -> ");
         tmp.Append(uriStr);
 
         uriStr = tmp;
     }
 
-    // Instead of calling NS_OpenURI, we create the channel ourselves and call
-    // SetContentType, to avoid expensive MIME type lookups (bug 632490).
-    rv = NS_NewChannel(getter_AddRefs(chan), uri, serv,
-                       nsnull, nsnull, nsIRequest::LOAD_NORMAL);
-    if (NS_SUCCEEDED(rv))
-    {
-        chan->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
-        rv = chan->Open(getter_AddRefs(instream));
-    }
+    bool writeScript = false;
+    JSObject *scriptObj = nsnull;
+    JSVersion version = cx->findVersion();
+    nsCAutoString cachePath;
+    cachePath.AppendPrintf("jssubloader/%d", version);
+    NS_PathifyURI(uri, cachePath);
 
-    if (NS_FAILED(rv))
-    {
-        errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSTREAM);
-        goto return_exception;
-    }
-    
-    rv = chan->GetContentLength (&len);
-    if (NS_FAILED(rv) || len == -1)
-    {
-        errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOCONTENT);
-        goto return_exception;
-    }
-
-    buf = new char[len + 1];
-    if (!buf)
-        return NS_ERROR_OUT_OF_MEMORY;
-    buf[len] = '\0';
-    
-    do {
-        rv = instream->Read (buf + readcount, len - readcount, &lastReadCount);
-        if (NS_FAILED(rv))
-        {
-            errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_BADREAD);
-            goto return_exception;
-        }
-        readcount += lastReadCount;
-    } while (lastReadCount && readcount != PRUint32(len));
-    
-    if (static_cast<PRUint32>(len) != readcount)
-    {
-        errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_READUNDERFLOW);
-        goto return_exception;
+    if (cache)
+        rv = ReadCachedScript(cache, cachePath, cx, &scriptObj);
+    if (!scriptObj) {
+        rv = ReadScript(uri, cx, target_obj, charset, (char *)uriStr.get(), serv, &scriptObj);
+        writeScript = true;
     }
 
-    /* we can't hold onto jsPrincipals as a module var because the
-     * JSPRINCIPALS_DROP macro takes a JSContext, which we won't have in the
-     * destructor */
-    rv = mSystemPrincipal->GetJSPrincipals(cx, &jsPrincipals);
-    if (NS_FAILED(rv) || !jsPrincipals)
-    {
-        errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOPRINCIPALS);
-        goto return_exception;
-    }
-
-    /* set our own error reporter so we can report any bad things as catchable
-     * exceptions, including the source/line number */
-    er = JS_SetErrorReporter (cx, mozJSLoaderErrorReporter);
-
-    if (charset)
-    {
-        nsString script;
-        rv = nsScriptLoader::ConvertToUTF16(
-                nsnull, reinterpret_cast<PRUint8*>(buf.get()), len,
-                nsDependentString(reinterpret_cast<PRUnichar*>(charset)), nsnull, script);
+    if (NS_FAILED(rv) || !scriptObj)
+        return rv;
 
-        if (NS_FAILED(rv))
-        {
-            JSPRINCIPALS_DROP(cx, jsPrincipals);
-            errmsg = JS_NewStringCopyZ(cx, LOAD_ERROR_BADCHARSET);
-            goto return_exception;
-        }
-        ok = JS_EvaluateUCScriptForPrincipals(cx, target_obj, jsPrincipals,
-                                              reinterpret_cast<const jschar*>(script.get()),
-                                              script.Length(), uriStr.get(), 1, rval);
-    }
-    else
-    {
-        ok = JS_EvaluateScriptForPrincipals(cx, target_obj, jsPrincipals,
-                                            buf, len, uriStr.get(), 1, rval);
-    }
+    ok = false;
+    if (scriptObj)
+        ok = JS_ExecuteScriptVersion(cx, target_obj, scriptObj, rval, version);
 
-    JSPRINCIPALS_DROP(cx, jsPrincipals);
-
-    if (ok)
-    {
+    if (ok) {
         JSAutoEnterCompartment rac;
-
         if (!rac.enter(cx, result_obj) || !JS_WrapValue(cx, rval))
             return NS_ERROR_UNEXPECTED; 
     }
 
-    /* repent for our evil deeds */
-    JS_SetErrorReporter (cx, er);
+    if (cache && ok && writeScript) {
+        WriteCachedScript(cache, cachePath, cx, scriptObj);
+    }
 
     cc->SetReturnValueWasSet (ok);
     return NS_OK;
+}
 
- return_exception:
-    JS_SetPendingException (cx, STRING_TO_JSVAL(errmsg));
-    return NS_OK;
-}
--- a/js/src/xpconnect/loader/mozJSSubScriptLoader.h
+++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.h
@@ -33,34 +33,40 @@
  * 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 ***** */
 
+#include "jsapi.h"
 #include "nsCOMPtr.h"
 #include "mozIJSSubScriptLoader.h"
 #include "nsIScriptSecurityManager.h"
 
 #define MOZ_JSSUBSCRIPTLOADER_CID                    \
 { /* 829814d6-1dd2-11b2-8e08-82fa0a339b00 */         \
     0x929814d6,                                      \
     0x1dd2,                                          \
     0x11b2,                                          \
     {0x8e, 0x08, 0x82, 0xfa, 0x0a, 0x33, 0x9b, 0x00} \
 }
 
+class nsIIOService;
+
 class mozJSSubScriptLoader : public mozIJSSubScriptLoader
 {
 public:
     mozJSSubScriptLoader();
     virtual ~mozJSSubScriptLoader();
 
     // all the interface method declarations...
     NS_DECL_ISUPPORTS
     NS_DECL_MOZIJSSUBSCRIPTLOADER
 
 private:
+    nsresult ReadScript(nsIURI *uri, JSContext *cx, JSObject *target_obj,
+                        jschar *charset, const char *uriStr,
+                        nsIIOService *serv, JSObject **scriptObjp);
+
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
-    
 };