Bug 1498320 - Separate source-unit-aware functionality out of BytecodeCompiler into unrelated subclasses so that unit-agnostic functionality can be shared. NOT REVIEWED YET draft
authorJeff Walden <jwalden@mit.edu>
Thu, 01 Nov 2018 17:34:55 -0700
changeset 1745636 fc2825851782bbbf93d4eada5403f93d072cb9b3
parent 1745635 f44ae23672ffc006f900fec17867dbf2d59ef4bb
child 1745637 307cbf664bee8ae37d4f3779e14711ea5e4bb351
push id311974
push userjwalden@mit.edu
push dateSat, 03 Nov 2018 04:48:36 +0000
treeherdertry@736aa614eacd [default view] [failures only]
bugs1498320
milestone65.0a1
Bug 1498320 - Separate source-unit-aware functionality out of BytecodeCompiler into unrelated subclasses so that unit-agnostic functionality can be shared. NOT REVIEWED YET
js/src/frontend/BytecodeCompiler.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Maybe.h"
 
 #include "builtin/ModuleObject.h"
 #if defined(JS_BUILD_BINAST)
 # include "frontend/BinSource.h"
 #endif // JS_BUILD_BINAST
 #include "frontend/BytecodeEmitter.h"
+#include "frontend/EitherParser.h"
 #include "frontend/ErrorReporter.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/Parser.h"
 #include "js/SourceBufferHolder.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSScript.h"
 #include "vm/TraceLogging.h"
@@ -34,171 +35,290 @@ using namespace js::frontend;
 
 using mozilla::Maybe;
 using mozilla::Nothing;
 
 using JS::CompileOptions;
 using JS::ReadOnlyCompileOptions;
 using JS::SourceBufferHolder;
 
