Bug 1503086 - Initialize all SourceBufferHolders with a fallible function that in all cases assumes ownership of given-ownership data. r=tcampbell, r=bz, r=mrbkap on some finicky worker code lightly touched here
authorJeff Walden <jwalden@mit.edu>
Tue, 23 Oct 2018 12:27:16 -0700
changeset 502936 c489ba287b497b77e6bb7d4b7550e0792670726c
parent 502935 2e69bd4775a5441cbf0a95a055658cbcd83a3607
child 502937 43b8810bb3eda6bc628dddda9209153c57de653b
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell, bz, mrbkap
bugs1503086
milestone65.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 1503086 - Initialize all SourceBufferHolders with a fallible function that in all cases assumes ownership of given-ownership data. r=tcampbell, r=bz, r=mrbkap on some finicky worker code lightly touched here
dom/base/nsFrameMessageManager.cpp
dom/base/nsJSUtils.cpp
dom/script/ScriptLoader.cpp
dom/workers/ScriptLoader.cpp
dom/workers/WorkerPrivate.cpp
dom/worklet/Worklet.cpp
dom/xul/XULDocument.cpp
dom/xul/nsXULContentSink.cpp
dom/xul/nsXULElement.cpp
dom/xul/nsXULElement.h
ipc/testshell/XPCShellEnvironment.cpp
js/public/SourceBufferHolder.h
js/src/builtin/Eval.cpp
js/src/builtin/TestingFunctions.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/jsapi-tests/testCompileNonSyntactic.cpp
js/src/jsapi-tests/testErrorLineOfContext.cpp
js/src/jsapi-tests/testJSEvaluateScript.cpp
js/src/jsapi-tests/testMutedErrors.cpp
js/src/jsapi-tests/testScriptObject.cpp
js/src/shell/js.cpp
js/src/vm/CompilationAndEvaluation.cpp
js/src/vm/Debugger.cpp
js/src/vm/JSFunction.cpp
js/src/wasm/AsmJS.cpp
js/xpconnect/loader/ChromeScriptLoader.cpp
js/xpconnect/loader/mozJSSubScriptLoader.cpp
js/xpconnect/src/Sandbox.cpp
mfbt/Attributes.h
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1386,20 +1386,24 @@ nsMessageManagerScriptExecutor::TryCache
       }
 
       uint32_t size = (uint32_t)std::min(written, (uint64_t)UINT32_MAX);
       ScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), size,
                                    EmptyString(), nullptr,
                                    dataStringBuf, dataStringLength);
     }
 
-    JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength,
-                                  JS::SourceBufferHolder::GiveOwnership);
+    if (!dataStringBuf || dataStringLength == 0) {
+      return;
+    }
 
-    if (!dataStringBuf || dataStringLength == 0) {
+    JS::UniqueTwoByteChars srcChars(dataStringBuf);
+
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, std::move(srcChars), dataStringLength)) {
       return;
     }
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(url.get(), 1);
     options.setNoScriptRval(true);
 
     if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -91,19 +91,26 @@ nsJSUtils::CompileFunction(AutoJSAPI& js
                 js::IsObjectInContextCompartment(aScopeChain[0], cx));
 
   // Do the junk Gecko is supposed to do before calling into JSAPI.
   for (size_t i = 0; i < aScopeChain.length(); ++i) {
     JS::ExposeObjectToActiveJS(aScopeChain[i]);
   }
 
   // Compile.
+  const nsPromiseFlatString& flatBody = PromiseFlatString(aBody);
+
+  JS::SourceBufferHolder source;
+  if (!source.init(cx, flatBody.get(), flatBody.Length(),
+                   JS::SourceBufferHolder::NoOwnership))
+  {
+    return NS_ERROR_FAILURE;
+  }
+
   JS::Rooted<JSFunction*> fun(cx);
-  JS::SourceBufferHolder source(PromiseFlatString(aBody).get(), aBody.Length(),
-                                JS::SourceBufferHolder::NoOwnership);
   if (!JS::CompileFunction(cx, aScopeChain, aOptions,
                            PromiseFlatCString(aName).get(),
                            aArgCount, aArgArray,
                            source, &fun))
   {
     return NS_ERROR_FAILURE;
   }
 
@@ -265,18 +272,25 @@ nsJSUtils::ExecutionContext::CompileAndE
                                             const nsAString& aScript)
 {
   MOZ_ASSERT(!mEncodeBytecode, "A JSScript is needed for calling FinishIncrementalEncoding");
   if (mSkip) {
     return mRv;
   }
 
   const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
-  JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(),
-                                JS::SourceBufferHolder::NoOwnership);
+  JS::SourceBufferHolder srcBuf;
+  if (!srcBuf.init(mCx, flatScript.get(), flatScript.Length(),
+                   JS::SourceBufferHolder::NoOwnership))
+  {
+    mSkip = true;
+    mRv = EvaluationExceptionToNSResult(mCx);
+    return mRv;
+  }
+
   JS::Rooted<JSScript*> script(mCx);
   return CompileAndExec(aCompileOptions, srcBuf, &script);
 }
 
 nsresult
 nsJSUtils::ExecutionContext::DecodeAndExec(JS::CompileOptions& aCompileOptions,
                                            mozilla::Vector<uint8_t>& aBytecodeBuf,
                                            size_t aBytecodeIndex)
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -1946,22 +1946,38 @@ ScriptLoader::GetScriptSource(JSContext*
 
     size_t nbytes = inlineData.Length() * sizeof(char16_t);
     JS::UniqueTwoByteChars chars(static_cast<char16_t*>(JS_malloc(aCx, nbytes)));
     if (!chars) {
       return Nothing();
     }
 
     memcpy(chars.get(), inlineData.get(), nbytes);
-    return Some(SourceBufferHolder(std::move(chars), inlineData.Length()));
+
+    SourceBufferHolder srcBuf;
+    if (!srcBuf.init(aCx, std::move(chars), inlineData.Length())) {
+      return Nothing();
+    }
+
+    return Some(SourceBufferHolder(std::move(srcBuf)));
   }
 
   size_t length = aRequest->ScriptText().length();
   JS::UniqueTwoByteChars chars(aRequest->ScriptText().extractOrCopyRawBuffer());
-  return Some(SourceBufferHolder(std::move(chars), length));
+  if (!chars) {
+    JS_ReportOutOfMemory(aCx);
+    return Nothing();
+  }
+
+  SourceBufferHolder srcBuf;
+  if (!srcBuf.init(aCx, std::move(chars), length)) {
+    return Nothing();
+  }
+
+  return Some(SourceBufferHolder(std::move(srcBuf)));
 }
 
 nsresult
 ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest)
 {
   LOG(("ScriptLoadRequest (%p): Process request", aRequest));
 
   NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ScriptLoader.h"
 
+#include <algorithm>
+
 #include "nsIChannel.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocShell.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIInputStreamPump.h"
 #include "nsIIOService.h"
@@ -2119,21 +2121,29 @@ ScriptExecutorRunnable::WorkerRun(JSCont
 
     JS::CompileOptions options(aCx);
     options.setFileAndLine(filename.get(), 1)
            .setNoScriptRval(true);
 
     MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
     options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
 
-    JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf,
-                                  loadInfo.mScriptTextLength,
-                                  JS::SourceBufferHolder::GiveOwnership);
-    loadInfo.mScriptTextBuf = nullptr;
-    loadInfo.mScriptTextLength = 0;
+    // Pass ownership of the data, first to local variables, then to the
+    // UniqueTwoByteChars moved into the |init| function.
+    size_t dataLength = 0;
+    char16_t* data = nullptr;
+
+    std::swap(dataLength, loadInfo.mScriptTextLength);
+    std::swap(data, loadInfo.mScriptTextBuf);
+
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(aCx, JS::UniqueTwoByteChars(data), dataLength)) {
+      mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
+      return true;
+    }
 
     // Our ErrorResult still shouldn't be a failure.
     MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
     JS::Rooted<JS::Value> unused(aCx);
     if (!JS::Evaluate(aCx, options, srcBuf, &unused)) {
       mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
       return true;
     }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4897,22 +4897,25 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
       uint32_t lineNo = 0, dummyColumn = 0;
       info->mHandler->GetLocation(&filename, &lineNo, &dummyColumn);
 
       JS::CompileOptions options(aes.cx());
       options.setFileAndLine(filename, lineNo).setNoScriptRval(true);
 
       JS::Rooted<JS::Value> unused(aes.cx());
 
-      JS::SourceBufferHolder srcBuf(script.BeginReading(), script.Length(),
-                                    JS::SourceBufferHolder::NoOwnership);
-      if (!JS::Evaluate(aes.cx(), options, srcBuf, &unused) &&
-          !JS_IsExceptionPending(aCx)) {
-        retval = false;
-        break;
+      JS::SourceBufferHolder srcBuf;
+      if (!srcBuf.init(aes.cx(), script.BeginReading(), script.Length(),
+                       JS::SourceBufferHolder::NoOwnership) ||
+          !JS::Evaluate(aes.cx(), options, srcBuf, &unused))
+      {
+        if (!JS_IsExceptionPending(aCx)) {
+          retval = false;
+          break;
+        }
       }
     } else {
       ErrorResult rv;
       JS::Rooted<JS::Value> ignoredVal(aCx);
       callback->Call(GlobalScope(), info->mHandler->GetArgs(), &ignoredVal, rv,
                      reason);
       if (rv.IsUncatchableException()) {
         rv.SuppressException();
--- a/dom/worklet/Worklet.cpp
+++ b/dom/worklet/Worklet.cpp
@@ -396,20 +396,20 @@ ExecutionRunnable::RunOnWorkletThread()
   JS::CompileOptions compileOptions(cx);
   compileOptions.setIntroductionType("Worklet");
   compileOptions.setFileAndLine(url.get(), 0);
   compileOptions.setIsRunOnce(true);
   compileOptions.setNoScriptRval(true);
 
   JSAutoRealm ar(cx, globalObj);
 
-  JS::SourceBufferHolder buffer(mScriptBuffer.release(), mScriptLength,
-                                JS::SourceBufferHolder::GiveOwnership);
   JS::Rooted<JS::Value> unused(cx);
-  if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
+  JS::SourceBufferHolder buffer;
+  if (!buffer.init(cx, std::move(mScriptBuffer), mScriptLength) ||
+      !JS::Evaluate(cx, compileOptions, buffer, &unused)) {
     ErrorResult error;
     error.MightThrowJSException();
     error.StealExceptionFromJSContext(cx);
     mResult = error.StealNSResult();
     return;
   }
 
   // All done.
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -18,16 +18,18 @@
      which may be lazily created during frame construction, the
      document observer methods will never be called because we'll be
      adding the XUL nodes into the content model "quietly".
 
 */
 
 #include "mozilla/ArrayUtils.h"
 
+#include <algorithm>
+
 #include "XULDocument.h"
 
 #include "nsError.h"
 #include "nsIBoxObject.h"
 #include "nsIChromeRegistry.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIContentViewer.h"
@@ -1278,32 +1280,29 @@ XULDocument::OnStreamComplete(nsIStreamL
                                             !mOffThreadCompileStringBuf),
                    "XULDocument can't load multiple scripts at once");
 
         rv = ScriptLoader::ConvertToUTF16(channel, string, stringLen,
                                           EmptyString(), this,
                                           mOffThreadCompileStringBuf,
                                           mOffThreadCompileStringLength);
         if (NS_SUCCEEDED(rv)) {
-            // Attempt to give ownership of the buffer to the JS engine.  If
-            // we hit offthread compilation, however, we will have to take it
-            // back below in order to keep the memory alive until compilation
-            // completes.
-            JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf,
-                                          mOffThreadCompileStringLength,
-                                          JS::SourceBufferHolder::GiveOwnership);
-            mOffThreadCompileStringBuf = nullptr;
-            mOffThreadCompileStringLength = 0;
+            // Pass ownership of the buffer, carefully emptying the existing
+            // fields in the process.  Note that the |Compile| function called
+            // below always takes ownership of the buffer.
+            char16_t* units = nullptr;
+            size_t unitsLength = 0;
 
-            rv = mCurrentScriptProto->Compile(srcBuf, uri, 1, this, this);
+            std::swap(units, mOffThreadCompileStringBuf);
+            std::swap(unitsLength, mOffThreadCompileStringLength);
+
+            rv = mCurrentScriptProto->Compile(units, unitsLength,
+                                              JS::SourceBufferHolder::GiveOwnership,
+                                              uri, 1, this, this);
             if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
-                // We will be notified via OnOffThreadCompileComplete when the
-                // compile finishes. The JS engine has taken ownership of the
-                // source buffer.
-                MOZ_RELEASE_ASSERT(!srcBuf.ownsChars());
                 mOffThreadCompiling = true;
                 BlockOnload();
                 return NS_OK;
             }
         }
     }
 
     return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
--- a/dom/xul/nsXULContentSink.cpp
+++ b/dom/xul/nsXULContentSink.cpp
@@ -500,19 +500,21 @@ XULContentSinkImpl::HandleEndElement(con
         nsXULPrototypeScript* script =
             static_cast<nsXULPrototypeScript*>(node.get());
 
         // If given a src= attribute, we must ignore script tag content.
         if (!script->mSrcURI && !script->HasScriptObject()) {
             nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
 
             script->mOutOfLine = false;
-            if (doc)
-                script->Compile(mText, mTextLength, mDocumentURL,
+            if (doc) {
+                script->Compile(mText, mTextLength,
+                                JS::SourceBufferHolder::NoOwnership, mDocumentURL,
                                 script->mLineNo, doc);
+            }
         }
 
         FlushText(false);
     }
     break;
 
     default:
         NS_ERROR("didn't expect that");
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -2273,28 +2273,41 @@ OffThreadScriptReceiverCallback(JS::OffT
     // may be invoked off the main thread.
     nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(aCallbackData);
     RefPtr<NotifyOffThreadScriptCompletedRunnable> notify =
         new NotifyOffThreadScriptCompletedRunnable(aReceiver, aToken);
     NS_DispatchToMainThread(notify);
 }
 
 nsresult
-nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf,
+nsXULPrototypeScript::Compile(const char16_t* aText,
+                              size_t aTextLength,
+                              JS::SourceBufferHolder::Ownership aOwnership,
                               nsIURI* aURI, uint32_t aLineNo,
                               nsIDocument* aDocument,
                               nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
 {
     // We'll compile the script in the compilation scope.
     AutoJSAPI jsapi;
     if (!jsapi.Init(xpc::CompilationScope())) {
+        if (aOwnership == JS::SourceBufferHolder::GiveOwnership) {
+            // In this early-exit case -- before the |srcBuf.init| call will
+            // own |aText| -- we must relinquish ownership manually.
+            js_free(const_cast<char16_t*>(aText));
+        }
+
         return NS_ERROR_UNEXPECTED;
     }
     JSContext* cx = jsapi.cx();
 
+    JS::SourceBufferHolder srcBuf;
+    if (NS_WARN_IF(!srcBuf.init(cx, aText, aTextLength, aOwnership))) {
+        return NS_ERROR_FAILURE;
+    }
+
     nsAutoCString urlspec;
     nsresult rv = aURI->GetSpec(urlspec);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Ok, compile it to create a prototype script object!
     JS::CompileOptions options(cx);
@@ -2304,46 +2317,32 @@ nsXULPrototypeScript::Compile(JS::Source
     // Function.prototype.toSource(). If it's out of line, we retrieve the
     // source from the files on demand.
     options.setSourceIsLazy(mOutOfLine);
     JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
     if (scope) {
       JS::ExposeObjectToActiveJS(scope);
     }
 
-    if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) {
-        if (!JS::CompileOffThread(cx, options,
-                                  aSrcBuf,
+    if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aTextLength)) {
+        if (!JS::CompileOffThread(cx, options, srcBuf,
                                   OffThreadScriptReceiverCallback,
                                   static_cast<void*>(aOffThreadReceiver))) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
         NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver);
     } else {
         JS::Rooted<JSScript*> script(cx);
-        if (!JS::Compile(cx, options, aSrcBuf, &script))
+        if (!JS::Compile(cx, options, srcBuf, &script))
             return NS_ERROR_OUT_OF_MEMORY;
         Set(script);
     }
     return NS_OK;
 }
 
-nsresult
-nsXULPrototypeScript::Compile(const char16_t* aText,
-                              int32_t aTextLength,
-                              nsIURI* aURI,
-                              uint32_t aLineNo,
-                              nsIDocument* aDocument,
-                              nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */)
-{
-  JS::SourceBufferHolder srcBuf(aText, aTextLength,
-                                JS::SourceBufferHolder::NoOwnership);
-  return Compile(srcBuf, aURI, aLineNo, aDocument, aOffThreadReceiver);
-}
-
 void
 nsXULPrototypeScript::UnlinkJSObjects()
 {
     if (mScriptObject) {
         mScriptObject = nullptr;
         mozilla::DropJSObjects(this);
     }
 }
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -7,16 +7,17 @@
 
   The base XUL element class and associates.
 
 */
 
 #ifndef nsXULElement_h__
 #define nsXULElement_h__
 
+#include "js/SourceBufferHolder.h"
 #include "js/TracingAPI.h"
 #include "mozilla/Attributes.h"
 #include "nsIServiceManager.h"
 #include "nsAtom.h"
 #include "mozilla/dom/NodeInfo.h"
 #include "nsIControllers.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIURI.h"
@@ -47,20 +48,16 @@ class StyleRule;
 } // namespace css
 namespace dom {
 class BoxObject;
 class HTMLIFrameElement;
 enum class CallerType : uint32_t;
 } // namespace dom
 } // namespace mozilla
 
