Bug 987556 Part 2 Use JS::SourceBufferHolder in frontend::ByteCompiler API. r=luke
authorBen Kelly <ben@wanderview.com>
Fri, 25 Apr 2014 10:11:56 -0400
changeset 198652 6dc10870c5c5beecc246b2f69fef09c4dac5d727
parent 198651 f09a01f7050c1b4806b914ddbd750f83a3799991
child 198653 51d989e348f5440eb1f935ea3abfddac8b7d4f70
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)
reviewersluke
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 2 Use JS::SourceBufferHolder in frontend::ByteCompiler API. r=luke
js/src/NamespaceImports.h
js/src/builtin/Eval.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/jit/AsmJSLink.cpp
js/src/jsapi.cpp
js/src/jsfun.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsworkers.cpp
js/src/vm/Debugger.cpp
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -32,16 +32,18 @@ class AutoValueVector;
 
 class AutoIdArray;
 
 class AutoGCRooter;
 template <typename T> class AutoVectorRooter;
 template<typename K, typename V> class AutoHashMapRooter;
 template<typename T> class AutoHashSetRooter;
 
+class SourceBufferHolder;
+
 class HandleValueArray;
 
 }
 
 // Do the importing.
 namespace js {
 
 using JS::Value;
@@ -82,16 +84,17 @@ using JS::AutoVectorRooter;
 using JS::CallArgs;
 using JS::CallNonGenericMethod;
 using JS::CallReceiver;
 using JS::CompileOptions;
 using JS::IsAcceptableThis;
 using JS::NativeImpl;
 using JS::OwningCompileOptions;
 using JS::ReadOnlyCompileOptions;
+using JS::SourceBufferHolder;
 
 using JS::Rooted;
 using JS::RootedFunction;
 using JS::RootedId;
 using JS::RootedObject;
 using JS::RootedScript;
 using JS::RootedString;
 using JS::RootedValue;
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -315,19 +315,20 @@ EvalKernel(JSContext *cx, const CallArgs
 
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setOriginPrincipals(originPrincipals)
                .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset);
+        SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                      scopeobj, callerScript, options,
-                                                     chars.get(), length, flatStr, staticLevel);
+                                                     srcBuf, flatStr, staticLevel);
         if (!compiled)
             return false;
 
         MarkFunctionsWithinEvalScript(compiled);
 
         esg.setNewScript(compiled);
     }
 
@@ -383,19 +384,20 @@ js::DirectEvalStringFromIon(JSContext *c
 
         CompileOptions options(cx);
         options.setFileAndLine(filename, 1)
                .setCompileAndGo(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setOriginPrincipals(originPrincipals)
                .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset);
+        SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership);
         JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                      scopeobj, callerScript, options,
-                                                     chars.get(), length, flatStr, staticLevel);
+                                                     srcBuf, flatStr, staticLevel);
         if (!compiled)
             return false;
 
         MarkFunctionsWithinEvalScript(compiled);
 
         esg.setNewScript(compiled);
     }
 
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -22,22 +22,22 @@
 
 #include "frontend/Parser-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 using mozilla::Maybe;
 
 static bool
-CheckLength(ExclusiveContext *cx, size_t length)
+CheckLength(ExclusiveContext *cx, SourceBufferHolder &srcBuf)
 {
     // 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 (length > UINT32_MAX) {
+    if (srcBuf.length() > UINT32_MAX) {
         if (cx->isJSContext())
             JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr,
                                  JSMSG_SOURCE_TOO_LONG);
         return false;
     }
     return true;
 }
 
