Bug 987556 Part 5 Use JS::SourceBufferHolder in nsScriptLoader. r=bz
authorBen Kelly <ben@wanderview.com>
Fri, 25 Apr 2014 10:11:56 -0400
changeset 198655 338c656d6d1dc7d9e9a344a990b7f2ff0bad3562
parent 198654 3855870ea3afb59aba20f086800fcb3e610ace9d
child 198656 e9292c9b5a80e94ec970ed5099f51278bce9e61a
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs987556
milestone31.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 987556 Part 5 Use JS::SourceBufferHolder in nsScriptLoader. r=bz
content/base/src/nsScriptLoader.cpp
content/base/src/nsScriptLoader.h
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -71,22 +71,31 @@ class nsScriptLoadRequest MOZ_FINAL : pu
 public:
   nsScriptLoadRequest(nsIScriptElement* aElement,
                       uint32_t aVersion,
                       CORSMode aCORSMode)
     : mElement(aElement),
       mLoading(true),
       mIsInline(true),
       mHasSourceMapURL(false),
+      mScriptTextBuf(nullptr),
+      mScriptTextLength(0),
       mJSVersion(aVersion),
       mLineNo(1),
       mCORSMode(aCORSMode)
   {
   }
 
+  ~nsScriptLoadRequest()
+  {
+    if (mScriptTextBuf) {
+      js_free(mScriptTextBuf);
+    }
+  }
+
   NS_DECL_THREADSAFE_ISUPPORTS
 
   void FireScriptAvailable(nsresult aResult)
   {
     mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
   }
   void FireScriptEvaluated(nsresult aResult)
   {
@@ -98,17 +107,18 @@ public:
     return mElement == nullptr;
   }
 
   nsCOMPtr<nsIScriptElement> mElement;
   bool mLoading;          // Are we still waiting for a load to complete?
   bool mIsInline;         // Is the script inline or loaded?
   bool mHasSourceMapURL;  // Does the HTTP header have a source map url?
   nsString mSourceMapURL; // Holds source map url for loaded scripts
-  nsString mScriptText;   // Holds script for text loaded scripts
+  jschar* mScriptTextBuf;   // Holds script text for non-inline scripts. Don't
+  size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
   uint32_t mJSVersion;
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIPrincipal> mOriginPrincipal;
   nsAutoCString mURL;   // Keep the URI's filename alive during off thread parsing.
   int32_t mLineNo;
   const CORSMode mCORSMode;
 };
 
@@ -846,25 +856,25 @@ nsScriptLoader::AttemptAsyncScriptParse(
   }
 
   AutoPushJSContext cx(context->GetNativeContext());
   JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
 
   JS::CompileOptions options(cx);
   FillCompileOptionsForRequest(aRequest, global, &options);
 
-  if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptText.Length())) {
+  if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptTextLength)) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
     new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
 
   if (!JS::CompileOffThread(cx, options,
-                            aRequest->mScriptText.get(), aRequest->mScriptText.Length(),
+                            aRequest->mScriptTextBuf, aRequest->mScriptTextLength,
                             OffThreadScriptLoaderCallback,
                             static_cast<void*>(runnable))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   mDocument->BlockOnload();
 
   unused << runnable.forget();
@@ -879,37 +889,49 @@ nsScriptLoader::ProcessRequest(nsScriptL
 
   if (!aOffThreadToken) {
     nsresult rv = AttemptAsyncScriptParse(aRequest);
     if (rv != NS_ERROR_FAILURE)
       return rv;
   }
 
   NS_ENSURE_ARG(aRequest);
-  nsAFlatString* script;
   nsAutoString textData;
+  const jschar* scriptBuf = nullptr;
+  size_t scriptLength = 0;
+  JS::SourceBufferHolder::Ownership giveScriptOwnership =
+    JS::SourceBufferHolder::NoOwnership;
 
   nsCOMPtr<nsIDocument> doc;
 
   nsCOMPtr<nsINode> scriptElem = do_QueryInterface(aRequest->mElement);
 
   // If there's no script text, we try to get it from the element
   if (aRequest->mIsInline) {
     // XXX This is inefficient - GetText makes multiple
     // copies.
     aRequest->mElement->GetScriptText(textData);
 
-    script = &textData;
+    scriptBuf = textData.get();
+    scriptLength = textData.Length();
+    giveScriptOwnership = JS::SourceBufferHolder::NoOwnership;
   }
   else {
-    script = &aRequest->mScriptText;
+    scriptBuf = aRequest->mScriptTextBuf;
+    scriptLength = aRequest->mScriptTextLength;
+
+    giveScriptOwnership = JS::SourceBufferHolder::GiveOwnership;
+    aRequest->mScriptTextBuf = nullptr;
+    aRequest->mScriptTextLength = 0;
 
     doc = scriptElem->OwnerDoc();
   }
 
+  JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength, giveScriptOwnership);
+
   nsCOMPtr<nsIScriptElement> oldParserInsertedScript;
   uint32_t parserCreated = aRequest->mElement->GetParserCreated();
   if (parserCreated) {
     oldParserInsertedScript = mCurrentParserInsertedScript;
     mCurrentParserInsertedScript = aRequest->mElement;
   }
 
   FireScriptAvailable(NS_OK, aRequest);
@@ -932,17 +954,17 @@ nsScriptLoader::ProcessRequest(nsScriptL
   }
 
   nsresult rv = NS_OK;
   if (runScript) {
     if (doc) {
       doc->BeginEvaluatingExternalScript();
     }
     aRequest->mElement->BeginEvaluating();
-    rv = EvaluateScript(aRequest, *script, aOffThreadToken);
+    rv = EvaluateScript(aRequest, srcBuf, aOffThreadToken);
     aRequest->mElement->EndEvaluating();
     if (doc) {
       doc->EndEvaluatingExternalScript();
     }
 
     nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
                                          scriptElem,
                                          NS_LITERAL_STRING("afterscriptexecute"),
@@ -1039,17 +1061,17 @@ nsScriptLoader::FillCompileOptionsForReq
                                               /* aAllowWrapping = */ true))) {
     MOZ_ASSERT(elementVal.isObject());
     aOptions->setElement(&elementVal.toObject());
   }
 }
 
 nsresult
 nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