+class SourceAwareCompiler;
+class ScriptCompiler;
+class ModuleCompiler;
+class StandaloneFunctionCompiler;
+
 // The BytecodeCompiler class contains resources common to compiling scripts and
 // function bodies.
 class MOZ_STACK_CLASS BytecodeCompiler
 {
   protected:
     AutoKeepAtoms keepAtoms;
 
     JSContext* cx;
     const ReadOnlyCompileOptions& options;
-    SourceBufferHolder& sourceBuffer;
 
     RootedScriptSourceObject sourceObject;
-    ScriptSource* scriptSource;
+    ScriptSource* scriptSource = nullptr;
 
     Maybe<UsedNameTracker> usedNames;
-    Maybe<Parser<SyntaxParseHandler, char16_t>> syntaxParser;
-    Maybe<Parser<FullParseHandler, char16_t>> parser;
 
     Directives directives;
 
     RootedScript script;
 
   protected:
-    // Construct an object passing mandatory arguments.
-    BytecodeCompiler(JSContext* cx,
-                     const ReadOnlyCompileOptions& options,
-                     SourceBufferHolder& sourceBuffer);
+    BytecodeCompiler(JSContext* cx, const ReadOnlyCompileOptions& options);
+
+    friend class SourceAwareCompiler;
+    friend class ScriptCompiler;
+    friend class ModuleCompiler;
+    friend class StandaloneFunctionCompiler;
 
   public:
     ScriptSourceObject* sourceObjectPtr() const {
         return sourceObject.get();
     }
 
   protected:
-    // Call this before calling compile{Global,Eval}Script.
-    MOZ_MUST_USE bool prepareScriptParse() {
-        return createSourceAndParser(ParseGoal::Script) && createCompleteScript();
+    void assertSourceCreated() const {
+        MOZ_ASSERT(sourceObject != nullptr);
+        MOZ_ASSERT(scriptSource != nullptr);
+    }
+
+    MOZ_MUST_USE bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
+
+    void createUsedNames() {
+        usedNames.emplace(cx);
     }
 
-    void assertSourceAndParserCreated() const {
-        MOZ_ASSERT(sourceObject != nullptr);
-        MOZ_ASSERT(scriptSource != nullptr);
-        MOZ_ASSERT(usedNames.isSome());
+    // Create a script for source of the given length, using the explicitly-
+    // provided toString offsets as the created script's offsets in the source.
+    MOZ_MUST_USE bool internalCreateScript(uint32_t toStringStart, uint32_t toStringEnd,
+                                           uint32_t sourceBufferLength);
+
+    MOZ_MUST_USE bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter,
+                                     const EitherParser& parser, SharedContext* sharedContext);
+
+    // This function will eventually be templated on source-units type.  It
+    // lives here, not in SourceAwareCompiler, because it mostly uses fields in
+    // *this* class.
+    MOZ_MUST_USE bool assignSource(SourceBufferHolder& sourceBuffer);
+
+    bool canLazilyParse() const;
+
+    MOZ_MUST_USE bool
+    deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment);
+};
+
+class MOZ_STACK_CLASS SourceAwareCompiler
+{
+  protected:
+    SourceBufferHolder& sourceBuffer_;
+
+    Maybe<Parser<SyntaxParseHandler, char16_t>> syntaxParser;
+    Maybe<Parser<FullParseHandler, char16_t>> parser;
+
+    using TokenStreamPosition = frontend::TokenStreamPosition<char16_t>;
+
+  protected:
+    explicit SourceAwareCompiler(SourceBufferHolder& sourceBuffer)
+      : sourceBuffer_(sourceBuffer)
+    {
+        MOZ_ASSERT(sourceBuffer_.get() != nullptr);
+    }
+
+    // Call this before calling compile{Global,Eval}Script.
+    MOZ_MUST_USE bool prepareScriptParse(BytecodeCompiler& info) {
+        return createSourceAndParser(info, ParseGoal::Script) && createCompleteScript(info);
+    }
+
+    void assertSourceAndParserCreated(BytecodeCompiler& info) const {
+        info.assertSourceCreated();
+        MOZ_ASSERT(info.usedNames.isSome());
         MOZ_ASSERT(parser.isSome());
     }
 
-    JSScript* compileScript(HandleObject environment, SharedContext* sc);
+    void assertSourceParserAndScriptCreated(BytecodeCompiler& info) {
+        assertSourceAndParserCreated(info);
+        MOZ_ASSERT(info.script != nullptr);
+    }
 
-    MOZ_MUST_USE bool createSourceAndParser(ParseGoal goal,
+    MOZ_MUST_USE bool emplaceEmitter(BytecodeCompiler& info, Maybe<BytecodeEmitter>& emitter,
+                                     SharedContext* sharedContext)
+    {
+        return info.emplaceEmitter(emitter, EitherParser(parser.ptr()), sharedContext);
+    }
+
+    MOZ_MUST_USE bool createSourceAndParser(BytecodeCompiler& compiler, 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()
     // value.
-    MOZ_MUST_USE bool createCompleteScript();
-
-    // This uses explicitly-provided toString offsets as the created script's
-    // offsets in the source.
-    MOZ_MUST_USE bool createScript(uint32_t toStringStart, uint32_t toStringEnd);
-
-    MOZ_MUST_USE bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext);
-
-    using TokenStreamPosition = frontend::TokenStreamPosition<char16_t>;
-
-    MOZ_MUST_USE bool handleParseFailure(const Directives& newDirectives,
-                                         TokenStreamPosition& startPosition);
-
-  private:
-    void assertSourceParserAndScriptCreated() const {
-        assertSourceAndParserCreated();
-        MOZ_ASSERT(script != nullptr);
+    MOZ_MUST_USE bool createCompleteScript(BytecodeCompiler& info) {
+        uint32_t len = sourceBuffer_.length();
+        return info.internalCreateScript(0, len, len);
     }
 
-    bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
-    bool canLazilyParse();
-    bool createParser(ParseGoal goal);
-
-    bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment);
+    MOZ_MUST_USE bool
+    handleParseFailure(BytecodeCompiler& compiler, const Directives& newDirectives,
+                       TokenStreamPosition& startPosition);
 };
 
-class MOZ_STACK_CLASS GlobalScriptBytecodeCompiler final
+class MOZ_STACK_CLASS ScriptCompiler
+  : public SourceAwareCompiler
+{
+    using Base = SourceAwareCompiler;
+
+  protected:
+    using Base::Base;
+
+    JSScript* compileScript(BytecodeCompiler& compiler, HandleObject environment,
+                            SharedContext* sc);
+};
+
+class MOZ_STACK_CLASS GlobalScriptInfo final
   : public BytecodeCompiler
 {
     GlobalSharedContext globalsc_;
 
   public:
-    GlobalScriptBytecodeCompiler(JSContext* cx, const ReadOnlyCompileOptions& options,
-                                 SourceBufferHolder& sourceBuffer, ScopeKind scopeKind)
-      : BytecodeCompiler(cx, options, sourceBuffer),
+    GlobalScriptInfo(JSContext* cx, const ReadOnlyCompileOptions& options, ScopeKind scopeKind)
+      : BytecodeCompiler(cx, options),
         globalsc_(cx, scopeKind, directives, options.extraWarningsOption)
     {
         MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
     }
 
-    JSScript* compile() {
-        if (!prepareScriptParse()) {
-            return nullptr;
-        }
-        return compileScript(nullptr, &globalsc_);
+    GlobalSharedContext* sharedContext() {
+        return &globalsc_;
     }
 };
 
-class MOZ_STACK_CLASS EvalScriptBytecodeCompiler final
+class MOZ_STACK_CLASS GlobalScriptCompiler final
+  : public ScriptCompiler
+{
+    using Base = ScriptCompiler;
+
+  public:
+    explicit GlobalScriptCompiler(SourceBufferHolder& srcBuf)
+      : Base(srcBuf)
+    {}
+
+    JSScript* compile(GlobalScriptInfo& info) {
+        if (!prepareScriptParse(info)) {
+            return nullptr;
+        }
+        return compileScript(info, nullptr, info.sharedContext());
+    }
+};
+
+class MOZ_STACK_CLASS EvalScriptInfo final
   : public BytecodeCompiler
 {
     HandleObject environment_;
     EvalSharedContext evalsc_;
 
   public:
-    EvalScriptBytecodeCompiler(JSContext* cx, const ReadOnlyCompileOptions& options,
-                               SourceBufferHolder& sourceBuffer, HandleObject environment,
-                               HandleScope enclosingScope)
-      : BytecodeCompiler(cx, options, sourceBuffer),
+    EvalScriptInfo(JSContext* cx, const ReadOnlyCompileOptions& options, HandleObject environment,
+                   HandleScope enclosingScope)
+      : BytecodeCompiler(cx, options),
         environment_(environment),
         evalsc_(cx, environment_, enclosingScope, directives, options.extraWarningsOption)
     {}
 
-    JSScript* compile() {
-        if (!prepareScriptParse()) {
-            return nullptr;
-        }
-        return compileScript(environment_, &evalsc_);
+    HandleObject environment() {
+        return environment_;
+    }
+
+    EvalSharedContext* sharedContext() {
+        return &evalsc_;
     }
 };
 
-class MOZ_STACK_CLASS ModuleBytecodeCompiler final
+class MOZ_STACK_CLASS EvalScriptCompiler final
+  : public ScriptCompiler
+{
+    using Base = ScriptCompiler;
+
+  public:
+    explicit EvalScriptCompiler(SourceBufferHolder& srcBuf)
+      : Base(srcBuf)
+    {}
+
+    JSScript* compile(EvalScriptInfo& info) {
+        if (!prepareScriptParse(info)) {
+            return nullptr;
+        }
+        return compileScript(info, info.environment(), info.sharedContext());
+    }
+};
+
+class MOZ_STACK_CLASS ModuleInfo final
   : public BytecodeCompiler
 {
   public:
-    ModuleBytecodeCompiler(JSContext* cx, const ReadOnlyCompileOptions& options,
-                           SourceBufferHolder& sourceBuffer)
-      : BytecodeCompiler(cx, options, sourceBuffer)
+    ModuleInfo(JSContext* cx, const ReadOnlyCompileOptions& options)
+      : BytecodeCompiler(cx, options)
+    {}
+};
+
+class MOZ_STACK_CLASS ModuleCompiler final
+  : public SourceAwareCompiler
+{
+    using Base = SourceAwareCompiler;
+
+  public:
+    explicit ModuleCompiler(SourceBufferHolder& srcBuf)
+      : Base(srcBuf)
     {}
 
-    ModuleObject* compile();
+    ModuleObject* compile(ModuleInfo& info);
 };
 
-class MOZ_STACK_CLASS StandaloneFunctionBytecodeCompiler
+class MOZ_STACK_CLASS StandaloneFunctionInfo final
   : public BytecodeCompiler
 {
   public:
-    StandaloneFunctionBytecodeCompiler(JSContext* cx, const ReadOnlyCompileOptions& options,
-                                       SourceBufferHolder& sourceBuffer)
-      : BytecodeCompiler(cx, options, sourceBuffer)
+    StandaloneFunctionInfo(JSContext* cx, const ReadOnlyCompileOptions& options)
+      : BytecodeCompiler(cx, options)
+    {}
+};
+
+class MOZ_STACK_CLASS StandaloneFunctionCompiler final
+  : public SourceAwareCompiler
+{
+    using Base = SourceAwareCompiler;
+
+  public:
+    explicit StandaloneFunctionCompiler(SourceBufferHolder& srcBuf)
+      : Base(srcBuf)
     {}
 
-    MOZ_MUST_USE bool prepare(const Maybe<uint32_t>& parameterListEnd) {
-        return createSourceAndParser(ParseGoal::Script, parameterListEnd);
+    MOZ_MUST_USE bool prepare(StandaloneFunctionInfo& info,
+                              const Maybe<uint32_t>& parameterListEnd)
+    {
+        return createSourceAndParser(info, ParseGoal::Script, parameterListEnd);
     }
 
-    CodeNode* parse(MutableHandleFunction fun, HandleScope enclosingScope,
-                    GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
-                    const Maybe<uint32_t>& parameterListEnd);
+    CodeNode* parse(StandaloneFunctionInfo& info, MutableHandleFunction fun,
+                    HandleScope enclosingScope, GeneratorKind generatorKind,
+                    FunctionAsyncKind asyncKind, const Maybe<uint32_t>& parameterListEnd);
+
+    MOZ_MUST_USE bool compile(StandaloneFunctionInfo& info, CodeNode* parsedFunction,
+                              MutableHandleFunction fun);
 
-    MOZ_MUST_USE bool compile(CodeNode* parsedFunction, MutableHandleFunction fun);
+  private:
+    // Create a script for a function with the given toString offsets in source
+    // text.
+    MOZ_MUST_USE bool createFunctionScript(StandaloneFunctionInfo& info,
+                                           uint32_t toStringStart, uint32_t toStringEnd)
+    {
+        return info.internalCreateScript(toStringStart, toStringEnd, sourceBuffer_.length());
+    }
 };
 
 AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
                                            const ErrorReporter& errorReporter)
 #ifdef JS_TRACE_LOGGING
   : logger_(TraceLoggerForCurrentThread(cx))
 {
     // If the tokenizer hasn't yet gotten any tokens, use the line and column
@@ -243,140 +363,136 @@ AutoFrontendTraceLog::AutoFrontendTraceL
     frontendEvent_.emplace(TraceLogger_Frontend, errorReporter.getFilename(), line, column);
     frontendLog_.emplace(logger_, *frontendEvent_);
     typeLog_.emplace(logger_, id);
 }
 #else
 { }
 #endif
 
-BytecodeCompiler::BytecodeCompiler(JSContext* cx,
-                                   const ReadOnlyCompileOptions& options,
-                                   SourceBufferHolder& sourceBuffer)
+BytecodeCompiler::BytecodeCompiler(JSContext* cx, const ReadOnlyCompileOptions& options)
   : keepAtoms(cx),
     cx(cx),
     options(options),
-    sourceBuffer(sourceBuffer),
     sourceObject(cx),
-    scriptSource(nullptr),
     directives(options.strictOption),
     script(cx)
-{
-    MOZ_ASSERT(sourceBuffer.get());
-}
+{}
 
 bool
 BytecodeCompiler::createScriptSource(const Maybe<uint32_t>& parameterListEnd)
 {
     sourceObject = CreateScriptSourceObject(cx, options, parameterListEnd);
     if (!sourceObject) {
         return false;
     }
 
     scriptSource = sourceObject->source();
+    return true;
+}
 
+bool
+BytecodeCompiler::assignSource(SourceBufferHolder& sourceBuffer)
+{
     if (!cx->realm()->behaviors().discardSource()) {
         if (options.sourceIsLazy) {
             scriptSource->setSourceRetrievable();
         } else if (!scriptSource->setSourceCopy(cx, sourceBuffer)) {
             return false;
         }
     }
 
     return true;
 }
 
 bool
-BytecodeCompiler::canLazilyParse()
+BytecodeCompiler::canLazilyParse() const
 {
     return options.canLazilyParse &&
            !cx->realm()->behaviors().disableLazyParsing() &&
            !cx->realm()->behaviors().discardSource() &&
            !options.sourceIsLazy &&
            !cx->lcovEnabled() &&
            // Disabled during record/replay. The replay debugger requires
            // scripts to be constructed in a consistent order, which might not
            // happen with lazy parsing.
            !mozilla::recordreplay::IsRecordingOrReplaying();
 }
 
 bool
-BytecodeCompiler::createParser(ParseGoal goal)
+SourceAwareCompiler::createSourceAndParser(BytecodeCompiler& info, ParseGoal goal,
+                                           const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
 {
-    usedNames.emplace(cx);
+    if (!info.createScriptSource(parameterListEnd)) {
+        return false;
+    }
 
-    if (canLazilyParse()) {
-        syntaxParser.emplace(cx, cx->tempLifoAlloc(), options,
-                             sourceBuffer.get(), sourceBuffer.length(),
-                             /* foldConstants = */ false, *usedNames, nullptr, nullptr,
-                             sourceObject, goal);
+    if (!info.assignSource(sourceBuffer_)) {
+        return false;
+    }
+
+    info.createUsedNames();
+
+    if (info.canLazilyParse()) {
+        syntaxParser.emplace(info.cx, info.cx->tempLifoAlloc(), info.options,
+                             sourceBuffer_.get(), sourceBuffer_.length(),
+                             /* foldConstants = */ false, *info.usedNames, nullptr, nullptr,
+                             info.sourceObject, goal);
         if (!syntaxParser->checkOptions()) {
             return false;
         }
     }
 
-    parser.emplace(cx, cx->tempLifoAlloc(), options, sourceBuffer.get(), sourceBuffer.length(),
-                   /* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr,
-                   sourceObject, goal);
-    parser->ss = scriptSource;
+    parser.emplace(info.cx, info.cx->tempLifoAlloc(), info.options,
+                   sourceBuffer_.get(), sourceBuffer_.length(),
+                   /* foldConstants = */ true, *info.usedNames, syntaxParser.ptrOr(nullptr),
+                   nullptr, info.sourceObject, goal);
+    parser->ss = info.scriptSource;
     return parser->checkOptions();
 }
 
 bool
-BytecodeCompiler::createSourceAndParser(ParseGoal goal,
-                                        const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
+BytecodeCompiler::internalCreateScript(uint32_t toStringStart, uint32_t toStringEnd,
+                                       uint32_t sourceBufferLength)
 {
-    return createScriptSource(parameterListEnd) &&
-           createParser(goal);
-}
-
-bool
-BytecodeCompiler::createCompleteScript()
-{
-    return createScript(0, sourceBuffer.length());
-}
-
-bool
-BytecodeCompiler::createScript(uint32_t toStringStart, uint32_t toStringEnd)
-{
-    script = JSScript::Create(cx, options,
-                              sourceObject, /* sourceStart = */ 0, sourceBuffer.length(),
+    script = JSScript::Create(cx, options, sourceObject, /* sourceStart = */ 0, sourceBufferLength,
                               toStringStart, toStringEnd);
     return script != nullptr;
 }
 
 bool
-BytecodeCompiler::emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext)
+BytecodeCompiler::emplaceEmitter(Maybe<BytecodeEmitter>& emitter,
+                                 const EitherParser& parser, SharedContext* sharedContext)
 {
     BytecodeEmitter::EmitterMode emitterMode =
         options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
-    emitter.emplace(/* parent = */ nullptr, parser.ptr(), sharedContext, script,
+    emitter.emplace(/* parent = */ nullptr, parser, sharedContext, script,
                     /* lazyScript = */ nullptr, options.lineno, emitterMode);
     return emitter->init();
 }
 
 bool
-BytecodeCompiler::handleParseFailure(const Directives& newDirectives,
-                                     TokenStreamPosition& startPosition)
+SourceAwareCompiler::handleParseFailure(BytecodeCompiler& info, const Directives& newDirectives,
+                                        TokenStreamPosition& startPosition)
 {
     if (parser->hadAbortedSyntaxParse()) {
         // Hit some unrecoverable ambiguity during an inner syntax parse.
         // Syntax parsing has now been disabled in the parser, so retry
         // the parse.
         parser->clearAbortedSyntaxParse();
-    } else if (parser->anyChars.hadError() || directives == newDirectives) {
+    } else if (parser->anyChars.hadError() || info.directives == newDirectives) {
         return false;
     }
 
     parser->tokenStream.seek(startPosition);
 
     // Assignment must be monotonic to prevent reparsing iloops
-    MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
-    MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
-    directives = newDirectives;
+    MOZ_ASSERT_IF(info.directives.strict(), newDirectives.strict());
+    MOZ_ASSERT_IF(info.directives.asmJS(), newDirectives.asmJS());
+    info.directives = newDirectives;
     return true;
 }
 
 bool
 BytecodeCompiler::deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment)
 {
     RootedObject env(cx, environment);
     while (env->is<EnvironmentObject>() || env->is<DebugEnvironmentProxy>()) {
@@ -394,27 +510,29 @@ BytecodeCompiler::deoptimizeArgumentsInE
         }
         env = env->enclosingEnvironment();
     }
 
     return true;
 }
 
 JSScript*
-BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
+ScriptCompiler::compileScript(BytecodeCompiler& info, HandleObject environment, SharedContext* sc)
 {
-    assertSourceParserAndScriptCreated();
+    assertSourceParserAndScriptCreated(info);
 
-    TokenStreamPosition startPosition(keepAtoms, parser->tokenStream);
+    TokenStreamPosition startPosition(info.keepAtoms, parser->tokenStream);
 
     Maybe<BytecodeEmitter> emitter;
-    if (!emplaceEmitter(emitter, sc)) {
+    if (!emplaceEmitter(info, emitter, sc)) {
         return nullptr;
     }
 
+    JSContext* cx = info.cx;
+
     for (;;) {
         ParseNode* pn;
         {
             AutoGeckoProfilerEntry pseudoFrame(cx, "script parsing");
             if (sc->isEvalContext()) {
                 pn = parser->evalBody(sc->asEvalContext());
             } else {
                 pn = parser->globalBody(sc->asGlobalContext());
@@ -424,74 +542,76 @@ BytecodeCompiler::compileScript(HandleOb
         // Successfully parsed. Emit the script.
         AutoGeckoProfilerEntry pseudoFrame(cx, "script emit");
         if (pn) {
             if (sc->isEvalContext() && sc->hasDebuggerStatement() && !cx->helperThread()) {
                 // If the eval'ed script contains any debugger statement, force construction
                 // of arguments objects for the caller script and any other scripts it is
                 // transitively nested inside. The debugger can access any variable on the
                 // scope chain.
-                if (!deoptimizeArgumentsInEnclosingScripts(cx, environment)) {
+                if (!info.deoptimizeArgumentsInEnclosingScripts(cx, environment)) {
                     return nullptr;
                 }
             }
             if (!emitter->emitScript(pn)) {
                 return nullptr;
             }
             break;
         }
 
         // Maybe we aborted a syntax parse. See if we can try again.
-        if (!handleParseFailure(directives, startPosition)) {
+        if (!handleParseFailure(info, info.directives, startPosition)) {
             return nullptr;
         }
 
         // Reset UsedNameTracker state before trying again.
-        usedNames->reset();
+        info.usedNames->reset();
     }
 
     // We have just finished parsing the source. Inform the source so that we
     // can compute statistics (e.g. how much time our functions remain lazy).
-    script->scriptSource()->recordParseEnded();
+    info.script->scriptSource()->recordParseEnded();
 
     // Enqueue an off-thread source compression task after finishing parsing.
-    if (!scriptSource->tryCompressOffThread(cx)) {
+    if (!info.scriptSource->tryCompressOffThread(cx)) {
         return nullptr;
     }
 
     MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
 
-    return script;
+    return info.script;
 }
 
 ModuleObject*
-ModuleBytecodeCompiler::compile()
+ModuleCompiler::compile(ModuleInfo& info)
 {
-    if (!createSourceAndParser(ParseGoal::Module) || !createCompleteScript()) {
+    if (!createSourceAndParser(info, ParseGoal::Module) || !createCompleteScript(info)) {
         return nullptr;
     }
 
+    JSContext* cx = info.cx;
+
     Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
     if (!module) {
         return nullptr;
     }
 
-    module->init(script);
+    module->init(info.script);
 
     ModuleBuilder builder(cx, module, parser->anyChars);
 
     RootedScope enclosingScope(cx, &cx->global()->emptyGlobalScope());
     ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
     ParseNode* pn = parser->moduleBody(&modulesc);
     if (!pn) {
         return nullptr;
     }
 
     Maybe<BytecodeEmitter> emitter;
-    if (!emplaceEmitter(emitter, &modulesc)) {
+    if (!emplaceEmitter(info, emitter, &modulesc)) {
         return nullptr;
     }
     if (!emitter->emitScript(pn->as<CodeNode>().body())) {
         return nullptr;
     }
 
     if (!builder.initModule()) {
         return nullptr;
@@ -500,83 +620,85 @@ ModuleBytecodeCompiler::compile()
     RootedModuleEnvironmentObject env(cx, ModuleEnvironmentObject::create(cx, module));
     if (!env) {
         return nullptr;
     }
 
     module->setInitialEnvironment(env);
 
     // Enqueue an off-thread source compression task after finishing parsing.
-    if (!scriptSource->tryCompressOffThread(cx)) {
+    if (!info.scriptSource->tryCompressOffThread(cx)) {
         return nullptr;
     }
 
     MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
     return module;
 }
 
 // Parse a standalone JS function, which might appear as the value of an
 // event handler attribute in an HTML <INPUT> tag, or in a Function()
 // constructor.
 CodeNode*
-StandaloneFunctionBytecodeCompiler::parse(MutableHandleFunction fun, HandleScope enclosingScope,
-                                          GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
-                                          const Maybe<uint32_t>& parameterListEnd)
+StandaloneFunctionCompiler::parse(StandaloneFunctionInfo& info, MutableHandleFunction fun,
+                                  HandleScope enclosingScope, GeneratorKind generatorKind,
+                                  FunctionAsyncKind asyncKind,
+                                  const Maybe<uint32_t>& parameterListEnd)
 {
     MOZ_ASSERT(fun);
     MOZ_ASSERT(fun->isTenured());
 
-    assertSourceAndParserCreated();
+    assertSourceAndParserCreated(info);
 
-    TokenStreamPosition startPosition(keepAtoms, parser->tokenStream);
+    TokenStreamPosition startPosition(info.keepAtoms, parser->tokenStream);
 
     // Speculatively parse using the default directives implied by the context.
     // If a directive is encountered (e.g., "use strict") that changes how the
     // function should have been parsed, we backup and reparse with the new set
     // of directives.
 
     ParseNode* fn;
     do {
-        Directives newDirectives = directives;
+        Directives newDirectives = info.directives;
         fn = parser->standaloneFunction(fun, enclosingScope, parameterListEnd, generatorKind,
-                                        asyncKind, directives, &newDirectives);
-        if (!fn && !handleParseFailure(newDirectives, startPosition)) {
+                                        asyncKind, info.directives, &newDirectives);
+        if (!fn && !handleParseFailure(info, newDirectives, startPosition)) {
             return nullptr;
         }
     } while (!fn);
 
     return &fn->as<CodeNode>();
 }
 
 // Compile a standalone JS function.
 bool
-StandaloneFunctionBytecodeCompiler::compile(CodeNode* parsedFunction, MutableHandleFunction fun)
+StandaloneFunctionCompiler::compile(StandaloneFunctionInfo& info, CodeNode* parsedFunction,
+                                    MutableHandleFunction fun)
 {
     FunctionBox* funbox = parsedFunction->funbox();
     if (funbox->function()->isInterpreted()) {
         MOZ_ASSERT(fun == funbox->function());
 
-        if (!createScript(funbox->toStringStart, funbox->toStringEnd)) {
+        if (!createFunctionScript(info, funbox->toStringStart, funbox->toStringEnd)) {
             return false;
         }
 
         Maybe<BytecodeEmitter> emitter;
-        if (!emplaceEmitter(emitter, funbox)) {
+        if (!emplaceEmitter(info, emitter, funbox)) {
             return false;
         }
         if (!emitter->emitFunctionScript(parsedFunction, BytecodeEmitter::TopLevelFunction::Yes)) {
             return false;
         }
     } else {
         fun.set(funbox->function());
         MOZ_ASSERT(IsAsmJSModule(fun));
     }
 
     // Enqueue an off-thread source compression task after finishing parsing.
-    return scriptSource->tryCompressOffThread(cx);
+    return info.scriptSource->tryCompressOffThread(info.cx);
 }
 
 ScriptSourceObject*
 frontend::CreateScriptSourceObject(JSContext* cx, const ReadOnlyCompileOptions& options,
                                    const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
 {
     ScriptSource* ss = cx->new_<ScriptSource>();
     if (!ss) {
@@ -687,20 +809,21 @@ frontend::CompileGlobalScript(JSContext*
                               const ReadOnlyCompileOptions& options,
                               SourceBufferHolder& srcBuf,
                               ScriptSourceObject** sourceObjectOut)
 {
     MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
 
     AutoAssertReportedException assertException(cx);
 
-    GlobalScriptBytecodeCompiler compiler(cx, options, srcBuf, scopeKind);
-    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
+    GlobalScriptInfo info(cx, options, scopeKind);
+    AutoInitializeSourceObject autoSSO(info, sourceObjectOut);
 
-    JSScript* script = compiler.compile();
+    GlobalScriptCompiler compiler(srcBuf);
+    JSScript* script = compiler.compile(info);
     if (!script) {
         return nullptr;
     }
 
     assertException.reset();
     return script;
 }
 
@@ -771,20 +894,21 @@ JSScript*
 frontend::CompileEvalScript(JSContext* cx, HandleObject environment,
                             HandleScope enclosingScope,
                             const ReadOnlyCompileOptions& options,
                             SourceBufferHolder& srcBuf,
                             ScriptSourceObject** sourceObjectOut)
 {
     AutoAssertReportedException assertException(cx);
 
-    EvalScriptBytecodeCompiler compiler(cx, options, srcBuf, environment, enclosingScope);
-    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
+    EvalScriptInfo info(cx, options, environment, enclosingScope);
+    AutoInitializeSourceObject autoSSO(info, sourceObjectOut);
 
-    JSScript* script = compiler.compile();
+    EvalScriptCompiler compiler(srcBuf);
+    JSScript* script = compiler.compile(info);
     if (!script) {
         return nullptr;
     }
 
     assertException.reset();
     return script;
 
 }
@@ -799,20 +923,21 @@ frontend::CompileModule(JSContext* cx, c
 
     AutoAssertReportedException assertException(cx);
 
     CompileOptions options(cx, optionsInput);
     options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
     options.setIsRunOnce(true);
     options.allowHTMLComments = false;
 
-    ModuleBytecodeCompiler compiler(cx, options, srcBuf);
-    AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
+    ModuleInfo info(cx, options);
+    AutoInitializeSourceObject autoSSO(info, sourceObjectOut);
 
-    ModuleObject* module = compiler.compile();
+    ModuleCompiler compiler(srcBuf);
+    ModuleObject* module = compiler.compile(info);
     if (!module) {
         return nullptr;
     }
 
     assertException.reset();
     return module;
 }
 
@@ -1047,99 +1172,110 @@ bool
 frontend::CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun,
                                     const JS::ReadOnlyCompileOptions& options,
                                     JS::SourceBufferHolder& srcBuf,
                                     const Maybe<uint32_t>& parameterListEnd,
                                     HandleScope enclosingScope /* = nullptr */)
 {
     AutoAssertReportedException assertException(cx);
 
-    StandaloneFunctionBytecodeCompiler compiler(cx, options, srcBuf);
-    if (!compiler.prepare(parameterListEnd)) {
+    StandaloneFunctionInfo info(cx, options);
+
+    StandaloneFunctionCompiler compiler(srcBuf);
+    if (!compiler.prepare(info, parameterListEnd)) {
         return false;
     }
 
     RootedScope scope(cx, enclosingScope);
     if (!scope) {
         scope = &cx->global()->emptyGlobalScope();
     }
 
-    CodeNode* parsedFunction = compiler.parse(fun, scope, GeneratorKind::NotGenerator,
+    CodeNode* parsedFunction = compiler.parse(info ,fun, scope, GeneratorKind::NotGenerator,
                                               FunctionAsyncKind::SyncFunction, parameterListEnd);
-    if (!parsedFunction || !compiler.compile(parsedFunction, fun)) {
+    if (!parsedFunction || !compiler.compile(info, parsedFunction, fun)) {
         return false;
     }
 
     assertException.reset();
     return true;
 }
 
 bool
 frontend::CompileStandaloneGenerator(JSContext* cx, MutableHandleFunction fun,
                                      const JS::ReadOnlyCompileOptions& options,
                                      JS::SourceBufferHolder& srcBuf,
                                      const Maybe<uint32_t>& parameterListEnd)
 {
     AutoAssertReportedException assertException(cx);
 
-    StandaloneFunctionBytecodeCompiler compiler(cx, options, srcBuf);
-    if (!compiler.prepare(parameterListEnd)) {
+    StandaloneFunctionInfo info(cx, options);
+
+    StandaloneFunctionCompiler compiler(srcBuf);
+    if (!compiler.prepare(info, parameterListEnd)) {
         return false;
     }
 
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
-    CodeNode* parsedFunction = compiler.parse(fun, emptyGlobalScope, GeneratorKind::Generator,
-                                              FunctionAsyncKind::SyncFunction, parameterListEnd);
-    if (!parsedFunction || !compiler.compile(parsedFunction, fun)) {
+    CodeNode* parsedFunction =
+        compiler.parse(info, fun, emptyGlobalScope, GeneratorKind::Generator,
+                       FunctionAsyncKind::SyncFunction, parameterListEnd);
+    if (!parsedFunction || !compiler.compile(info, parsedFunction, fun)) {
         return false;
     }
 
     assertException.reset();
     return true;
 }
 
 bool
 frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
                                          const ReadOnlyCompileOptions& options,
                                          JS::SourceBufferHolder& srcBuf,
                                          const Maybe<uint32_t>& parameterListEnd)
 {
     AutoAssertReportedException assertException(cx);
 
-    StandaloneFunctionBytecodeCompiler compiler(cx, options, srcBuf);
-    if (!compiler.prepare(parameterListEnd)) {
+    StandaloneFunctionInfo info(cx, options);
+
+    StandaloneFunctionCompiler compiler(srcBuf);
+    if (!compiler.prepare(info, parameterListEnd)) {
         return false;
     }
 
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
-    CodeNode* parsedFunction = compiler.parse(fun, emptyGlobalScope, GeneratorKind::NotGenerator,
-                                              FunctionAsyncKind::AsyncFunction, parameterListEnd);
-    if (!parsedFunction || !compiler.compile(parsedFunction, fun)) {
+    CodeNode* parsedFunction =
+        compiler.parse(info, fun, emptyGlobalScope, GeneratorKind::NotGenerator,
+                       FunctionAsyncKind::AsyncFunction, parameterListEnd);
+    if (!parsedFunction || !compiler.compile(info, parsedFunction, fun)) {
         return false;
     }
 
     assertException.reset();
     return true;
 }
 
 bool
 frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
                                           const ReadOnlyCompileOptions& options,
                                           JS::SourceBufferHolder& srcBuf,
                                           const Maybe<uint32_t>& parameterListEnd)
 {
     AutoAssertReportedException assertException(cx);
 
-    StandaloneFunctionBytecodeCompiler compiler(cx, options, srcBuf);
-    if (!compiler.prepare(parameterListEnd)) {
+    StandaloneFunctionInfo info(cx, options);
+
+    StandaloneFunctionCompiler compiler(srcBuf);
+    if (!compiler.prepare(info, parameterListEnd)) {
         return false;
     }
 
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
-    CodeNode* parsedFunction = compiler.parse(fun, emptyGlobalScope, GeneratorKind::Generator,
-                                              FunctionAsyncKind::AsyncFunction, parameterListEnd);
-    if (!parsedFunction || !compiler.compile(parsedFunction, fun)) {
+    CodeNode* parsedFunction =
+        compiler.parse(info, fun, emptyGlobalScope, GeneratorKind::Generator,
+                       FunctionAsyncKind::AsyncFunction, parameterListEnd);
+    if (!parsedFunction || !compiler.compile(info, parsedFunction, fun)) {
         return false;
     }
 
     assertException.reset();
     return true;
 }