@@ -149,24 +149,24 @@ CanLazilyParse(ExclusiveContext *cx, con
         !cx->compartment()->options().discardSource() &&
         !options.sourceIsLazy &&
         !(cx->compartment()->debugMode() &&
           cx->compartment()->runtimeFromAnyThread()->debugHooks.newScriptHook);
 }
 
 void
 frontend::MaybeCallSourceHandler(JSContext *cx, const ReadOnlyCompileOptions &options,
-                                 const jschar *chars, size_t length)
+                                 SourceBufferHolder &srcBuf)
 {
     JSSourceHandler listener = cx->runtime()->debugHooks.sourceHandler;
     void *listenerData = cx->runtime()->debugHooks.sourceHandlerData;
 
     if (listener) {
         void *listenerTSData;
-        listener(options.filename(), options.lineno, chars, length,
+        listener(options.filename(), options.lineno, srcBuf.get(), srcBuf.length(),
                  &listenerTSData, listenerData);
     }
 }
 
 ScriptSourceObject *
 frontend::CreateScriptSourceObject(ExclusiveContext *cx, const ReadOnlyCompileOptions &options)
 {
     ScriptSource *ss = cx->new_<ScriptSource>();
@@ -179,84 +179,89 @@ frontend::CreateScriptSourceObject(Exclu
 
     return ScriptSourceObject::create(cx, ss, options);
 }
 
 JSScript *
 frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain,
                         HandleScript evalCaller,
                         const ReadOnlyCompileOptions &options,
-                        const jschar *chars, size_t length,
+                        SourceBufferHolder &srcBuf,
                         JSString *source_ /* = nullptr */,
                         unsigned staticLevel /* = 0 */,
                         SourceCompressionTask *extraSct /* = nullptr */)
 {
+    JS_ASSERT(srcBuf.get());
+
     RootedString source(cx, source_);
 
     js::TraceLogger *logger = nullptr;
     if (cx->isJSContext())
         logger = TraceLoggerForMainThread(cx->asJSContext()->runtime());
     else
         logger = TraceLoggerForCurrentThread();
     uint32_t logId = js::TraceLogCreateTextId(logger, options);
     js::AutoTraceLog scriptLogger(logger, logId);
     js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileScript);
 
     if (cx->isJSContext())
-        MaybeCallSourceHandler(cx->asJSContext(), options, chars, length);
+        MaybeCallSourceHandler(cx->asJSContext(), options, srcBuf);
 
     /*
      * The scripted callerFrame can only be given for compile-and-go scripts
      * and non-zero static level requires callerFrame.
      */
     JS_ASSERT_IF(evalCaller, options.compileAndGo);
     JS_ASSERT_IF(evalCaller, options.forEval);
     JS_ASSERT_IF(staticLevel != 0, evalCaller);
 
-    if (!CheckLength(cx, length))
+    if (!CheckLength(cx, srcBuf))
         return nullptr;
     JS_ASSERT_IF(staticLevel != 0, !options.sourceIsLazy);
 
     RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
     if (!sourceObject)
         return nullptr;
 
     ScriptSource *ss = sourceObject->source();
 
     SourceCompressionTask mysct(cx);
     SourceCompressionTask *sct = extraSct ? extraSct : &mysct;
 
     if (!cx->compartment()->options().discardSource()) {
         if (options.sourceIsLazy)
             ss->setSourceRetrievable();
-        else if (!ss->setSourceCopy(cx, chars, length, false, sct))
+        else if (!ss->setSourceCopy(cx, srcBuf, false, sct))
             return nullptr;
     }
 
     bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
-        syntaxParser.construct(cx, alloc, options, chars, length, /* foldConstants = */ false,
+        syntaxParser.construct(cx, alloc, options, srcBuf.get(), srcBuf.length(),
+                               /* foldConstants = */ false,
                                (Parser<SyntaxParseHandler> *) nullptr,
                                (LazyScript *) nullptr);
     }
 
-    Parser<FullParseHandler> parser(cx, alloc, options, chars, length, /* foldConstants = */ true,
+    Parser<FullParseHandler> parser(cx, alloc, options, srcBuf.get(), srcBuf.length(),
+                                    /* foldConstants = */ true,
                                     canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr);
     parser.sct = sct;
     parser.ss = ss;
 
     Directives directives(options.strictOption);
     GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption);
 
     bool savedCallerFun = options.compileAndGo &&
                           evalCaller && evalCaller->functionOrCallerFunction();
     Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
-                                                  options, staticLevel, sourceObject, 0, length));
+                                                  options, staticLevel, sourceObject, 0,
+                                                  srcBuf.length()));
     if (!script)
         return nullptr;
 
     // We can specialize a bit for the given scope chain if that scope chain is the global object.
     JSObject *globalScope =
         scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr;
     JS_ASSERT_IF(globalScope, globalScope->isNative());
     JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