-                               const nsAFlatString& aScript,
+                               JS::SourceBufferHolder& aSrcBuf,
                                void** aOffThreadToken)
 {
   // We need a document to evaluate scripts.
   if (!mDocument) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIContent> scriptContent(do_QueryInterface(aRequest->mElement));
@@ -1090,17 +1112,17 @@ nsScriptLoader::EvaluateScript(nsScriptL
   context->SetProcessingScriptTag(true);
 
   // Update our current script.
   nsCOMPtr<nsIScriptElement> oldCurrent = mCurrentScript;
   mCurrentScript = aRequest->mElement;
 
   JS::CompileOptions options(entryScript.cx());
   FillCompileOptionsForRequest(aRequest, global, &options);
-  nsresult rv = nsJSUtils::EvaluateString(entryScript.cx(), aScript, global, options,
+  nsresult rv = nsJSUtils::EvaluateString(entryScript.cx(), aSrcBuf, global, options,
                                           aOffThreadToken);
 
   // Put the old script back in case it wants to do anything else.
   mCurrentScript = oldCurrent;
 
   context->SetProcessingScriptTag(oldProcessingScriptTag);
   return rv;
 }
@@ -1241,20 +1263,22 @@ DetectByteOrderMark(const unsigned char*
     break;
   }
   return !oCharset.IsEmpty();
 }
 
 /* static */ nsresult
 nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
                                uint32_t aLength, const nsAString& aHintCharset,
-                               nsIDocument* aDocument, nsString& aString)
+                               nsIDocument* aDocument,
+                               jschar*& aBufOut, size_t& aLengthOut)
 {
   if (!aLength) {
-    aString.Truncate();
+    aBufOut = nullptr;
+    aLengthOut = 0;
     return NS_OK;
   }
 
   // The encoding info precedence is as follows from high to low:
   // The BOM
   // HTTP Content-Type (if name recognized)
   // charset attribute (if name recognized)
   // The encoding of the document
@@ -1297,27 +1321,50 @@ nsScriptLoader::ConvertToUTF16(nsIChanne
 
   int32_t unicodeLength = 0;
 
   nsresult rv =
     unicodeDecoder->GetMaxLength(reinterpret_cast<const char*>(aData),
                                  aLength, &unicodeLength);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!aString.SetLength(unicodeLength, fallible_t())) {
+  aBufOut = static_cast<jschar*>(js_malloc(unicodeLength * sizeof(jschar)));
+  if (!aBufOut) {
+    aLengthOut = 0;
     return NS_ERROR_OUT_OF_MEMORY;
   }
-
-  char16_t *ustr = aString.BeginWriting();
+  aLengthOut = unicodeLength;
 
   rv = unicodeDecoder->Convert(reinterpret_cast<const char*>(aData),
-                               (int32_t *) &aLength, ustr,
+                               (int32_t *) &aLength, aBufOut,
                                &unicodeLength);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
-  aString.SetLength(unicodeLength);
+  aLengthOut = unicodeLength;
+  if (NS_FAILED(rv)) {
+    js_free(aBufOut);
+    aBufOut = nullptr;
+    aLengthOut = 0;
+  }
+  return rv;
+}
+
+/* static */ nsresult
+nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
+                               uint32_t aLength, const nsAString& aHintCharset,
+                               nsIDocument* aDocument, nsString& aString)
+{
+  jschar* bufOut = nullptr;
+  size_t lengthOut = 0;
+  nsresult rv = ConvertToUTF16(aChannel, aData, aLength, aHintCharset,
+                aDocument, bufOut, lengthOut);
+  aString.SetLength(lengthOut);
+  if (bufOut) {
+    memcpy(aString.BeginWriting(), bufOut, lengthOut * sizeof(jschar));
+    js_free(bufOut);
+  }
   return rv;
 }
 
 NS_IMETHODIMP
 nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
                                  nsISupports* aContext,
                                  nsresult aStatus,
                                  uint32_t aStringLen,