-namespace JS {
-class SourceBufferHolder;
-} // namespace JS
-
 ////////////////////////////////////////////////////////////////////////
 
 #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING
 #define XUL_PROTOTYPE_ATTRIBUTE_METER(counter) (nsXULPrototypeAttribute::counter++)
 #else
 #define XUL_PROTOTYPE_ATTRIBUTE_METER(counter) ((void) 0)
 #endif
 
@@ -213,22 +210,18 @@ public:
                                 nsXULPrototypeDocument* aProtoDoc);
     virtual nsresult Deserialize(nsIObjectInputStream* aStream,
                                  nsXULPrototypeDocument* aProtoDoc,
                                  nsIURI* aDocumentURI,
                                  const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override;
     nsresult DeserializeOutOfLine(nsIObjectInputStream* aInput,
                                   nsXULPrototypeDocument* aProtoDoc);
 
-    nsresult Compile(JS::SourceBufferHolder& aSrcBuf,
-                     nsIURI* aURI, uint32_t aLineNo,
-                     nsIDocument* aDocument,
-                     nsIOffThreadScriptReceiver *aOffThreadReceiver = nullptr);
-
-    nsresult Compile(const char16_t* aText, int32_t aTextLength,
+    nsresult Compile(const char16_t* aText, size_t aTextLength,
+                     JS::SourceBufferHolder::Ownership aOwnership,
                      nsIURI* aURI, uint32_t aLineNo,
                      nsIDocument* aDocument,
                      nsIOffThreadScriptReceiver *aOffThreadReceiver = nullptr);
 
     void UnlinkJSObjects();
 
     void Set(JSScript* aObject);
 
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -487,19 +487,24 @@ XPCShellEnvironment::EvaluateString(cons
 {
   AutoEntryScript aes(GetGlobalObject(),
                       "ipc XPCShellEnvironment::EvaluateString");
   JSContext* cx = aes.cx();
 
   JS::CompileOptions options(cx);
   options.setFileAndLine("typein", 0);
 
+  JS::SourceBufferHolder srcBuf;
+  if (!srcBuf.init(cx, aString.get(), aString.Length(),
+                   JS::SourceBufferHolder::NoOwnership))
+  {
+    return false;
+  }
+
   JS::Rooted<JSScript*> script(cx);
-  JS::SourceBufferHolder srcBuf(aString.get(), aString.Length(),
-                                JS::SourceBufferHolder::NoOwnership);
   if (!JS::Compile(cx, options, srcBuf, &script))
   {
      return false;
   }
 
   if (aResult) {
       aResult->Truncate();
   }
--- a/js/public/SourceBufferHolder.h
+++ b/js/public/SourceBufferHolder.h
@@ -6,129 +6,184 @@
 /*
  * SourceBufferHolder groups buffer and length values and provides a way to
  * optionally pass ownership of the buffer to the JS engine without copying.
  *
  * Rules for use:
  *
  *  1) The data array must be allocated with js_malloc() or js_realloc() if
  *     ownership is being granted to the SourceBufferHolder.
- *  2) If ownership is not given to the SourceBufferHolder, then the memory
- *     must be kept alive until the JS compilation is complete.
+ *  2) If ownership is not given to SourceBufferHolder::init, then the memory
+ *     must be kept alive until the JS compilation is complete.  If ownership
+ *     is given to SourceBufferHolder::init, it is given *even if that function
+ *     fails*.
  *  3) Any code calling SourceBufferHolder::take() must guarantee to keep the
  *     memory alive until JS compilation completes.  Normally only the JS
  *     engine should be calling take().
  *
  * Example use:
  *
  *    size_t length = 512;
  *    char16_t* chars = js_pod_malloc<char16_t>(length);
- *    JS::SourceBufferHolder srcBuf(chars, length, JS::SourceBufferHolder::GiveOwnership);
- *    JS::Compile(cx, options, srcBuf);
+ *    if (!chars) {
+ *        JS_ReportOutOfMemory(cx);
+ *        return false;
+ *    }
+ *    JS::SourceBufferHolder srcBuf;
+ *    if (!srcBuf.init(cx, chars, length, JS::SourceBufferHolder::GiveOwnership) ||
+ *        !JS::Compile(cx, options, srcBuf))
+ *    {
+ *        return false;
+ *    }
  */
 
 #ifndef js_SourceBufferHolder_h
 #define js_SourceBufferHolder_h
 
 #include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/Attributes.h" // MOZ_IS_CLASS_INIT, MOZ_MUST_USE
+#include "mozilla/Likely.h" // MOZ_UNLIKELY
 
 #include <stddef.h> // size_t
+#include <stdint.h> // UINT32_MAX
 
 #include "js/Utility.h" // JS::UniqueTwoByteChars
 
 namespace JS {
 
+namespace detail {
+
+MOZ_COLD extern JS_PUBLIC_API(void)
+ReportSourceTooLong(JSContext* cx);
+
+}
+
 class SourceBufferHolder final
 {
   private:
-    const char16_t* data_;
-    size_t length_;
-    bool ownsChars_;
-
-  private:
-    void fixEmptyBuffer() {
-        // Ensure that null buffers properly return an unowned, empty,
-        // null-terminated string.
-        static const char16_t NullChar_ = 0;
-        if (!data_) {
-            data_ = &NullChar_;
-            length_ = 0;
-            ownsChars_ = false;
-        }
-    }
+    const char16_t* data_ = nullptr;
+    size_t length_ = 0;
+    bool ownsChars_ = false;
 
   public:
-    enum Ownership {
-      NoOwnership,
-      GiveOwnership
-    };
+    /**
+     * Construct a SourceBufferHolder.  It must be initialized using |init()|
+     * before it can be used as compilation source text.
+     */
+    SourceBufferHolder() = default;
 
-    SourceBufferHolder(const char16_t* data, size_t dataLength, Ownership ownership)
-      : data_(data),
-        length_(dataLength),
-        ownsChars_(ownership == GiveOwnership)
-    {
-        fixEmptyBuffer();
-    }
-
-    SourceBufferHolder(UniqueTwoByteChars&& data, size_t dataLength)
-      : data_(data.release()),
-        length_(dataLength),
-        ownsChars_(true)
-    {
-        fixEmptyBuffer();
-    }
-
+    /**
+     * Construct a SourceBufferHolder from contents extracted from |other|.
+     * This SourceBufferHolder will then act exactly as |other| would have
+     * acted, had it not been passed to this function.  |other| will return to
+     * its default-constructed state and must have |init()| called on it to use
+     * it.
+     */
     SourceBufferHolder(SourceBufferHolder&& other)
       : data_(other.data_),
         length_(other.length_),
         ownsChars_(other.ownsChars_)
     {
         other.data_ = nullptr;
         other.length_ = 0;
         other.ownsChars_ = false;
     }
 
     ~SourceBufferHolder() {
         if (ownsChars_) {
             js_free(const_cast<char16_t*>(data_));
         }
     }
 
+    enum Ownership {
+        NoOwnership,
+        GiveOwnership
+    };
+
+    /**
+     * Initialize this with data.
+     *
+     * If |ownership == GiveOwnership|, *this function* takes ownership of
+     * |data|, *even if* this function fails.  You MUST NOT free |data| after
+     * this function takes ownership of it, REGARDLESS whether this function
+     * succeeds or fails.  This single-owner-friendly approach reduces risk of
+     * leaks on failure.
+     *
+     * |data| may be null if |dataLength == 0|; if so, this will silently be
+     * initialized using non-null, unowned data.
+     */
+    MOZ_IS_CLASS_INIT MOZ_MUST_USE bool
+    init(JSContext* cx, const char16_t* data, size_t dataLength, Ownership ownership) {
+        MOZ_ASSERT_IF(data == nullptr, dataLength == 0);
+
+        static const char16_t nonNullData[] = u"";
+
+        // Initialize all fields *before* checking length.  This ensures that
+        // if data ownership is being passed to SourceBufferHolder, a failure
+        // of length-checking will result in this class freeing the data on
+        // destruction.
+        if (data) {
+            data_ = data;
+            length_ = static_cast<uint32_t>(dataLength);
+            ownsChars_ = ownership == GiveOwnership;
+        } else {
+            data_ = nonNullData;
+            length_ = 0;
+            ownsChars_ = false;
+        }
+
+        // IMPLEMENTATION DETAIL, DO NOT RELY ON: This limit is used so we can
+        // store offsets in JSScripts as uint32_t.  It could be lifted fairly
+        // easily if desired as the compiler uses size_t internally.
+        if (MOZ_UNLIKELY(dataLength > UINT32_MAX)) {
+            detail::ReportSourceTooLong(cx);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Initialize this from data with ownership transferred from the caller.
+     */
+    MOZ_IS_CLASS_INIT MOZ_MUST_USE
+    bool init(JSContext* cx, UniqueTwoByteChars data, size_t dataLength) {
+        return init(cx, data.release(), dataLength, GiveOwnership);
+    }
+
     /** Access the underlying source buffer without affecting ownership. */
     const char16_t* get() const {
         return data_;
     }
 
     /** Length of the source buffer in char16_t code units (not bytes). */
-    size_t length() const {
+    uint32_t length() const {
         return length_;
     }
 
     /**
      * Returns true if the SourceBufferHolder owns the buffer and will free it
      * upon destruction.  If true, it is legal to call take().
      */
     bool ownsChars() const {
         return ownsChars_;
     }
 
     /**
      * Retrieve and take ownership of the underlying data buffer.  The caller
      * is now responsible for calling js_free() on the returned value, *but
      * only after JS script compilation has completed*.
      *
-     * After the buffer has been taken the SourceBufferHolder functions as if
-     * it had been constructed on an unowned buffer;  get() and length() still
-     * work.  In order for this to be safe the taken buffer must be kept alive
-     * until after JS script compilation completes as noted above.
+     * After the buffer has been taken, this will continue to refer to the same
+     * data -- it just won't own the data.  get() and length() will return the
+     * same values.  The taken buffer must be kept alive until after JS script
+     * compilation completes, as noted above, for this to be safe.
      *
-     * It's the caller's responsibility to check ownsChars() before taking the
-     * buffer.  Taking and then free'ing an unowned buffer will have dire
-     * consequences.
+     * The caller must check ownsChars() before attempting to take the buffer.
+     * Taking and then free'ing an unowned buffer will have dire consequences.
      */
     char16_t* take() {
         MOZ_ASSERT(ownsChars_);
         ownsChars_ = false;
         return const_cast<char16_t*>(data_);
     }
 
   private:
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -312,21 +312,26 @@ EvalKernel(JSContext* cx, HandleValue v,
             options.setIntroductionType("eval");
         }
 
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr)) {
             return false;
         }
 
+        SourceBufferHolder srcBuf;
+
         const char16_t* chars = linearChars.twoByteRange().begin().get();
         SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
-        SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
+        if (!srcBuf.init(cx, chars, linearStr->length(), ownership)) {
+            return false;
+        }
+
         JSScript* compiled = frontend::CompileEvalScript(cx, env, enclosing, options, srcBuf);
         if (!compiled) {
             return false;
         }
 
         esg.setNewScript(compiled);
     }
 
@@ -396,21 +401,26 @@ js::DirectEvalStringFromIon(JSContext* c
             options.setIntroductionType("eval");
         }
 
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr)) {
             return false;
         }
 
+        SourceBufferHolder srcBuf;
+
         const char16_t* chars = linearChars.twoByteRange().begin().get();
         SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
-        SourceBufferHolder srcBuf(chars, linearStr->length(), ownership);
+        if (!srcBuf.init(cx, chars, linearStr->length(), ownership)) {
+            return false;
+        }
+
         JSScript* compiled = frontend::CompileEvalScript(cx, env, enclosing, options, srcBuf);
         if (!compiled) {
             return false;
         }
 
         esg.setNewScript(compiled);
     }
 
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -4028,17 +4028,21 @@ EvalReturningScope(JSContext* cx, unsign
     unsigned lineno;
 
     JS::DescribeScriptedCaller(cx, &filename, &lineno);
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(filename.get(), lineno);
     options.setNoScriptRval(true);
 
-    JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, src, srclen, JS::SourceBufferHolder::NoOwnership)) {
+        return false;
+    }
+
     RootedScript script(cx);
     if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
         return false;
     }
 
     if (global) {
         global = CheckedUnwrap(global);
         if (!global) {
@@ -4129,17 +4133,21 @@ ShellCloneAndExecuteScript(JSContext* cx
     unsigned lineno;
 
     JS::DescribeScriptedCaller(cx, &filename, &lineno);
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(filename.get(), lineno);
     options.setNoScriptRval(true);
 
-    JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, src, srclen, JS::SourceBufferHolder::NoOwnership)) {
+        return false;
+    }
+
     RootedScript script(cx);
     if (!JS::Compile(cx, options, srcBuf, &script)) {
         return false;
     }
 
     global = CheckedUnwrap(global);
     if (!global) {
         JS_ReportErrorASCII(cx, "Permission denied to access global");
@@ -5553,19 +5561,23 @@ js::TestingFunctionArgumentToScript(JSCo
         }
         size_t len = GetLinearStringLength(linearStr);
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr)) {
             return nullptr;
         }
         const char16_t* chars = linearChars.twoByteRange().begin().get();
 
+        SourceBufferHolder source;
+        if (!source.init(cx, chars, len, SourceBufferHolder::NoOwnership)) {
+            return nullptr;
+        }
+
         RootedScript script(cx);
         CompileOptions options(cx);
-        SourceBufferHolder source(chars, len, SourceBufferHolder::NoOwnership);
         if (!JS::Compile(cx, options, source, &script)) {
             return nullptr;
         }
         return script;
     }
 
     RootedFunction fun(cx, JS_ValueToFunction(cx, v));
     if (!fun) {
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -107,17 +107,16 @@ class MOZ_STACK_CLASS BytecodeCompiler
     }
 
     void assertSourceParserAndScriptCreated() const {
         assertSourceAndParserCreated();
         MOZ_ASSERT(script != nullptr);
     }
 
     JSScript* compileScript(HandleObject environment, SharedContext* sc);