@@ -486,58 +491,60 @@ frontend::CompileLazyFunction(JSContext 
 
     return EmitFunctionScript(cx, &bce, pn->pn_body);
 }
 
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 static bool
 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyCompileOptions &options,
-                    const AutoNameVector &formals, const jschar *chars, size_t length,
+                    const AutoNameVector &formals, SourceBufferHolder &srcBuf,
                     GeneratorKind generatorKind)
 {
     js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
     uint32_t logId = js::TraceLogCreateTextId(logger, options);
     js::AutoTraceLog scriptLogger(logger, logId);
     js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileFunction);
 
     // FIXME: make Function pass in two strings and parse them as arguments and
     // ProgramElements respectively.
 
-    MaybeCallSourceHandler(cx, options, chars, length);
+    MaybeCallSourceHandler(cx, options, srcBuf);
 
-    if (!CheckLength(cx, length))
+    if (!CheckLength(cx, srcBuf))
         return false;
 
     RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
     if (!sourceObject)
         return nullptr;
     ScriptSource *ss = sourceObject->source();
 
     SourceCompressionTask sct(cx);
     JS_ASSERT(!options.sourceIsLazy);
     if (!cx->compartment()->options().discardSource()) {
-        if (!ss->setSourceCopy(cx, chars, length, true, &sct))
+        if (!ss->setSourceCopy(cx, srcBuf, true, &sct))
             return false;
     }
 
     bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
         syntaxParser.construct(cx, &cx->tempLifoAlloc(),
-                               options, chars, length, /* foldConstants = */ false,
+                               options, srcBuf.get(), srcBuf.length(),
+                               /* foldConstants = */ false,
                                (Parser<SyntaxParseHandler> *) nullptr,
                                (LazyScript *) nullptr);
     }
 
     JS_ASSERT(!options.forEval);
 
     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(),
-                                    options, chars, length, /* foldConstants = */ true,
+                                    options, srcBuf.get(), srcBuf.length(),
+                                    /* foldConstants = */ true,
                                     canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr);
     parser.sct = &sct;
     parser.ss = ss;
 
     JS_ASSERT(fun);
     JS_ASSERT(fun->isTenured());
 
     fun->setArgCount(formals.length());
@@ -579,17 +586,17 @@ CompileFunctionBody(JSContext *cx, Mutab
     if (!NameFunctions(cx, fn))
         return false;
 
     if (fn->pn_funbox->function()->isInterpreted()) {
         JS_ASSERT(fun == fn->pn_funbox->function());
 
         Rooted<JSScript*> script(cx, JSScript::Create(cx, js::NullPtr(), false, options,
                                                       /* staticLevel = */ 0, sourceObject,
-                                                      /* sourceStart = */ 0, length));
+                                                      /* sourceStart = */ 0, srcBuf.length()));
         if (!script)
             return false;
 
         script->bindings = fn->pn_funbox->bindings;
 
         /*
          * The reason for checking fun->environment() below is that certain
          * consumers of JS::CompileFunction, namely
@@ -621,20 +628,20 @@ CompileFunctionBody(JSContext *cx, Mutab
         return false;
 
     return true;
 }
 
 bool
 frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun,
                               const ReadOnlyCompileOptions &options,
-                              const AutoNameVector &formals, const jschar *chars, size_t length)
+                              const AutoNameVector &formals, JS::SourceBufferHolder &srcBuf)
 {
-    return CompileFunctionBody(cx, fun, options, formals, chars, length, NotGenerator);
+    return CompileFunctionBody(cx, fun, options, formals, srcBuf, NotGenerator);
 }
 
 bool
 frontend::CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun,
                                    const ReadOnlyCompileOptions &options, const AutoNameVector &formals,
-                                   const jschar *chars, size_t length)
+                                   JS::SourceBufferHolder &srcBuf)
 {
-    return CompileFunctionBody(cx, fun, options, formals, chars, length, StarGenerator);
+    return CompileFunctionBody(cx, fun, options, formals, srcBuf, StarGenerator);
 }
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -18,42 +18,42 @@ class LazyScript;
 class LifoAlloc;
 struct SourceCompressionTask;
 
 namespace frontend {
 
 JSScript *
 CompileScript(ExclusiveContext *cx, LifoAlloc *alloc,
               HandleObject scopeChain, HandleScript evalCaller,
-              const ReadOnlyCompileOptions &options, const jschar *chars, size_t length,
+              const ReadOnlyCompileOptions &options, SourceBufferHolder &srcBuf,
               JSString *source_ = nullptr, unsigned staticLevel = 0,
               SourceCompressionTask *extraSct = nullptr);
 
 bool
 CompileLazyFunction(JSContext *cx, Handle<LazyScript*> lazy, const jschar *chars, size_t length);
 
 bool
 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun,
                     const ReadOnlyCompileOptions &options,
-                    const AutoNameVector &formals, const jschar *chars, size_t length);
+                    const AutoNameVector &formals, JS::SourceBufferHolder &srcBuf);
 bool
 CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun,
                          const ReadOnlyCompileOptions &options,
-                         const AutoNameVector &formals, const jschar *chars, size_t length);
+                         const AutoNameVector &formals, JS::SourceBufferHolder &srcBuf);
 
 ScriptSourceObject *
 CreateScriptSourceObject(ExclusiveContext *cx, const ReadOnlyCompileOptions &options);
 
 /*
  * This should be called while still on the main thread if compilation will
  * occur on a worker thread.
  */
 void
 MaybeCallSourceHandler(JSContext *cx, const ReadOnlyCompileOptions &options,
-                       const jschar *chars, size_t length);
+                       JS::SourceBufferHolder &srcBuf);
 
 /*
  * True if str consists of an IdentifierStart character, followed by one or
  * more IdentifierPart characters, i.e. it matches the IdentifierName production
  * in the language spec.
  *
  * This returns true even if str is a keyword like "if".
  *
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -573,17 +573,18 @@ HandleDynamicLinkFailure(JSContext *cx, 
     if (module.bufferArgumentName())
         formals.infallibleAppend(module.bufferArgumentName());
 
     CompileOptions options(cx);
     options.setOriginPrincipals(module.scriptSource()->originPrincipals())
            .setCompileAndGo(false)
            .setNoScriptRval(false);
 
-    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, src->chars(), end - begin))
+    SourceBufferHolder srcBuf(src->chars(), end - begin, SourceBufferHolder::NoOwnership);
+    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
         return false;
 
     // Call the function we just recompiled.
     args.setCallee(ObjectValue(*fun));
     return Invoke(cx, args);
 }
 
 #ifdef MOZ_VTUNE
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4528,17 +4528,18 @@ JS::Compile(JSContext *cx, HandleObject 
             const jschar *chars, size_t length)
 {
     JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     AutoLastFrameCheck lfc(cx);
 
-    return frontend::CompileScript(cx, &cx->tempLifoAlloc(), obj, NullPtr(), options, chars, length);
+    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+    return frontend::CompileScript(cx, &cx->tempLifoAlloc(), obj, NullPtr(), options, srcBuf);
 }
 
 JSScript *
 JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
             const char *bytes, size_t length)
 {
     jschar *chars;
     if (options.utf8)
@@ -4711,17 +4712,18 @@ JS::CompileFunction(JSContext *cx, Handl
             return nullptr;
     }
 
     RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED, obj,
                                        funAtom, JSFunction::FinalizeKind, TenuredObject));
     if (!fun)
         return nullptr;
 
-    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length))
+    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
         return nullptr;
 
     if (obj && funAtom && options.defineOnScope) {
         Rooted<jsid> id(cx, AtomToId(funAtom));
         RootedValue value(cx, ObjectValue(*fun));
         if (!JSObject::defineGeneric(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
             return nullptr;
     }
@@ -4866,19 +4868,20 @@ Evaluate(JSContext *cx, HandleObject obj
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     AutoLastFrameCheck lfc(cx);
 
     options.setCompileAndGo(obj->is<GlobalObject>());
     options.setNoScriptRval(!rval);
     SourceCompressionTask sct(cx);
+    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
     RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                     obj, NullPtr(), options,
-                                                    chars, length, nullptr, 0, &sct));
+                                                    srcBuf, nullptr, 0, &sct));
     if (!script)
         return false;
 
     JS_ASSERT(script->getVersion() == options.version);
 
     bool result = Execute(cx, script, *obj, rval);
     if (!sct.complete())
         result = false;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1612,20 +1612,21 @@ FunctionConstructor(JSContext *cx, unsig
 
     if (!JSFunction::setTypeForScriptedFunction(cx, fun))
         return false;
 
     if (hasRest)
         fun->setHasRest();
 
     bool ok;
+    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
     if (isStarGenerator)
-        ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, chars, length);
+        ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, srcBuf);
     else
-        ok = frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length);
+        ok = frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf);
     args.rval().setObject(*fun);
     return ok;
 }
 
 bool
 js::Function(JSContext *cx, unsigned argc, Value *vp)
 {
     return FunctionConstructor(cx, argc, vp, NotGenerator);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1562,21 +1562,21 @@ ScriptSource::substring(JSContext *cx, u
     SourceDataCache::AutoHoldEntry holder;
     const jschar *chars = this->chars(cx, holder);
     if (!chars)
         return nullptr;
     return js_NewStringCopyN<CanGC>(cx, chars + start, stop - start);
 }
 
 bool
-ScriptSource::setSourceCopy(ExclusiveContext *cx, const jschar *src, uint32_t length,
+ScriptSource::setSourceCopy(ExclusiveContext *cx, SourceBufferHolder &srcBuf,
                             bool argumentsNotIncluded, SourceCompressionTask *task)
 {
     JS_ASSERT(!hasSourceData());
-    length_ = length;
+    length_ = srcBuf.length();
     argumentsNotIncluded_ = argumentsNotIncluded;
 
     // There are several cases where source compression is not a good idea:
     //  - If the script is tiny, then compression will save little or no space.
     //  - If the script is enormous, then decompression can take seconds. With
     //    lazy parsing, decompression is not uncommon, so this can significantly
     //    increase latency.
     //  - If there is only one core, then compression will contend with JS
@@ -1599,26 +1599,28 @@ ScriptSource::setSourceCopy(ExclusiveCon
     bool canCompressOffThread =
         WorkerThreadState().cpuCount > 1 &&
         WorkerThreadState().threadCount >= 2;
 #else
     bool canCompressOffThread = false;
 #endif
     const size_t TINY_SCRIPT = 256;
     const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
-    if (TINY_SCRIPT <= length && length < HUGE_SCRIPT && canCompressOffThread) {
+    if (TINY_SCRIPT <= srcBuf.length() && srcBuf.length() < HUGE_SCRIPT && canCompressOffThread) {
         task->ss = this;
-        task->chars = src;
+        task->chars = srcBuf.get();
         ready_ = false;
         if (!StartOffThreadCompression(cx, task))
             return false;
+    } else if (srcBuf.ownsChars()) {
+        data.source = srcBuf.take();
     } else {
-        if (!adjustDataSize(sizeof(jschar) * length))
+        if (!adjustDataSize(sizeof(jschar) * srcBuf.length()))
             return false;
-        PodCopy(data.source, src, length_);
+        PodCopy(data.source, srcBuf.get(), length_);
     }
 
     return true;
 }
 
 void
 ScriptSource::setSource(const jschar *src, size_t length)
 {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -475,18 +475,17 @@ class ScriptSource
     void incref() { refs++; }
     void decref() {
         JS_ASSERT(refs != 0);
         if (--refs == 0)
             destroy();
     }
     bool initFromOptions(ExclusiveContext *cx, const ReadOnlyCompileOptions &options);
     bool setSourceCopy(ExclusiveContext *cx,
-                       const jschar *src,
-                       uint32_t length,
+                       JS::SourceBufferHolder &srcBuf,
                        bool argumentsNotIncluded,
                        SourceCompressionTask *tok);
     void setSource(const jschar *src, size_t length);
     bool ready() const { return ready_; }
     void setSourceRetrievable() { sourceRetrievable_ = true; }
     bool sourceRetrievable() const { return sourceRetrievable_; }
     bool hasSourceData() const { return !ready() || !!data.source; }
     uint32_t length() const {
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -294,17 +294,18 @@ bool
 js::StartOffThreadParseScript(JSContext *cx, const ReadOnlyCompileOptions &options,
                               const jschar *chars, size_t length,
                               JS::OffThreadCompileCallback callback, void *callbackData)
 {
     // Suppress GC so that calls below do not trigger a new incremental GC
     // which could require barriers on the atoms compartment.
     gc::AutoSuppressGC suppress(cx);
 
-    frontend::MaybeCallSourceHandler(cx, options, chars, length);
+    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+    frontend::MaybeCallSourceHandler(cx, options, srcBuf);
 
     EnsureWorkerThreadsInitialized(cx);
 
     JS::CompartmentOptions compartmentOptions(cx->compartment()->options());
     compartmentOptions.setZone(JS::FreshZone);
     compartmentOptions.setInvisibleToDebugger(true);
     compartmentOptions.setMergeable(true);
 
@@ -855,20 +856,22 @@ WorkerThread::handleParseWorkload()
 
     parseTask = WorkerThreadState().parseWorklist().popCopy();
     parseTask->cx->setWorkerThread(this);
 
     {
         AutoUnlockWorkerThreadState unlock;
         PerThreadData::AutoEnterRuntime enter(threadData.addr(),
                                               parseTask->exclusiveContextGlobal->runtimeFromAnyThread());
+        SourceBufferHolder srcBuf(parseTask->chars, parseTask->length,
+                                  SourceBufferHolder::NoOwnership);
         parseTask->script = frontend::CompileScript(parseTask->cx, &parseTask->alloc,
                                                     NullPtr(), NullPtr(),
                                                     parseTask->options,
-                                                    parseTask->chars, parseTask->length);
+                                                    srcBuf);
     }
 
     // The callback is invoked while we are still off the main thread.
     parseTask->callback(parseTask, parseTask->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
     // migrate it into the correct compartment.
     WorkerThreadState().parseFinishedList().append(parseTask);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4653,18 +4653,19 @@ js::EvaluateInEnv(JSContext *cx, Handle<
     CompileOptions options(cx);
     options.setCompileAndGo(true)
            .setForEval(true)
            .setNoScriptRval(false)
            .setFileAndLine(filename, lineno)
            .setCanLazilyParse(false)
            .setIntroductionType("debugger eval");
     RootedScript callerScript(cx, frame ? frame.script() : nullptr);
+    SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership);
     RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), env, callerScript,
-                                                    options, chars.get(), length,
+                                                    options, srcBuf,
                                                     /* source = */ nullptr,
                                                     /* staticLevel = */ frame ? 1 : 0));
     if (!script)
         return false;
 
     script->setActiveEval();
     ExecuteType type = !frame ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG;
     return ExecuteKernel(cx, script, *env, thisv, type, frame, rval.address());