Bug 1498320 - Separate source-unit-aware functionality out of BytecodeCompiler into unrelated subclasses so that unit-agnostic functionality can be shared. r=tcampbell
authorJeff Walden <jwalden@mit.edu>
Thu, 01 Nov 2018 17:34:55 -0700
changeset 446506 532b05c76fa0ba9e56c278430a6e763feefa23a4
parent 446505 630a1645006614713cbe8698542c6d278601cb6f
child 446507 422924c19ce5c0aa87174e713e3aa43564f1a4b3
push id35042
push useraiakab@mozilla.com
push dateThu, 15 Nov 2018 09:54:38 +0000
treeherdermozilla-central@dca9c72df68b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1498320
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1498320 - Separate source-unit-aware functionality out of BytecodeCompiler into unrelated subclasses so that unit-agnostic functionality can be shared. r=tcampbell
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,292 @@ 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 toStringStart = 0;
+        uint32_t len = sourceBuffer_.length();
+        uint32_t toStringEnd = len;
+        return info.internalCreateScript(toStringStart, toStringEnd, 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 +365,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 +512,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 +544,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 +622,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 +811,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 +896,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 +925,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 +1174,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;
 }