-    bool checkLength();
     bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
     bool canLazilyParse();
     bool createParser(ParseGoal goal);
     bool createSourceAndParser(ParseGoal goal,
                                const Maybe<uint32_t>& parameterListEnd = Nothing());
 
     // This assumes the created script's offsets in the source used to parse it
     // are the same as are used to compute its Function.prototype.toString()
@@ -198,38 +197,18 @@ BytecodeCompiler::BytecodeCompiler(JSCon
     scriptSource(nullptr),
     directives(options.strictOption),
     script(cx)
 {
     MOZ_ASSERT(sourceBuffer.get());
 }
 
 bool
-BytecodeCompiler::checkLength()
-{
-    // Note this limit is simply so we can store sourceStart and sourceEnd in
-    // JSScript as 32-bits. It could be lifted fairly easily, since the compiler
-    // is using size_t internally already.
-    if (sourceBuffer.length() > UINT32_MAX) {
-        if (!cx->helperThread()) {
-            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
-                                      JSMSG_SOURCE_TOO_LONG);
-        }
-        return false;
-    }
-    return true;
-}
-
-bool
 BytecodeCompiler::createScriptSource(const Maybe<uint32_t>& parameterListEnd)
 {
-    if (!checkLength()) {
-        return false;
-    }
-
     sourceObject = CreateScriptSourceObject(cx, options, parameterListEnd);
     if (!sourceObject) {
         return false;
     }
 
     scriptSource = sourceObject->source();
 
     if (!cx->realm()->behaviors().discardSource()) {
--- a/js/src/jsapi-tests/testCompileNonSyntactic.cpp
+++ b/js/src/jsapi-tests/testCompileNonSyntactic.cpp
@@ -69,53 +69,60 @@ testCompile(bool nonSyntactic)
 
     constexpr size_t length = sizeof(src) - 1;
     static_assert(sizeof(src_16) / sizeof(*src_16) - 1 == length,
                   "Source buffers must be same length");
 
     JS::CompileOptions options(cx);
     options.setNonSyntacticScope(nonSyntactic);
 
-    JS::RootedScript script(cx);
+    JS::SourceBufferHolder buf;
+    CHECK(buf.init(cx, src_16, length, JS::SourceBufferHolder::NoOwnership));
 
-    JS::SourceBufferHolder buf(src_16, length, JS::SourceBufferHolder::NoOwnership);
+    JS::RootedScript script(cx);
 
     // Check explicit non-syntactic compilation first to make sure it doesn't
     // modify our options object.
     CHECK(CompileForNonSyntacticScope(cx, options, buf, &script));
     CHECK_EQUAL(script->hasNonSyntacticScope(), true);
 
     CHECK(CompileLatin1ForNonSyntacticScope(cx, options, src, length, &script));
     CHECK_EQUAL(script->hasNonSyntacticScope(), true);
 
     {
-        JS::SourceBufferHolder srcBuf(src_16, length, JS::SourceBufferHolder::NoOwnership);
+        JS::SourceBufferHolder srcBuf;
+        CHECK(srcBuf.init(cx, src_16, length, JS::SourceBufferHolder::NoOwnership));
+
         CHECK(CompileForNonSyntacticScope(cx, options, srcBuf, &script));
         CHECK_EQUAL(script->hasNonSyntacticScope(), true);
     }
 
 
     CHECK(Compile(cx, options, buf, &script));
     CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
 
     CHECK(CompileLatin1(cx, options, src, length, &script));
     CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
 
     {
-        JS::SourceBufferHolder srcBuf(src_16, length, JS::SourceBufferHolder::NoOwnership);
+        JS::SourceBufferHolder srcBuf;
+        CHECK(srcBuf.init(cx, src_16, length, JS::SourceBufferHolder::NoOwnership));
+
         CHECK(Compile(cx, options, srcBuf, &script));
         CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
     }
 
 
     options.forceAsync = true;
     OffThreadTask task;
     OffThreadToken* token;
 
-    JS::SourceBufferHolder srcBuf(src_16, length, JS::SourceBufferHolder::NoOwnership);
+    JS::SourceBufferHolder srcBuf;
+    CHECK(srcBuf.init(cx, src_16, length, JS::SourceBufferHolder::NoOwnership));
+
     CHECK(CompileOffThread(cx, options, srcBuf, task.OffThreadCallback, &task));
     CHECK(token = task.waitUntilDone(cx));
     CHECK(script = FinishOffThreadScript(cx, token));
     CHECK_EQUAL(script->hasNonSyntacticScope(), nonSyntactic);
 
     return true;
 }
 END_TEST(testCompileScript);
--- a/js/src/jsapi-tests/testErrorLineOfContext.cpp
+++ b/js/src/jsapi-tests/testErrorLineOfContext.cpp
@@ -37,18 +37,21 @@ bool
 eval(const char16_t* chars, size_t len, JS::MutableHandleValue rval)
 {
     JS::RealmOptions globalOptions;
     JS::RootedObject global(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
 						   JS::FireOnNewGlobalHook, globalOptions));
     CHECK(global);
 
     JSAutoRealm ar(cx, global);
+
+    JS::SourceBufferHolder srcBuf;
+    CHECK(srcBuf.init(cx, chars, len, JS::SourceBufferHolder::NoOwnership));
+
     JS::CompileOptions options(cx);
-    JS::SourceBufferHolder srcBuf(chars, len, JS::SourceBufferHolder::NoOwnership);
     return JS::Evaluate(cx, options, srcBuf, rval);
 }
 
 template<size_t N>
 bool
 testLineOfContextHasNoLineTerminator(const char16_t (&chars)[N], char16_t expectedLast)
 {
     JS::RootedValue rval(cx);
--- a/js/src/jsapi-tests/testJSEvaluateScript.cpp
+++ b/js/src/jsapi-tests/testJSEvaluateScript.cpp
@@ -14,17 +14,20 @@ BEGIN_TEST(testJSEvaluateScript)
     CHECK(obj);
 
     static const char16_t src[] = u"var x = 5;";
 
     JS::RootedValue retval(cx);
     JS::CompileOptions opts(cx);
     JS::AutoObjectVector scopeChain(cx);
     CHECK(scopeChain.append(obj));
-    JS::SourceBufferHolder srcBuf(src, ArrayLength(src) - 1, JS::SourceBufferHolder::NoOwnership);
+
+    JS::SourceBufferHolder srcBuf;
+    CHECK(srcBuf.init(cx, src, ArrayLength(src) - 1, JS::SourceBufferHolder::NoOwnership));
+
     CHECK(JS::Evaluate(cx, scopeChain, opts.setFileAndLine(__FILE__, __LINE__),
                        srcBuf, &retval));
 
     bool hasProp = true;
     CHECK(JS_AlreadyHasOwnProperty(cx, obj, "x", &hasProp));
     CHECK(hasProp);
 
     hasProp = false;
--- a/js/src/jsapi-tests/testMutedErrors.cpp
+++ b/js/src/jsapi-tests/testMutedErrors.cpp
@@ -52,17 +52,19 @@ eval(const char* asciiChars, bool mutedE
     JSAutoRealm ar(cx, global);
     CHECK(JS::InitRealmStandardClasses(cx));
 
 
     JS::CompileOptions options(cx);
     options.setMutedErrors(mutedErrors)
            .setFileAndLine("", 0);
 
-    JS::SourceBufferHolder srcBuf(chars.get(), len, JS::SourceBufferHolder::NoOwnership);
+    JS::SourceBufferHolder srcBuf;
+    CHECK(srcBuf.init(cx, chars.get(), len, JS::SourceBufferHolder::NoOwnership));
+
     return JS::Evaluate(cx, options, srcBuf, rval);
 }
 
 bool
 testOuter(const char* asciiChars)
 {
     CHECK(testInner(asciiChars, false));
     CHECK(testInner(asciiChars, true));
--- a/js/src/jsapi-tests/testScriptObject.cpp
+++ b/js/src/jsapi-tests/testScriptObject.cpp
@@ -76,44 +76,50 @@ BEGIN_FIXTURE_TEST(ScriptObjectFixture, 
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_CompileScriptForPrincipals)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
 
+    JS::SourceBufferHolder srcBuf;
+    CHECK(srcBuf.init(cx, uc_code, code_size, JS::SourceBufferHolder::NoOwnership));
+
     JS::RootedScript script(cx);
-    JS::SourceBufferHolder srcBuf(uc_code, code_size, JS::SourceBufferHolder::NoOwnership);
     CHECK(JS::Compile(cx, options, srcBuf, &script));
 
     return tryScript(script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript_empty)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
 
+    JS::SourceBufferHolder srcBuf;
+    CHECK(srcBuf.init(cx, uc_code, 0, JS::SourceBufferHolder::NoOwnership));
+
     JS::RootedScript script(cx);
-    JS::SourceBufferHolder srcBuf(uc_code, 0, JS::SourceBufferHolder::NoOwnership);
     CHECK(JS::Compile(cx, options, srcBuf, &script));
 
     return tryScript(script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript_empty)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScriptForPrincipals)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
 
+    JS::SourceBufferHolder srcBuf;
+    CHECK(srcBuf.init(cx, uc_code, code_size, JS::SourceBufferHolder::NoOwnership));
+
     JS::RootedScript script(cx);
-    JS::SourceBufferHolder srcBuf(uc_code, code_size, JS::SourceBufferHolder::NoOwnership);
     CHECK(JS::Compile(cx, options, srcBuf, &script));
 
     return tryScript(script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScriptForPrincipals)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile)
 {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2136,18 +2136,23 @@ Evaluate(JSContext* cx, unsigned argc, V
 
             if (loadBytecode) {
                 JS::TranscodeResult rv = JS::DecodeScript(cx, loadBuffer, &script);
                 if (!ConvertTranscodeResultToJSException(cx, rv)) {
                     return false;
                 }
             } else {
                 mozilla::Range<const char16_t> chars = codeChars.twoByteRange();
-                JS::SourceBufferHolder srcBuf(chars.begin().get(), chars.length(),
-                                              JS::SourceBufferHolder::NoOwnership);
+                JS::SourceBufferHolder srcBuf;
+                if (!srcBuf.init(cx, chars.begin().get(), chars.length(),
+                                 JS::SourceBufferHolder::NoOwnership))
+                {
+                    return false;
+                }
+
                 if (envChain.length() == 0) {
                     (void) JS::Compile(cx, options, srcBuf, &script);
                 } else {
                     (void) JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script);
                 }
             }
 
             if (!script) {
@@ -2378,18 +2383,22 @@ Run(JSContext* cx, unsigned argc, Value*
         return false;
     }
 
     AutoStableStringChars chars(cx);
     if (!chars.initTwoByte(cx, str)) {
         return false;
     }
 
-    JS::SourceBufferHolder srcBuf(chars.twoByteRange().begin().get(), str->length(),
-                                  JS::SourceBufferHolder::NoOwnership);
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, chars.twoByteRange().begin().get(), str->length(),
+                     JS::SourceBufferHolder::NoOwnership))
+    {
+        return false;
+    }
 
     RootedScript script(cx);
     int64_t startClock = PRMJ_Now();
     {
         /* FIXME: This should use UTF-8 (bug 987069). */
         UniqueChars filename = JS_EncodeStringToLatin1(cx, str);
         if (!filename) {
             return false;
@@ -3860,18 +3869,20 @@ EvalInContext(JSContext* cx, unsigned ar
         sobj = ToWindowIfWindowProxy(sobj);
 
         if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
             JS_ReportErrorASCII(cx, "Invalid scope argument to evalcx");
             return false;
         }
         JS::CompileOptions opts(cx);
         opts.setFileAndLine(filename.get(), lineno);
-        JS::SourceBufferHolder srcBuf(src, srclen, JS::SourceBufferHolder::NoOwnership);
-        if (!JS::Evaluate(cx, opts, srcBuf, args.rval())) {
+        JS::SourceBufferHolder srcBuf;
+        if (!srcBuf.init(cx, src, srclen, JS::SourceBufferHolder::NoOwnership) ||
+            !JS::Evaluate(cx, opts, srcBuf, args.rval()))
+        {
             return false;
         }
     }
 
     if (!cx->compartment()->wrap(cx, args.rval())) {
         return false;
     }
 
@@ -3974,19 +3985,21 @@ WorkerMain(WorkerInput* input)
         JSAutoRealm ar(cx, global);
 
         JS::CompileOptions options(cx);
         options.setFileAndLine("<string>", 1)
                .setIsRunOnce(true);
 
         AutoReportException are(cx);
         RootedScript script(cx);
-        JS::SourceBufferHolder srcBuf(input->chars.get(), input->length,
-                                      JS::SourceBufferHolder::NoOwnership);
-        if (!JS::Compile(cx, options, srcBuf, &script)) {
+        JS::SourceBufferHolder srcBuf;
+        if (!srcBuf.init(cx, input->chars.get(), input->length,
+                         JS::SourceBufferHolder::NoOwnership) ||
+            !JS::Compile(cx, options, srcBuf, &script))
+        {
             break;
         }
         RootedValue result(cx);
         JS_ExecuteScript(cx, script, &result);
     } while (0);
 
     KillWatchdog(cx);
     JS_SetGrayGCRootsTracer(cx, nullptr, nullptr);
@@ -4651,23 +4664,31 @@ Compile(JSContext* cx, unsigned argc, Va
         return false;
     }
 
     JS::CompileOptions options(cx);
     options.setIntroductionType("js shell compile")
            .setFileAndLine("<string>", 1)
            .setIsRunOnce(true)
            .setNoScriptRval(true);
+
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, stableChars.twoByteRange().begin().get(), scriptContents->length(),
+                     JS::SourceBufferHolder::NoOwnership))
+    {
+        return false;
+    }
+
     RootedScript script(cx);