@@ -1421,17 +1468,17 @@ nsScriptLoader::PrepareLoadedRequest(nsS
       aRequest->mElement->GetScriptCharset(hintCharset);
     } else {
       nsTArray<PreloadInfo>::index_type i =
         mPreloads.IndexOf(aRequest, 0, PreloadRequestComparator());
       NS_ASSERTION(i != mPreloads.NoIndex, "Incorrect preload bookkeeping");
       hintCharset = mPreloads[i].mCharset;
     }
     rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument,
-                        aRequest->mScriptText);
+                        aRequest->mScriptTextBuf, aRequest->mScriptTextLength);
 
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // This assertion could fire errorously if we ran out of memory when
   // inserting the request in the array. However it's an unlikely case
   // so if you see this assertion it is likely something else that is
   // wrong, especially if you see it more than once.
--- a/content/base/src/nsScriptLoader.h
+++ b/content/base/src/nsScriptLoader.h
@@ -16,16 +16,20 @@
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsIDocument.h"
 #include "nsIStreamLoader.h"
 
 class nsScriptLoadRequest;
 class nsIURI;
 
+namespace JS {
+  class SourceBufferHolder;
+}
+
 //////////////////////////////////////////////////////////////
 // Script loader implementation
 //////////////////////////////////////////////////////////////
 
 class nsScriptLoader : public nsIStreamLoaderObserver
 {
   friend class nsScriptRequestProcessor;
 public:
@@ -135,16 +139,37 @@ public:
    * Convert the given buffer to a UTF-16 string.
    * @param aChannel     Channel corresponding to the data. May be null.
    * @param aData        The data to convert
    * @param aLength      Length of the data
    * @param aHintCharset Hint for the character set (e.g., from a charset
    *                     attribute). May be the empty string.
    * @param aDocument    Document which the data is loaded for. Must not be
    *                     null.
+   * @param aBufOut      [out] jschar array allocated by ConvertToUTF16 and
+   *                     containing data converted to unicode.  Caller must
+   *                     js_free() this data when no longer needed.
+   * @param aLengthOut   [out] Length of array returned in aBufOut in number
+   *                     of jschars.
+   */
+  static nsresult ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
+                                 uint32_t aLength,
+                                 const nsAString& aHintCharset,
+                                 nsIDocument* aDocument,
+                                 jschar*& aBufOut, size_t& aLengthOut);
+
+  /**
+   * Convert the given buffer to a UTF-16 string.
+   * @param aChannel     Channel corresponding to the data. May be null.
+   * @param aData        The data to convert
+   * @param aLength      Length of the data
+   * @param aHintCharset Hint for the character set (e.g., from a charset
+   *                     attribute). May be the empty string.
+   * @param aDocument    Document which the data is loaded for. Must not be
+   *                     null.
    * @param aString      [out] Data as converted to unicode
    */
   static nsresult ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
                                  uint32_t aLength,
                                  const nsAString& aHintCharset,
                                  nsIDocument* aDocument, nsString& aString);
 
   /**
@@ -272,17 +297,17 @@ private:
   nsresult AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest);
   nsresult ProcessRequest(nsScriptLoadRequest* aRequest,
                           void **aOffThreadToken = nullptr);
   void FireScriptAvailable(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   void FireScriptEvaluated(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   nsresult EvaluateScript(nsScriptLoadRequest* aRequest,
-                          const nsAFlatString& aScript,
+                          JS::SourceBufferHolder& aSrcBuf,
                           void **aOffThreadToken);
 
   already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject();
   void FillCompileOptionsForRequest(nsScriptLoadRequest *aRequest,
                                     JS::Handle<JSObject *> aScopeChain,
                                     JS::CompileOptions *aOptions);
 
   nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest,