Bug 1459127 - Store ScriptSourceObject reference into LazyScript inside LazyScript. r=jimb
authorTooru Fujisawa <arai_a@mac.com>
Fri, 11 May 2018 14:03:56 +0900
changeset 417948 f6d3ea212415298d10b0ba08a479c7f1a5d02030
parent 417947 1c1d3f24941ae0710a55bdcae8ee3d2126ab5b82
child 417949 c06d63503dfbd14ea95240763341ff46b720f314
push id33984
push usercbrindusan@mozilla.com
push dateSat, 12 May 2018 09:47:51 +0000
treeherdermozilla-central@809b0329507e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1459127
milestone62.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 1459127 - Store ScriptSourceObject reference into LazyScript inside LazyScript. r=jimb
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jsapi-tests/testBinASTReader.cpp
js/src/jsapi.cpp
js/src/shell/js.cpp
js/src/vm/Debugger.cpp
js/src/vm/JSFunction.cpp
js/src/vm/JSScript.cpp
js/src/vm/JSScript.h
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 /* JS reflection package. */
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 
 #include <stdlib.h>
 
 #include "jspubtd.h"
 
 #include "builtin/Array.h"
 #include "builtin/Reflect.h"
@@ -3466,20 +3467,26 @@ reflect_parse(JSContext* cx, uint32_t ar
     CompileOptions options(cx);
     options.setFileAndLine(filename, lineno);
     options.setCanLazilyParse(false);
     options.allowHTMLComments = target == ParseTarget::Script;
     mozilla::Range<const char16_t> chars = linearChars.twoByteRange();
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
+
+    RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
+                                                                                 mozilla::Nothing()));
+    if (!sourceObject)
+        return false;
+
     Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options,
                                               chars.begin().get(), chars.length(),
                                               /* foldConstants = */ false, usedNames, nullptr,
-                                              nullptr);
+                                              nullptr, sourceObject);
     if (!parser.checkOptions())
         return false;
 
     serialize.setParser(&parser);
 
     ParseNode* pn;
     if (target == ParseTarget::Script) {
         pn = parser.parse();
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -218,24 +218,25 @@ bool
 BytecodeCompiler::createParser()
 {
     usedNames.emplace(cx);
     if (!usedNames->init())
         return false;
 
     if (canLazilyParse()) {
         syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
-                             /* foldConstants = */ false, *usedNames, nullptr, nullptr);
-
+                             /* foldConstants = */ false, *usedNames, nullptr, nullptr,
+                             sourceObject);
         if (!syntaxParser->checkOptions())
             return false;
     }
 
     parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
-                   /* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr);
+                   /* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr,
+                   sourceObject);
     parser->ss = scriptSource;
     return parser->checkOptions();
 }
 
 bool
 BytecodeCompiler::createSourceAndParser(const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
 {
     return createScriptSource(parameterListEnd) &&
@@ -770,32 +771,31 @@ frontend::CompileLazyFunction(JSContext*
             ? JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS
             : JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS;
         cx->runtime()->addTelemetry(HISTOGRAM, delta.ToMilliseconds());
     }
 
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
+
+    RootedScriptSourceObject sourceObject(cx, &lazy->sourceObject());
     Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
                                               /* foldConstants = */ true, usedNames, nullptr,
-                                              lazy);
+                                              lazy, sourceObject);
     if (!parser.checkOptions())
         return false;
 
     Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
     ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->toStringStart(),
                                                   lazy->strict(), lazy->generatorKind(),
                                                   lazy->asyncKind());
     if (!pn)
         return false;
 
-    RootedScriptSourceObject sourceObject(cx, lazy->sourceObject());
-    MOZ_ASSERT(sourceObject);
-
     Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
                                                   lazy->sourceStart(), lazy->sourceEnd(),
                                                   lazy->toStringStart(), lazy->toStringEnd()));
     if (!script)
         return false;
 
     if (lazy->isLikelyConstructorWrapper())
         script->setLikelyConstructorWrapper();
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7938,18 +7938,17 @@ BytecodeEmitter::emitFunction(ParseNode*
         if (fun->isInterpretedLazy()) {
             // We need to update the static scope chain regardless of whether
             // the LazyScript has already been initialized, due to the case
             // where we previously successfully compiled an inner function's
             // lazy script but failed to compile the outer script after the
             // fact. If we attempt to compile the outer script again, the
             // static scope chain will be newly allocated and will mismatch
             // the previously compiled LazyScript's.
-            ScriptSourceObject* source = &script->sourceObject()->as<ScriptSourceObject>();
-            fun->lazyScript()->setEnclosingScopeAndSource(innermostScope(), source);
+            fun->lazyScript()->setEnclosingScope(innermostScope());
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the
             // parent.  Use default values for the rest.
             Rooted<JSScript*> parent(cx, script);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -792,25 +792,27 @@ ParserBase::errorNoOffset(unsigned error
     ReportCompileError(context, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber, args);
 
     va_end(args);
 }
 
 ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
                        const ReadOnlyCompileOptions& options,
                        bool foldConstants,
-                       UsedNameTracker& usedNames)
+                       UsedNameTracker& usedNames,
+                       ScriptSourceObject* sourceObject)
   : AutoGCRooter(cx, PARSER),
     context(cx),
     alloc(alloc),
     anyChars(cx, options, thisForCtor()),
     traceListHead(nullptr),
     pc(nullptr),
     usedNames(usedNames),
     ss(nullptr),
+    sourceObject(cx, sourceObject),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
     isUnexpectedEOF_(false),
     awaitHandling_(AwaitIsName)
 {
@@ -843,32 +845,34 @@ ParserBase::~ParserBase()
 
     context->frontendCollectionPool().removeActiveCompilation();
 }
 
 template <class ParseHandler>
 PerHandlerParser<ParseHandler>::PerHandlerParser(JSContext* cx, LifoAlloc& alloc,
                                                  const ReadOnlyCompileOptions& options,
                                                  bool foldConstants, UsedNameTracker& usedNames,
-                                                 LazyScript* lazyOuterFunction)
-  : ParserBase(cx, alloc, options, foldConstants, usedNames),
+                                                 LazyScript* lazyOuterFunction,
+                                                 ScriptSourceObject* sourceObject)
+  : ParserBase(cx, alloc, options, foldConstants, usedNames, sourceObject),
     handler(cx, alloc, lazyOuterFunction)
 {
 
 }
 
 template <class ParseHandler, typename CharT>
 GeneralParser<ParseHandler, CharT>::GeneralParser(JSContext* cx, LifoAlloc& alloc,
                                                   const ReadOnlyCompileOptions& options,
                                                   const CharT* chars, size_t length,
                                                   bool foldConstants,
                                                   UsedNameTracker& usedNames,
                                                   SyntaxParser* syntaxParser,
-                                                  LazyScript* lazyOuterFunction)
-  : Base(cx, alloc, options, foldConstants, usedNames, lazyOuterFunction),
+                                                  LazyScript* lazyOuterFunction,
+                                                  ScriptSourceObject* sourceObject)
+  : Base(cx, alloc, options, foldConstants, usedNames, lazyOuterFunction, sourceObject),
     tokenStream(cx, options, chars, length)
 {
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
     // standard "use strict" does not inhibit lazy parsing.
     if (options.extraWarningsOption)
         disableSyntaxParser();
     else
@@ -2529,17 +2533,18 @@ PerHandlerParser<SyntaxParseHandler>::fi
         pc->innerFunctionsForLazy.length() >= LazyScript::NumInnerFunctionsLimit)
     {
         MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
         return false;
     }
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
-    LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(),
+    LazyScript* lazy = LazyScript::Create(context, fun, sourceObject,
+                                          pc->closedOverBindingsForLazy(),
                                           pc->innerFunctionsForLazy,
                                           funbox->bufStart, funbox->bufEnd,
                                           funbox->toStringStart,
                                           funbox->startLine, funbox->startColumn);
     if (!lazy)
         return false;
 
     // Flags that need to be copied into the JSScript when we do the full
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -242,17 +242,17 @@ enum class PropertyType {
     DerivedConstructor
 };
 
 enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword };
 
 template <class ParseHandler, typename CharT>
 class AutoAwaitIsKeyword;
 
-class ParserBase
+class MOZ_STACK_CLASS ParserBase
   : public StrictModeGetter,
     private JS::AutoGCRooter
 {
   private:
     ParserBase* thisForCtor() { return this; }
 
     // This is needed to cast a parser to JS::AutoGCRooter.
     friend void js::frontend::TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
@@ -271,16 +271,18 @@ class ParserBase
     /* innermost parse context (stack-allocated) */
     ParseContext* pc;
 
     // For tracking used names in this parsing session.
     UsedNameTracker& usedNames;
 
     ScriptSource*       ss;
 
+    RootedScriptSourceObject sourceObject;
+
     /* Root atoms and objects allocated for the parsed tree. */
     AutoKeepAtoms       keepAtoms;
 
     /* Perform constant-folding; must be true when interfacing with the emitter. */
     const bool          foldConstants:1;
 
   protected:
 #if DEBUG
@@ -296,17 +298,18 @@ class ParserBase
   public:
     bool awaitIsKeyword() const {
       return awaitHandling_ != AwaitIsName;
     }
 
     template<class, typename> friend class AutoAwaitIsKeyword;
 
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
-               bool foldConstants, UsedNameTracker& usedNames);
+               bool foldConstants, UsedNameTracker& usedNames,
+               ScriptSourceObject* sourceObject);
     ~ParserBase();
 
     bool checkOptions();
 
     void trace(JSTracer* trc);
 
     const char* getFilename() const { return anyChars.getFilename(); }
     TokenPos pos() const { return anyChars.currentToken().pos; }
@@ -428,17 +431,17 @@ ParseContext::VarScope::VarScope(JSConte
 }
 
 enum FunctionCallBehavior {
     PermitAssignmentToFunctionCalls,
     ForbidAssignmentToFunctionCalls
 };
 
 template <class ParseHandler>
-class PerHandlerParser
+class MOZ_STACK_CLASS PerHandlerParser
   : public ParserBase
 {
   private:
     using Node = typename ParseHandler::Node;
 
   protected:
     /* State specific to the kind of parse being performed. */
     ParseHandler handler;
@@ -460,17 +463,18 @@ class PerHandlerParser
     // here, then intermediate all access to this field through accessors in
     // |GeneralParser<ParseHandler, CharT>| that impose the real type on this
     // field.
     void* internalSyntaxParser_;
 
   protected:
     PerHandlerParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                      bool foldConstants, UsedNameTracker& usedNames,
-                     LazyScript* lazyOuterFunction);
+                     LazyScript* lazyOuterFunction,
+                     ScriptSourceObject* sourceObject);
 
     static Node null() { return ParseHandler::null(); }
 
     Node stringLiteral();
 
     const char* nameIsArgumentsOrEval(Node node);
 
     bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
@@ -631,17 +635,17 @@ enum YieldHandling { YieldIsName, YieldI
 enum InHandling { InAllowed, InProhibited };
 enum DefaultHandling { NameRequired, AllowDefaultName };
 enum TripledotHandling { TripledotAllowed, TripledotProhibited };
 
 template <class ParseHandler, typename CharT>
 class Parser;
 
 template <class ParseHandler, typename CharT>
-class GeneralParser
+class MOZ_STACK_CLASS GeneralParser
   : public PerHandlerParser<ParseHandler>
 {
   public:
     using TokenStream = TokenStreamSpecific<CharT, ParserAnyCharsAccess<GeneralParser>>;
 
   private:
     using Base = PerHandlerParser<ParseHandler>;
     using FinalParser = Parser<ParseHandler, CharT>;
@@ -862,17 +866,18 @@ class GeneralParser
 
   public:
     TokenStream tokenStream;
 
   public:
     GeneralParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                   const CharT* chars, size_t length, bool foldConstants,
                   UsedNameTracker& usedNames, SyntaxParser* syntaxParser,
-                  LazyScript* lazyOuterFunction);
+                  LazyScript* lazyOuterFunction,
+                  ScriptSourceObject* sourceObject);
 
     inline void setAwaitHandling(AwaitHandling awaitHandling);
 
     /*
      * Parse a top-level JS script.
      */
     Node parse();
 
@@ -1243,17 +1248,17 @@ class GeneralParser
 
     bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
 
   private:
     inline bool asmJS(Node list);
 };
 
 template <typename CharT>
-class Parser<SyntaxParseHandler, CharT> final
+class MOZ_STACK_CLASS Parser<SyntaxParseHandler, CharT> final
   : public GeneralParser<SyntaxParseHandler, CharT>
 {
     using Base = GeneralParser<SyntaxParseHandler, CharT>;
     using Node = SyntaxParseHandler::Node;
 
     using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
 
     // Numerous Base::* functions have bodies like
@@ -1353,17 +1358,17 @@ class Parser<SyntaxParseHandler, CharT> 
                                bool tryAnnexB);
 
     bool asmJS(Node list);
 
     // Functions present only in Parser<SyntaxParseHandler, CharT>.
 };
 
 template <typename CharT>
-class Parser<FullParseHandler, CharT> final
+class MOZ_STACK_CLASS Parser<FullParseHandler, CharT> final
   : public GeneralParser<FullParseHandler, CharT>
 {
     using Base = GeneralParser<FullParseHandler, CharT>;
     using Node = FullParseHandler::Node;
 
     using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
 
     // Numerous Base::* functions have bodies like
--- a/js/src/jsapi-tests/testBinASTReader.cpp
+++ b/js/src/jsapi-tests/testBinASTReader.cpp
@@ -14,19 +14,20 @@
 #include <unistd.h>
 
 #elif defined(XP_WIN)
 
 #include <windows.h>
 
 #endif
 
+#include "mozilla/Maybe.h"
+
 #include "jsapi.h"
 
-
 #include "frontend/BinSource.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseContext.h"
 #include "frontend/Parser.h"
 #include "gc/Zone.h"
 #include "js/Vector.h"
 
 #include "jsapi-tests/tests.h"
@@ -162,20 +163,26 @@ runTestFromPath(JSContext* cx, const cha
 
         // Parse text file.
         CompileOptions txtOptions(cx);
         txtOptions.setFileAndLine(txtPath.begin(), 0);
 
         UsedNameTracker txtUsedNames(cx);
         if (!txtUsedNames.init())
             MOZ_CRASH("Couldn't initialize used names");
+
+        RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(
+                                                  cx, txtOptions, mozilla::Nothing()));
+        if (!sourceObject)
+            MOZ_CRASH("Couldn't initialize ScriptSourceObject");
+
         js::frontend::Parser<js::frontend::FullParseHandler, char16_t> txtParser(
             cx, allocScope.alloc(), txtOptions, txtSource.begin(), txtSource.length(),
             /* foldConstants = */ false, txtUsedNames, nullptr,
-            nullptr);
+            nullptr, sourceObject);
         if (!txtParser.checkOptions())
             MOZ_CRASH("Bad options");
 
         auto txtParsed = txtParser.parse(); // Will be deallocated once `parser` goes out of scope.
         RootedValue txtExn(cx);
         if (!txtParsed) {
             // Save exception for more detailed error message, if necessary.
             if (!js::GetAndClearException(cx, &txtExn))
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4457,20 +4457,27 @@ JS_BufferIsCompilableUnit(JSContext* cx,
     // Return true on any out-of-memory error or non-EOF-related syntax error, so our
     // caller doesn't try to collect more buffered source.
     bool result = true;
 
     CompileOptions options(cx);
     frontend::UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
+
+    RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
+                                                                                 mozilla::Nothing()));
+    if (!sourceObject)
+        return false;
+
     frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
                                                                   options, chars, length,
                                                                   /* foldConstants = */ true,