-    JS::SourceBufferHolder srcBuf(stableChars.twoByteRange().begin().get(),
-                                  scriptContents->length(),
-                                  JS::SourceBufferHolder::NoOwnership);
-    bool ok = JS::Compile(cx, options, srcBuf, &script);
+    if (!JS::Compile(cx, options, srcBuf, &script)) {
+        return false;
+    }
+
     args.rval().setUndefined();
-    return ok;
+    return true;
 }
 
 static ShellCompartmentPrivate*
 EnsureShellCompartmentPrivate(JSContext* cx)
 {
     Compartment* comp = cx->compartment();
     auto priv = static_cast<ShellCompartmentPrivate*>(JS_GetCompartmentPrivate(comp));
     if (!priv) {
@@ -4719,18 +4740,20 @@ ParseModule(JSContext* cx, unsigned argc
     }
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, scriptContents)) {
         return false;
     }
 
     const char16_t* chars = stableChars.twoByteRange().begin().get();
-    JS::SourceBufferHolder srcBuf(chars, scriptContents->length(),
-                                  JS::SourceBufferHolder::NoOwnership);
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, chars, scriptContents->length(), JS::SourceBufferHolder::NoOwnership)) {
+        return false;
+    }
 
     RootedObject module(cx, frontend::CompileModule(cx, options, srcBuf));
     if (!module) {
         return false;
     }
 
     args.rval().setObject(*module);
     return true;
@@ -5431,20 +5454,19 @@ OffThreadCompileScript(JSContext* cx, un
     }
 
     OffThreadJob* job = NewOffThreadJob(cx, ScriptKind::Script,
                                         OffThreadJob::Source(std::move(ownedChars)));
     if (!job) {
         return false;
     }
 
-    JS::SourceBufferHolder srcBuf(job->sourceChars(), length,
-                                  JS::SourceBufferHolder::NoOwnership);
-    if (!JS::CompileOffThread(cx, options, srcBuf,
-                              OffThreadCompileScriptCallback, job))
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, job->sourceChars(), length, JS::SourceBufferHolder::NoOwnership) ||
+        !JS::CompileOffThread(cx, options, srcBuf, OffThreadCompileScriptCallback, job))
     {
         job->cancel();
         DeleteOffThreadJob(cx, job);
         return false;
     }
 
     args.rval().setInt32(job->id);
     return true;
@@ -5525,20 +5547,19 @@ OffThreadCompileModule(JSContext* cx, un
     }
 
     OffThreadJob* job = NewOffThreadJob(cx, ScriptKind::Module,
                                         OffThreadJob::Source(std::move(ownedChars)));
     if (!job) {
         return false;
     }
 
-    JS::SourceBufferHolder srcBuf(job->sourceChars(), length,
-                                  JS::SourceBufferHolder::NoOwnership);
-    if (!JS::CompileOffThreadModule(cx, options, srcBuf,
-                                    OffThreadCompileScriptCallback, job))
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, job->sourceChars(), length, JS::SourceBufferHolder::NoOwnership) ||
+        !JS::CompileOffThreadModule(cx, options, srcBuf, OffThreadCompileScriptCallback, job))
     {
         job->cancel();
         DeleteOffThreadJob(cx, job);
         return false;
     }
 
     args.rval().setInt32(job->id);
     return true;