-                                                                  usedNames, nullptr, nullptr);
+                                                                  usedNames, nullptr, nullptr,
+                                                                  sourceObject);
     JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
     if (!parser.checkOptions() || !parser.parse()) {
         // We ran into an error. If it was because we ran out of source, we
         // return false so our caller knows to try to collect more buffered
         // source.
         if (parser.isUnexpectedEOF())
             result = false;
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4481,19 +4481,25 @@ Parse(JSContext* cx, unsigned argc, Valu
     CompileOptions options(cx);
     options.setIntroductionType("js shell parse")
            .setFileAndLine("<string>", 1)
            .setAllowSyntaxParser(allowSyntaxParser);
 
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
+
+    RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
+                                                                                 Nothing()));
+    if (!sourceObject)
+        return false;
+
     Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
                                               /* foldConstants = */ false, usedNames, nullptr,
-                                              nullptr);
+                                              nullptr, sourceObject);
     if (!parser.checkOptions())
         return false;
 
     ParseNode* pn = parser.parse(); // Deallocated once `parser` goes out of scope.
     if (!pn)
         return false;
 #ifdef DEBUG
     js::Fprinter out(stderr);
@@ -4532,19 +4538,26 @@ SyntaxParse(JSContext* cx, unsigned argc
     if (!stableChars.initTwoByte(cx, scriptContents))
         return false;
 
     const char16_t* chars = stableChars.twoByteRange().begin().get();
     size_t length = scriptContents->length();
     UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
+
+    RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
+                                                                                 Nothing()));
+    if (!sourceObject)
+        return false;
+
     Parser<frontend::SyntaxParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
                                                           options, chars, length, false,
-                                                          usedNames, nullptr, nullptr);
+                                                          usedNames, nullptr, nullptr,
+                                                          sourceObject);
     if (!parser.checkOptions())
         return false;
 
     bool succeeded = parser.parse();
     if (cx->isExceptionPending())
         return false;
 
     if (!succeeded && !parser.hadAbortedSyntaxParse()) {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -5045,21 +5045,28 @@ Debugger::isCompilableUnit(JSContext* cx
         return false;
 
     bool result = true;
 
     CompileOptions options(cx);
     frontend::UsedNameTracker usedNames(cx);
     if (!usedNames.init())
         return false;
+
+    RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
+                                                                                 Nothing()));
+    if (!sourceObject)
+        return false;
+
     frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
                                                                   options, chars.twoByteChars(),
                                                                   length,
                                                                   /* foldConstants = */ true,
-                                                                  usedNames, nullptr, nullptr);
+                                                                  usedNames, nullptr, nullptr,
+                                                                  sourceObject);
     JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
     if (!parser.checkOptions() || !parser.parse()) {
         // We ran into an error. If it was because we ran out of memory we report
         // it in the usual way.
         if (cx->isThrowingOutOfMemory()) {
             JS::SetWarningReporter(cx, older);
             return false;
         }
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -1650,17 +1650,17 @@ JSFunction::createScriptForLazilyInterpr
             // Remember the lazy script on the compiled script, so it can be
             // stored on the function again in case of re-lazification.
             // Only functions without inner functions are re-lazified.
             script->setLazyScript(lazy);
         }
 
         // XDR the newly delazified function.
         if (script->scriptSource()->hasEncoder()) {
-            RootedScriptSourceObject sourceObject(cx, lazy->sourceObject());
+            RootedScriptSourceObject sourceObject(cx, &lazy->sourceObject());
             if (!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
                 return false;
         }
 
         return true;
     }
 
     /* Lazily cloned self-hosted script. */
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -904,16 +904,18 @@ js::XDRScript(XDRState<XDR_DECODE>*, Han
               MutableHandleScript);
 
 template<XDRMode mode>
 XDRResult
 js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
                   HandleScriptSourceObject sourceObject, HandleFunction fun,
                   MutableHandle<LazyScript*> lazy)
 {
+    MOZ_ASSERT_IF(mode == XDR_DECODE, sourceObject);
+
     JSContext* cx = xdr->cx();
 
     {
         uint32_t sourceStart;
         uint32_t sourceEnd;
         uint32_t toStringStart;
         uint32_t toStringEnd;
         uint32_t lineno;
@@ -962,17 +964,17 @@ js::XDRLazyScript(XDRState<mode>* xdr, H
     {
         RootedFunction func(cx);
         GCPtrFunction* innerFunctions = lazy->innerFunctions();
         size_t numInnerFunctions = lazy->numInnerFunctions();
         for (size_t i = 0; i < numInnerFunctions; i++) {
             if (mode == XDR_ENCODE)
                 func = innerFunctions[i];
 
-            MOZ_TRY(XDRInterpretedFunction(xdr, nullptr, nullptr, &func));
+            MOZ_TRY(XDRInterpretedFunction(xdr, nullptr, sourceObject, &func));
 
             if (mode == XDR_DECODE)
                 innerFunctions[i] = func;
         }
     }
 
     return Ok();
 }
@@ -4170,32 +4172,36 @@ JSScript::formalIsAliased(unsigned argSl
 }
 
 bool
 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
 {
     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
 
-LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
+LazyScript::LazyScript(JSFunction* fun, ScriptSourceObject& sourceObject,
+                       void* table, uint64_t packedFields,
                        uint32_t sourceStart, uint32_t sourceEnd,
                        uint32_t toStringStart, uint32_t lineno, uint32_t column)
   : script_(nullptr),
     function_(fun),
     enclosingScope_(nullptr),
-    sourceObject_(nullptr),
+    sourceObject_(&sourceObject),
     table_(table),
     packedFields_(packedFields),
     sourceStart_(sourceStart),
     sourceEnd_(sourceEnd),
     toStringStart_(toStringStart),
     toStringEnd_(sourceEnd),
     lineno_(lineno),
     column_(column)
 {
+    MOZ_ASSERT(function_);
+    MOZ_ASSERT(sourceObject_);
+    MOZ_ASSERT(function_->compartment() == sourceObject_->compartment());
     MOZ_ASSERT(sourceStart <= sourceEnd);
     MOZ_ASSERT(toStringStart <= sourceStart);
 }
 
 void
 LazyScript::initScript(JSScript* script)
 {
     MOZ_ASSERT(script);
@@ -4206,46 +4212,43 @@ LazyScript::initScript(JSScript* script)
 void
 LazyScript::resetScript()
 {
     MOZ_ASSERT(script_.unbarrieredGet());
     script_.set(nullptr);
 }
 
 void
-LazyScript::setEnclosingScopeAndSource(Scope* enclosingScope, ScriptSourceObject* sourceObject)
+LazyScript::setEnclosingScope(Scope* enclosingScope)
 {
-    MOZ_ASSERT(function_->compartment() == sourceObject->compartment());
     // This method may be called to update the enclosing scope. See comment
     // above the callsite in BytecodeEmitter::emitFunction.
-    MOZ_ASSERT_IF(sourceObject_, sourceObject_ == sourceObject && enclosingScope_);
-    MOZ_ASSERT_IF(!sourceObject_, !enclosingScope_);
-
     enclosingScope_ = enclosingScope;
-    sourceObject_ = sourceObject;
 }
 
-ScriptSourceObject*
+ScriptSourceObject&
 LazyScript::sourceObject() const
 {
-    return sourceObject_ ? &sourceObject_->as<ScriptSourceObject>() : nullptr;
+    return sourceObject_->as<ScriptSourceObject>();
 }
 
 ScriptSource*
 LazyScript::maybeForwardedScriptSource() const
 {
-    JSObject* source = MaybeForwarded(sourceObject());
+    JSObject* source = MaybeForwarded(&sourceObject());
     return UncheckedUnwrapWithoutExpose(source)->as<ScriptSourceObject>().source();
 }
 
 /* static */ LazyScript*
 LazyScript::CreateRaw(JSContext* cx, HandleFunction fun,
+                      HandleScriptSourceObject sourceObject,
                       uint64_t packedFields, uint32_t sourceStart, uint32_t sourceEnd,
                       uint32_t toStringStart, uint32_t lineno, uint32_t column)
 {
+    MOZ_ASSERT(sourceObject);
     union {
         PackedView p;
         uint64_t packed;
     };
 
     packed = packedFields;
 
     // Reset runtime flags to obtain a fresh LazyScript.
@@ -4262,22 +4265,23 @@ LazyScript::CreateRaw(JSContext* cx, Han
     }
 
     LazyScript* res = Allocate<LazyScript>(cx);
     if (!res)
         return nullptr;
 
     cx->compartment()->scheduleDelazificationForDebugger();
 
-    return new (res) LazyScript(fun, table.forget(), packed, sourceStart, sourceEnd,
+    return new (res) LazyScript(fun, *sourceObject, table.forget(), packed, sourceStart, sourceEnd,
                                 toStringStart, lineno, column);
 }
 
 /* static */ LazyScript*
 LazyScript::Create(JSContext* cx, HandleFunction fun,
+                   HandleScriptSourceObject sourceObject,
                    const frontend::AtomVector& closedOverBindings,
                    Handle<GCVector<JSFunction*, 8>> innerFunctions,
                    uint32_t sourceStart, uint32_t sourceEnd,
                    uint32_t toStringStart, uint32_t lineno, uint32_t column)
 {
     union {
         PackedView p;
         uint64_t packedFields;
@@ -4293,17 +4297,18 @@ LazyScript::Create(JSContext* cx, Handle
     p.strict = false;
     p.bindingsAccessedDynamically = false;
     p.hasDebuggerStatement = false;
     p.hasDirectEval = false;
     p.isLikelyConstructorWrapper = false;
     p.isDerivedClassConstructor = false;
     p.needsHomeObject = false;
 
-    LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, sourceStart, sourceEnd,
+    LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
+                                            sourceStart, sourceEnd,
                                             toStringStart, lineno, column);
     if (!res)
         return nullptr;
 
     JSAtom** resClosedOverBindings = res->closedOverBindings();
     for (size_t i = 0; i < res->numClosedOverBindings(); i++)
         resClosedOverBindings[i] = closedOverBindings[i];
 
@@ -4323,41 +4328,40 @@ LazyScript::Create(JSContext* cx, Handle
 {
     // Dummy atom which is not a valid property name.
     RootedAtom dummyAtom(cx, cx->names().comma);
 
     // Dummy function which is not a valid function as this is the one which is
     // holding this lazy script.
     HandleFunction dummyFun = fun;
 
-    LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, sourceStart, sourceEnd,
+    LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
+                                            sourceStart, sourceEnd,
                                             toStringStart, lineno, column);
     if (!res)
         return nullptr;
 
     // Fill with dummies, to be GC-safe after the initialization of the free
     // variables and inner functions.
     size_t i, num;
     JSAtom** closedOverBindings = res->closedOverBindings();
     for (i = 0, num = res->numClosedOverBindings(); i < num; i++)
         closedOverBindings[i] = dummyAtom;
 
     GCPtrFunction* functions = res->innerFunctions();
     for (i = 0, num = res->numInnerFunctions(); i < num; i++)
         functions[i].init(dummyFun);
 
-    // Set the enclosing scope and source object of the lazy function. These
-    // values should only be non-null if we have a non-lazy enclosing script.
-    // LazyScript::isEnclosingScriptLazy relies on the source object being null
-    // if we're nested inside another lazy function.
-    MOZ_ASSERT(!!sourceObject == !!enclosingScope);
-    MOZ_ASSERT(!res->sourceObject());
+    // Set the enclosing scope of the lazy function. This value should only be
+    // non-null if we have a non-lazy enclosing script.
+    // LazyScript::isEnclosingScriptLazy relies on the enclosing scope being
+    // null if we're nested inside another lazy function.
     MOZ_ASSERT(!res->enclosingScope());
-    if (sourceObject)
-        res->setEnclosingScopeAndSource(enclosingScope, sourceObject);
+    if (enclosingScope)
+        res->setEnclosingScope(enclosingScope);
 
     MOZ_ASSERT(!res->hasScript());
     if (script)
         res->initScript(script);
 
     return res;
 }
 
@@ -4497,18 +4501,14 @@ JS::ubi::Concrete<js::LazyScript>::size(
     Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
     size += get().sizeOfExcludingThis(mallocSizeOf);
     return size;
 }
 
 const char*
 JS::ubi::Concrete<js::LazyScript>::scriptFilename() const
 {
-    auto sourceObject = get().sourceObject();
-    if (!sourceObject)
-        return nullptr;
-
-    auto source = sourceObject->source();
+    auto source = get().sourceObject().source();
     if (!source)
         return nullptr;
 
     return source->filename();
 }
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -2154,34 +2154,37 @@ class LazyScript : public gc::TenuredCel
     uint32_t sourceEnd_;
     uint32_t toStringStart_;
     uint32_t toStringEnd_;
     // Line and column of |begin_| position, that is the position where we
     // start parsing.
     uint32_t lineno_;
     uint32_t column_;
 
-    LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
+    LazyScript(JSFunction* fun, ScriptSourceObject& sourceObject,
+               void* table, uint64_t packedFields,
                uint32_t begin, uint32_t end, uint32_t toStringStart,
                uint32_t lineno, uint32_t column);
 
     // Create a LazyScript without initializing the closedOverBindings and the
     // innerFunctions. To be GC-safe, the caller must initialize both vectors
     // with valid atoms and functions.
     static LazyScript* CreateRaw(JSContext* cx, HandleFunction fun,
+                                 HandleScriptSourceObject sourceObject,
                                  uint64_t packedData, uint32_t begin, uint32_t end,
                                  uint32_t toStringStart, uint32_t lineno, uint32_t column);
 
   public:
     static const uint32_t NumClosedOverBindingsLimit = 1 << NumClosedOverBindingsBits;
     static const uint32_t NumInnerFunctionsLimit = 1 << NumInnerFunctionsBits;
 
     // Create a LazyScript and initialize closedOverBindings and innerFunctions
     // with the provided vectors.
     static LazyScript* Create(JSContext* cx, HandleFunction fun,
+                              HandleScriptSourceObject sourceObject,
                               const frontend::AtomVector& closedOverBindings,
                               Handle<GCVector<JSFunction*, 8>> innerFunctions,
                               uint32_t begin, uint32_t end,
                               uint32_t toStringStart, uint32_t lineno, uint32_t column);
 
     // Create a LazyScript and initialize the closedOverBindings and the
     // innerFunctions with dummy values to be replaced in a later initialization
     // phase.
@@ -2216,26 +2219,26 @@ class LazyScript : public gc::TenuredCel
     bool hasScript() const {
         return bool(script_);
     }
 
     Scope* enclosingScope() const {
         return enclosingScope_;
     }
 
-    ScriptSourceObject* sourceObject() const;
+    ScriptSourceObject& sourceObject() const;
     ScriptSource* scriptSource() const {
-        return sourceObject()->source();
+        return sourceObject().source();
     }
     ScriptSource* maybeForwardedScriptSource() const;
     bool mutedErrors() const {
         return scriptSource()->mutedErrors();
     }
 
-    void setEnclosingScopeAndSource(Scope* enclosingScope, ScriptSourceObject* sourceObject);
+    void setEnclosingScope(Scope* enclosingScope);
 
     uint32_t numClosedOverBindings() const {
         return p_.numClosedOverBindings;
     }
     JSAtom** closedOverBindings() {
         return (JSAtom**)table_;
     }
 
@@ -2383,17 +2386,17 @@ class LazyScript : public gc::TenuredCel
     }
 
     // Returns true if the enclosing script failed to compile.
     // See the comment in the definition for more details.
     bool hasUncompletedEnclosingScript() const;
 
     // Returns true if the enclosing script is also lazy.
     bool isEnclosingScriptLazy() const {
-        return !sourceObject_;
+        return !enclosingScope_;
     }
 
     friend class GCMarker;
     void traceChildren(JSTracer* trc);
     void finalize(js::FreeOp* fop);
 
     static const JS::TraceKind TraceKind = JS::TraceKind::LazyScript;