@@ -8206,19 +8227,22 @@ EntryPoints(JSContext* cx, unsigned argc
             if (!codeString || !codeString->ensureFlat(cx)) {
                 return false;
             }
 
             AutoStableStringChars stableChars(cx);
             if (!stableChars.initTwoByte(cx, codeString)) {
                 return false;
             }
-            JS::SourceBufferHolder srcBuf(stableChars.twoByteRange().begin().get(),
-                                          codeString->length(),
-                                          JS::SourceBufferHolder::NoOwnership);
+            JS::SourceBufferHolder srcBuf;
+            if (!srcBuf.init(cx, stableChars.twoByteRange().begin().get(), codeString->length(),
+                             JS::SourceBufferHolder::NoOwnership))
+            {
+                return false;
+            }
 
             CompileOptions options(cx);
             options.setIntroductionType("entryPoint eval")
                    .setFileAndLine("entryPoint eval", 1);
 
             js::shell::ShellAutoEntryMonitor sarep(cx);
             if (!JS::Evaluate(cx, options, srcBuf, &dummy)) {
                 return false;
--- a/js/src/vm/CompilationAndEvaluation.cpp
+++ b/js/src/vm/CompilationAndEvaluation.cpp
@@ -6,46 +6,55 @@
 
 /* Same-thread compilation and evaluation APIs. */
 
 #include "js/CompilationAndEvaluation.h"
 
 #include "mozilla/Maybe.h" // mozilla::None, mozilla::Some
 #include "mozilla/TextUtils.h" // mozilla::IsAscii
 
-#include <algorithm> // std::all_of
+#include <utility> // std::move
 
+#include "jsfriendapi.h" // js::GetErrorMessage
 #include "jstypes.h" // JS_PUBLIC_API
 
 #include "frontend/FullParseHandler.h" // frontend::FullParseHandler
 #include "frontend/ParseContext.h" // frontend::UsedNameTracker
 #include "frontend/Parser.h" // frontend::Parser, frontend::ParseGoal
 #include "js/CharacterEncoding.h" // JS::UTF8Chars, JS::UTF8CharsToNewTwoByteCharsZ
 #include "js/RootingAPI.h" // JS::Rooted
 #include "js/SourceBufferHolder.h" // JS::SourceBufferHolder
 #include "js/TypeDecls.h" // JS::HandleObject, JS::MutableHandleScript
+#include "js/Utility.h" // JS::UniqueTwoByteChars
 #include "js/Value.h" // JS::Value
 #include "util/CompleteFile.h" // js::FileContents, js::ReadCompleteFile
 #include "util/StringBuffer.h" // js::StringBuffer
 #include "vm/Debugger.h" // js::Debugger
 #include "vm/EnvironmentObject.h" // js::CreateNonSyntacticEnvironmentChain
 #include "vm/Interpreter.h" // js::Execute
 #include "vm/JSContext.h" // JSContext
 
 #include "vm/JSContext-inl.h" // JSContext::check
 
 using JS::CompileOptions;
 using JS::HandleObject;
 using JS::ReadOnlyCompileOptions;
 using JS::SourceBufferHolder;
+using JS::UniqueTwoByteChars;
 using JS::UTF8Chars;
 using JS::UTF8CharsToNewTwoByteCharsZ;
 
 using namespace js;
 
+JS_PUBLIC_API(void)
+JS::detail::ReportSourceTooLong(JSContext* cx)
+{
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SOURCE_TOO_LONG);
+}
+
 static bool
 CompileSourceBuffer(JSContext* cx, const ReadOnlyCompileOptions& options,
                     SourceBufferHolder& srcBuf, JS::MutableHandleScript script)
 {
     ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
 
     MOZ_ASSERT(!cx->zone()->isAtomsZone());
     AssertHeapIsIdle();
@@ -54,35 +63,44 @@ CompileSourceBuffer(JSContext* cx, const
     script.set(frontend::CompileGlobalScript(cx, scopeKind, options, srcBuf));
     return !!script;
 }
 
 static bool
 CompileLatin1(JSContext* cx, const ReadOnlyCompileOptions& options,
               const char* bytes, size_t length, JS::MutableHandleScript script)
 {
-    char16_t* chars = InflateString(cx, bytes, length);
+    auto chars = UniqueTwoByteChars(InflateString(cx, bytes, length));
     if (!chars) {
         return false;
     }
 
-    SourceBufferHolder source(chars, length, SourceBufferHolder::GiveOwnership);
+    SourceBufferHolder source;
+    if (!source.init(cx, std::move(chars), length)) {
+        return false;
+    }
+
     return CompileSourceBuffer(cx, options, source, script);
 }
 
 static bool
 CompileUtf8(JSContext* cx, const ReadOnlyCompileOptions& options,
             const char* bytes, size_t length, JS::MutableHandleScript script)
 {
-    char16_t* chars = UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get();
+    auto chars = UniqueTwoByteChars(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length),
+                                                                &length).get());
     if (!chars) {
         return false;
     }
 
-    SourceBufferHolder source(chars, length, SourceBufferHolder::GiveOwnership);
+    SourceBufferHolder source;
+    if (!source.init(cx, std::move(chars), length)) {
+        return false;
+    }
+
     return CompileSourceBuffer(cx, options, source, script);
 }
 
 bool
 JS::Compile(JSContext* cx, const ReadOnlyCompileOptions& options,
             SourceBufferHolder& srcBuf, JS::MutableHandleScript script)
 {
     return CompileSourceBuffer(cx, options, srcBuf, script);
@@ -334,34 +352,47 @@ JS::CompileFunction(JSContext* cx, AutoO
     StringBuffer funStr(cx);
     if (!BuildFunctionString(isInvalidName ? nullptr : name, nameLen, nargs, argnames, srcBuf,
                              &funStr, &parameterListEnd))
     {
         return false;
     }
 
     size_t newLen = funStr.length();
-    SourceBufferHolder newSrcBuf(funStr.stealChars(), newLen, SourceBufferHolder::GiveOwnership);
+    UniqueTwoByteChars stolen(funStr.stealChars());
+    if (!stolen) {
+        return false;
+    }
+
+    SourceBufferHolder newSrcBuf;
+    if (!newSrcBuf.init(cx, std::move(stolen), newLen)) {
+        return false;
+    }
 
     return CompileFunction(cx, options, nameAtom, isInvalidName, newSrcBuf, parameterListEnd, env,
                            scope, fun);
 }
 
 JS_PUBLIC_API(bool)
 JS::CompileFunctionUtf8(JSContext* cx, AutoObjectVector& envChain,
                         const ReadOnlyCompileOptions& options,
                         const char* name, unsigned nargs, const char* const* argnames,
                         const char* bytes, size_t length, MutableHandleFunction fun)
 {
-    char16_t* chars = UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get();
+    auto chars = UniqueTwoByteChars(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length),
+                                                                &length).get());
     if (!chars) {
         return false;
     }
 
-    SourceBufferHolder source(chars, length, SourceBufferHolder::GiveOwnership);
+    SourceBufferHolder source;
+    if (!source.init(cx, std::move(chars), length)) {
+        return false;
+    }
+
     return CompileFunction(cx, envChain, options, name, nargs, argnames,
                            source, fun);
 }
 
 JS_PUBLIC_API(bool)
 JS::InitScriptSourceElement(JSContext* cx, HandleScript script,
                             HandleObject element, HandleString elementAttrName)
 {
@@ -513,36 +544,45 @@ Evaluate(JSContext* cx, AutoObjectVector
     }
     return ::Evaluate(cx, scope->kind(), env, optionsArg, srcBuf, rval);
 }
 
 extern JS_PUBLIC_API(bool)
 JS::EvaluateUtf8(JSContext* cx, const ReadOnlyCompileOptions& options,
                  const char* bytes, size_t length, MutableHandle<Value> rval)
 {
-    char16_t* chars = UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get();
+    auto chars = UniqueTwoByteChars(UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length),
+                                                                &length).get());
     if (!chars) {
         return false;
     }
 
-    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::GiveOwnership);
+    SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, std::move(chars), length)) {
+        return false;
+    }
+
     RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
     return ::Evaluate(cx, ScopeKind::Global, globalLexical, options, srcBuf, rval);
 }
 
 extern JS_PUBLIC_API(bool)
 JS::EvaluateLatin1(JSContext* cx, const ReadOnlyCompileOptions& options,
                    const char* bytes, size_t length, MutableHandle<Value> rval)
 {
-    char16_t* chars = InflateString(cx, bytes, length);
+    auto chars = UniqueTwoByteChars(InflateString(cx, bytes, length));
     if (!chars) {
         return false;
     }
 
-    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::GiveOwnership);
+    SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, std::move(chars), length)) {
+        return false;
+    }
+
     RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
     return ::Evaluate(cx, ScopeKind::Global, globalLexical, options, srcBuf, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
              SourceBufferHolder& srcBuf, MutableHandleValue rval)
 {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -8898,18 +8898,23 @@ EvaluateInEnv(JSContext* cx, Handle<Env*
     cx->check(env, frame);
 
     CompileOptions options(cx);
     options.setIsRunOnce(true)
            .setNoScriptRval(false)
            .setFileAndLine(filename, lineno)
            .setIntroductionType("debugger eval")
            .maybeMakeStrictMode(frame && frame.hasScript() ? frame.script()->strict() : false);
+
+    SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, chars.begin().get(), chars.length(), SourceBufferHolder::NoOwnership)) {
+        return false;
+    }
+
     RootedScript callerScript(cx, frame && frame.hasScript() ? frame.script() : nullptr);
-    SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), SourceBufferHolder::NoOwnership);
     RootedScript script(cx);
 
     ScopeKind scopeKind;
     if (IsGlobalLexicalEnvironment(env)) {
         scopeKind = ScopeKind::Global;
     } else {
         scopeKind = ScopeKind::NonSyntactic;
     }
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -2113,17 +2113,21 @@ CreateDynamicFunction(JSContext* cx, con
     if (!stableChars.initTwoByte(cx, functionText)) {
         return false;
     }
 
     mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
     SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
                                               ? SourceBufferHolder::GiveOwnership
                                               : SourceBufferHolder::NoOwnership;
-    SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership);
+    SourceBufferHolder srcBuf;
+    if (!srcBuf.init(cx, chars.begin().get(), chars.length(), ownership)) {
+        return false;
+    }
+
     if (isAsync) {
         if (isGenerator) {
             if (!CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf, parameterListEnd)) {
                 return false;
             }
         } else {
             if (!CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd)) {
                 return false;
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -6711,21 +6711,26 @@ HandleInstantiationFailure(JSContext* cx
         options.strictOption = true;
     }
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, src)) {
         return false;
     }
 
+    SourceBufferHolder srcBuf;
+
     const char16_t* chars = stableChars.twoByteRange().begin().get();
     SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
                                               ? SourceBufferHolder::GiveOwnership
                                               : SourceBufferHolder::NoOwnership;
-    SourceBufferHolder srcBuf(chars, end - begin, ownership);
+    if (!srcBuf.init(cx, chars, end - begin, ownership)) {
+        return false;
+    }
+
     if (!frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, Nothing())) {
         return false;
     }
 
     // Call the function we just recompiled.
     args.setCallee(ObjectValue(*fun));
     return InternalCallOrConstruct(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
 }
--- a/js/xpconnect/loader/ChromeScriptLoader.cpp
+++ b/js/xpconnect/loader/ChromeScriptLoader.cpp
@@ -136,17 +136,21 @@ OffThreadScriptLoaderCallback(JS::OffThr
     SystemGroup::Dispatch(TaskCategory::Other, scriptCompiler.forget());
 }
 
 bool
 AsyncScriptCompiler::StartCompile(JSContext* aCx)
 {
     Rooted<JSObject*> global(aCx, mGlobalObject->GetGlobalJSObject());
 
-    JS::SourceBufferHolder srcBuf(std::move(mScriptText), mScriptLength);
+    JS::SourceBufferHolder srcBuf;
+    if (!srcBuf.init(aCx, std::move(mScriptText), mScriptLength)) {
+        return false;
+    }
+
     if (JS::CanCompileOffThread(aCx, mOptions, mScriptLength)) {
         if (!JS::CompileOffThread(aCx, mOptions, srcBuf,
                                   OffThreadScriptLoaderCallback,
                                   static_cast<void*>(this))) {
             return false;
         }
 
         NS_ADDREF(this);
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -155,25 +155,26 @@ PrepareScript(nsIURI* uri,
            .setNoScriptRval(!wantReturnValue);
     if (!charset.IsVoid()) {
         char16_t* scriptBuf = nullptr;
         size_t scriptLength = 0;
 
         nsresult rv =
             ScriptLoader::ConvertToUTF16(nullptr, reinterpret_cast<const uint8_t*>(buf), len,
                                          charset, nullptr, scriptBuf, scriptLength);
-
-        JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength,
-                                      JS::SourceBufferHolder::GiveOwnership);
-
         if (NS_FAILED(rv)) {
             ReportError(cx, LOAD_ERROR_BADCHARSET, uri);
             return false;
         }
 
+        JS::SourceBufferHolder srcBuf;
+        if (!srcBuf.init(cx, JS::UniqueTwoByteChars(scriptBuf), scriptLength)) {
+            return false;
+        }
+
         if (wantGlobalScript) {
             return JS::Compile(cx, options, srcBuf, script);
         }
         return JS::CompileForNonSyntacticScope(cx, options, srcBuf, script);
     }
     // We only use lazy source when no special encoding is specified because
     // the lazy source loader doesn't know the encoding.
     options.setSourceIsLazy(true);
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1919,19 +1919,22 @@ xpc::EvalInSandbox(JSContext* cx, Handle
         // This is clearly Gecko-specific and not in any spec.
         mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation");
         JSContext* sandcx = aes.cx();
         JSAutoRealm ar(sandcx, sandbox);
 
         JS::CompileOptions options(sandcx);
         options.setFileAndLine(filenameBuf.get(), lineNo);
         MOZ_ASSERT(JS_IsGlobalObject(sandbox));
-        JS::SourceBufferHolder buffer(PromiseFlatString(source).get(), source.Length(),
-                                      JS::SourceBufferHolder::NoOwnership);
-        ok = JS::Evaluate(sandcx, options, buffer, &v);
+
+        const nsPromiseFlatString& flat = PromiseFlatString(source);
+
+        JS::SourceBufferHolder buffer;
+        ok = buffer.init(sandcx, flat.get(), flat.Length(), JS::SourceBufferHolder::NoOwnership) &&
+             JS::Evaluate(sandcx, options, buffer, &v);
 
         // If the sandbox threw an exception, grab it off the context.
         if (aes.HasException()) {
             if (!aes.StealException(&exn)) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
         }
     }
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -693,17 +693,17 @@
  *   but logic elsewhere in the class ensures they are initialized prior to use.
  *   Using this attribute on a member disables the check that this member must be
  *   initialized in constructors via list-initialization, in the constructor body,
  *   or via functions called from the constructor body.
  * MOZ_IS_CLASS_INIT: Applies to class method declarations. Occasionally the
  *   constructor doesn't initialize all of the member variables and another function
  *   is used to initialize the rest. This marker is used to make the static analysis
  *   tool aware that the marked function is part of the initialization process
- *   and to include the marked function in the scan mechanism that determines witch
+ *   and to include the marked function in the scan mechanism that determines which
  *   member variables still remain uninitialized.
  * MOZ_NON_PARAM: Applies to types. Makes it compile time error to use the type
  *   in parameter without pointer or reference.
  * MOZ_NON_AUTOABLE: Applies to class declarations. Makes it a compile time error to
  *   use `auto` in place of this type in variable declarations.  This is intended to
  *   be used with types which are intended to be implicitly constructed into other
  *   other types before being assigned to variables.
  * MOZ_REQUIRED_BASE_METHOD: Applies to virtual class method declarations.