summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/BytecodeCompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/BytecodeCompiler.cpp')
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp738
1 files changed, 738 insertions, 0 deletions
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
new file mode 100644
index 000000000..d4c758b6c
--- /dev/null
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -0,0 +1,738 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/. */
+
+#include "frontend/BytecodeCompiler.h"
+
+#include "mozilla/IntegerPrintfMacros.h"
+
+#include "jscntxt.h"
+#include "jsscript.h"
+
+#include "builtin/ModuleObject.h"
+#include "frontend/BytecodeEmitter.h"
+#include "frontend/FoldConstants.h"
+#include "frontend/NameFunctions.h"
+#include "frontend/Parser.h"
+#include "vm/GlobalObject.h"
+#include "vm/TraceLogging.h"
+#include "wasm/AsmJS.h"
+
+#include "jsobjinlines.h"
+#include "jsscriptinlines.h"
+
+#include "vm/EnvironmentObject-inl.h"
+
+using namespace js;
+using namespace js::frontend;
+using mozilla::Maybe;
+
+class MOZ_STACK_CLASS AutoCompilationTraceLogger
+{
+ public:
+ AutoCompilationTraceLogger(ExclusiveContext* cx, const TraceLoggerTextId id,
+ const ReadOnlyCompileOptions& options);
+
+ private:
+ TraceLoggerThread* logger;
+ TraceLoggerEvent event;
+ AutoTraceLog scriptLogger;
+ AutoTraceLog typeLogger;
+};
+
+// The BytecodeCompiler class contains resources common to compiling scripts and
+// function bodies.
+class MOZ_STACK_CLASS BytecodeCompiler
+{
+ public:
+ // Construct an object passing mandatory arguments.
+ BytecodeCompiler(ExclusiveContext* cx,
+ LifoAlloc& alloc,
+ const ReadOnlyCompileOptions& options,
+ SourceBufferHolder& sourceBuffer,
+ HandleScope enclosingScope,
+ TraceLoggerTextId logId);
+
+ // Call setters for optional arguments.
+ void maybeSetSourceCompressor(SourceCompressionTask* sourceCompressor);
+ void setSourceArgumentsNotIncluded();
+
+ JSScript* compileGlobalScript(ScopeKind scopeKind);
+ JSScript* compileEvalScript(HandleObject environment, HandleScope enclosingScope);
+ ModuleObject* compileModule();
+ bool compileFunctionBody(MutableHandleFunction fun, Handle<PropertyNameVector> formals,
+ GeneratorKind generatorKind, FunctionAsyncKind asyncKind);
+
+ ScriptSourceObject* sourceObjectPtr() const;
+
+ private:
+ JSScript* compileScript(HandleObject environment, SharedContext* sc);
+ bool checkLength();
+ bool createScriptSource();
+ bool maybeCompressSource();
+ bool canLazilyParse();
+ bool createParser();
+ bool createSourceAndParser();
+ bool createScript();
+ bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext);
+ bool handleParseFailure(const Directives& newDirectives);
+ bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment);
+ bool maybeCompleteCompressSource();
+
+ AutoCompilationTraceLogger traceLogger;
+ AutoKeepAtoms keepAtoms;
+
+ ExclusiveContext* cx;
+ LifoAlloc& alloc;
+ const ReadOnlyCompileOptions& options;
+ SourceBufferHolder& sourceBuffer;
+
+ RootedScope enclosingScope;
+ bool sourceArgumentsNotIncluded;
+
+ RootedScriptSource sourceObject;
+ ScriptSource* scriptSource;
+
+ Maybe<SourceCompressionTask> maybeSourceCompressor;
+ SourceCompressionTask* sourceCompressor;
+
+ Maybe<UsedNameTracker> usedNames;
+ Maybe<Parser<SyntaxParseHandler>> syntaxParser;
+ Maybe<Parser<FullParseHandler>> parser;
+
+ Directives directives;
+ TokenStream::Position startPosition;
+
+ RootedScript script;
+};
+
+AutoCompilationTraceLogger::AutoCompilationTraceLogger(ExclusiveContext* cx,
+ const TraceLoggerTextId id, const ReadOnlyCompileOptions& options)
+ : logger(cx->isJSContext() ? TraceLoggerForMainThread(cx->asJSContext()->runtime())
+ : TraceLoggerForCurrentThread()),
+ event(logger, TraceLogger_AnnotateScripts, options),
+ scriptLogger(logger, event),
+ typeLogger(logger, id)
+{}
+
+BytecodeCompiler::BytecodeCompiler(ExclusiveContext* cx,
+ LifoAlloc& alloc,
+ const ReadOnlyCompileOptions& options,
+ SourceBufferHolder& sourceBuffer,
+ HandleScope enclosingScope,
+ TraceLoggerTextId logId)
+ : traceLogger(cx, logId, options),
+ keepAtoms(cx->perThreadData),
+ cx(cx),
+ alloc(alloc),
+ options(options),
+ sourceBuffer(sourceBuffer),
+ enclosingScope(cx, enclosingScope),
+ sourceArgumentsNotIncluded(false),
+ sourceObject(cx),
+ scriptSource(nullptr),
+ sourceCompressor(nullptr),
+ directives(options.strictOption),
+ startPosition(keepAtoms),
+ script(cx)
+{
+ MOZ_ASSERT(sourceBuffer.get());
+}
+
+void
+BytecodeCompiler::maybeSetSourceCompressor(SourceCompressionTask* sourceCompressor)
+{
+ this->sourceCompressor = sourceCompressor;
+}
+
+void
+BytecodeCompiler::setSourceArgumentsNotIncluded()
+{
+ sourceArgumentsNotIncluded = true;
+}
+
+bool
+BytecodeCompiler::checkLength()
+{
+ // Note this limit is simply so we can store sourceStart and sourceEnd in
+ // JSScript as 32-bits. It could be lifted fairly easily, since the compiler
+ // is using size_t internally already.
+ if (sourceBuffer.length() > UINT32_MAX) {
+ if (cx->isJSContext())
+ JS_ReportErrorNumberASCII(cx->asJSContext(), GetErrorMessage, nullptr,
+ JSMSG_SOURCE_TOO_LONG);
+ return false;
+ }
+ return true;
+}
+
+bool
+BytecodeCompiler::createScriptSource()
+{
+ if (!checkLength())
+ return false;
+
+ sourceObject = CreateScriptSourceObject(cx, options);
+ if (!sourceObject)
+ return false;
+
+ scriptSource = sourceObject->source();
+ return true;
+}
+
+bool
+BytecodeCompiler::maybeCompressSource()
+{
+ if (!sourceCompressor) {
+ maybeSourceCompressor.emplace(cx);
+ sourceCompressor = maybeSourceCompressor.ptr();
+ }
+
+ if (!cx->compartment()->behaviors().discardSource()) {
+ if (options.sourceIsLazy) {
+ scriptSource->setSourceRetrievable();
+ } else if (!scriptSource->setSourceCopy(cx, sourceBuffer, sourceArgumentsNotIncluded,
+ sourceCompressor))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+BytecodeCompiler::canLazilyParse()
+{
+ return options.canLazilyParse &&
+ !(enclosingScope && enclosingScope->hasOnChain(ScopeKind::NonSyntactic)) &&
+ !cx->compartment()->behaviors().disableLazyParsing() &&
+ !cx->compartment()->behaviors().discardSource() &&
+ !options.sourceIsLazy &&
+ !cx->lcovEnabled();
+}
+
+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,
+ (Parser<SyntaxParseHandler>*) nullptr, (LazyScript*) nullptr);
+
+ if (!syntaxParser->checkOptions())
+ return false;
+ }
+
+ parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
+ /* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr);
+ parser->sct = sourceCompressor;
+ parser->ss = scriptSource;
+ if (!parser->checkOptions())
+ return false;
+
+ parser->tokenStream.tell(&startPosition);
+ return true;
+}
+
+bool
+BytecodeCompiler::createSourceAndParser()
+{
+ return createScriptSource() &&
+ maybeCompressSource() &&
+ createParser();
+}
+
+bool
+BytecodeCompiler::createScript()
+{
+ script = JSScript::Create(cx, options,
+ sourceObject, /* sourceStart = */ 0, sourceBuffer.length());
+ return script != nullptr;
+}
+
+bool
+BytecodeCompiler::emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext)
+{
+ BytecodeEmitter::EmitterMode emitterMode =
+ options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
+ emitter.emplace(/* parent = */ nullptr, parser.ptr(), sharedContext, script,
+ /* lazyScript = */ nullptr, options.lineno, emitterMode);
+ return emitter->init();
+}
+
+bool
+BytecodeCompiler::handleParseFailure(const Directives& newDirectives)
+{
+ 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->tokenStream.hadError() || 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;
+ return true;
+}
+
+bool
+BytecodeCompiler::deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment)
+{
+ RootedObject env(cx, environment);
+ while (env->is<EnvironmentObject>() || env->is<DebugEnvironmentProxy>()) {
+ if (env->is<CallObject>()) {
+ RootedScript script(cx, env->as<CallObject>().callee().getOrCreateScript(cx));
+ if (!script)
+ return false;
+ if (script->argumentsHasVarBinding()) {
+ if (!JSScript::argumentsOptimizationFailed(cx, script))
+ return false;
+ }
+ }
+ env = env->enclosingEnvironment();
+ }
+
+ return true;
+}
+
+bool
+BytecodeCompiler::maybeCompleteCompressSource()
+{
+ return !maybeSourceCompressor || maybeSourceCompressor->complete();
+}
+
+JSScript*
+BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
+{
+ if (!createSourceAndParser())
+ return nullptr;
+
+ if (!createScript())
+ return nullptr;
+
+ Maybe<BytecodeEmitter> emitter;
+ if (!emplaceEmitter(emitter, sc))
+ return nullptr;
+
+ for (;;) {
+ ParseNode* pn;
+ if (sc->isEvalContext())
+ pn = parser->evalBody(sc->asEvalContext());
+ else
+ pn = parser->globalBody(sc->asGlobalContext());
+
+ // Successfully parsed. Emit the script.
+ if (pn) {
+ if (sc->isEvalContext() && sc->hasDebuggerStatement() && cx->isJSContext()) {
+ // 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->asJSContext(), environment))
+ return nullptr;
+ }
+ if (!NameFunctions(cx, pn))
+ return nullptr;
+ if (!emitter->emitScript(pn))
+ return nullptr;
+ parser->handler.freeTree(pn);
+
+ break;
+ }
+
+ // Maybe we aborted a syntax parse. See if we can try again.
+ if (!handleParseFailure(directives))
+ return nullptr;
+
+ // Reset UsedNameTracker state before trying again.
+ usedNames->reset();
+ }
+
+ if (!maybeCompleteCompressSource())
+ return nullptr;
+
+ MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
+
+ return script;
+}
+
+JSScript*
+BytecodeCompiler::compileGlobalScript(ScopeKind scopeKind)
+{
+ GlobalSharedContext globalsc(cx, scopeKind, directives, options.extraWarningsOption);
+ return compileScript(nullptr, &globalsc);
+}
+
+JSScript*
+BytecodeCompiler::compileEvalScript(HandleObject environment, HandleScope enclosingScope)
+{
+ EvalSharedContext evalsc(cx, environment, enclosingScope,
+ directives, options.extraWarningsOption);
+ return compileScript(environment, &evalsc);
+}
+
+ModuleObject*
+BytecodeCompiler::compileModule()
+{
+ if (!createSourceAndParser())
+ return nullptr;
+
+ Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
+ if (!module)
+ return nullptr;
+
+ if (!createScript())
+ return nullptr;
+
+ module->init(script);
+
+ ModuleBuilder builder(cx, module);
+ ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
+ ParseNode* pn = parser->moduleBody(&modulesc);
+ if (!pn)
+ return nullptr;
+
+ if (!NameFunctions(cx, pn))
+ return nullptr;
+
+ Maybe<BytecodeEmitter> emitter;
+ if (!emplaceEmitter(emitter, &modulesc))
+ return nullptr;
+ if (!emitter->emitScript(pn->pn_body))
+ return nullptr;
+
+ parser->handler.freeTree(pn);
+
+ if (!builder.initModule())
+ return nullptr;
+
+ RootedModuleEnvironmentObject env(cx, ModuleEnvironmentObject::create(cx, module));
+ if (!env)
+ return nullptr;
+
+ module->setInitialEnvironment(env);
+
+ if (!maybeCompleteCompressSource())
+ return nullptr;
+
+ MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
+ return module;
+}
+
+bool
+BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
+ Handle<PropertyNameVector> formals,
+ GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind)
+{
+ MOZ_ASSERT(fun);
+ MOZ_ASSERT(fun->isTenured());
+
+ fun->setArgCount(formals.length());
+
+ if (!createSourceAndParser())
+ return false;
+
+ // 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;
+ fn = parser->standaloneFunctionBody(fun, enclosingScope, formals, generatorKind, asyncKind,
+ directives, &newDirectives);
+ if (!fn && !handleParseFailure(newDirectives))
+ return false;
+ } while (!fn);
+
+ if (!NameFunctions(cx, fn))
+ return false;
+
+ if (fn->pn_funbox->function()->isInterpreted()) {
+ MOZ_ASSERT(fun == fn->pn_funbox->function());
+
+ if (!createScript())
+ return false;
+
+ Maybe<BytecodeEmitter> emitter;
+ if (!emplaceEmitter(emitter, fn->pn_funbox))
+ return false;
+ if (!emitter->emitFunctionScript(fn->pn_body))
+ return false;
+ } else {
+ fun.set(fn->pn_funbox->function());
+ MOZ_ASSERT(IsAsmJSModule(fun));
+ }
+
+ if (!maybeCompleteCompressSource())
+ return false;
+
+ return true;
+}
+
+ScriptSourceObject*
+BytecodeCompiler::sourceObjectPtr() const
+{
+ return sourceObject.get();
+}
+
+ScriptSourceObject*
+frontend::CreateScriptSourceObject(ExclusiveContext* cx, const ReadOnlyCompileOptions& options)
+{
+ ScriptSource* ss = cx->new_<ScriptSource>();
+ if (!ss)
+ return nullptr;
+ ScriptSourceHolder ssHolder(ss);
+
+ if (!ss->initFromOptions(cx, options))
+ return nullptr;
+
+ RootedScriptSource sso(cx, ScriptSourceObject::create(cx, ss));
+ if (!sso)
+ return nullptr;
+
+ // Off-thread compilations do all their GC heap allocation, including the
+ // SSO, in a temporary compartment. Hence, for the SSO to refer to the
+ // gc-heap-allocated values in |options|, it would need cross-compartment
+ // wrappers from the temporary compartment to the real compartment --- which
+ // would then be inappropriate once we merged the temporary and real
+ // compartments.
+ //
+ // Instead, we put off populating those SSO slots in off-thread compilations
+ // until after we've merged compartments.
+ if (cx->isJSContext()) {
+ if (!ScriptSourceObject::initFromOptions(cx->asJSContext(), sso, options))
+ return nullptr;
+ }
+
+ return sso;
+}
+
+// CompileScript independently returns the ScriptSourceObject (SSO) for the
+// compile. This is used by off-main-thread script compilation (OMT-SC).
+//
+// OMT-SC cannot initialize the SSO when it is first constructed because the
+// SSO is allocated initially in a separate compartment.
+//
+// After OMT-SC, the separate compartment is merged with the main compartment,
+// at which point the JSScripts created become observable by the debugger via
+// memory-space scanning.
+//
+// Whatever happens to the top-level script compilation (even if it fails and
+// returns null), we must finish initializing the SSO. This is because there
+// may be valid inner scripts observable by the debugger which reference the
+// partially-initialized SSO.
+class MOZ_STACK_CLASS AutoInitializeSourceObject
+{
+ BytecodeCompiler& compiler_;
+ ScriptSourceObject** sourceObjectOut_;
+
+ public:
+ AutoInitializeSourceObject(BytecodeCompiler& compiler, ScriptSourceObject** sourceObjectOut)
+ : compiler_(compiler),
+ sourceObjectOut_(sourceObjectOut)
+ { }
+
+ ~AutoInitializeSourceObject() {
+ if (sourceObjectOut_)
+ *sourceObjectOut_ = compiler_.sourceObjectPtr();
+ }
+};
+
+JSScript*
+frontend::CompileGlobalScript(ExclusiveContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
+ const ReadOnlyCompileOptions& options,
+ SourceBufferHolder& srcBuf,
+ SourceCompressionTask* extraSct,
+ ScriptSourceObject** sourceObjectOut)
+{
+ MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
+ BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr,
+ TraceLogger_ParserCompileScript);
+ AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
+ compiler.maybeSetSourceCompressor(extraSct);
+ return compiler.compileGlobalScript(scopeKind);
+}
+
+JSScript*
+frontend::CompileEvalScript(ExclusiveContext* cx, LifoAlloc& alloc,
+ HandleObject environment, HandleScope enclosingScope,
+ const ReadOnlyCompileOptions& options,
+ SourceBufferHolder& srcBuf,
+ SourceCompressionTask* extraSct,
+ ScriptSourceObject** sourceObjectOut)
+{
+ BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope,
+ TraceLogger_ParserCompileScript);
+ AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
+ compiler.maybeSetSourceCompressor(extraSct);
+ return compiler.compileEvalScript(environment, enclosingScope);
+}
+
+ModuleObject*
+frontend::CompileModule(ExclusiveContext* cx, const ReadOnlyCompileOptions& optionsInput,
+ SourceBufferHolder& srcBuf, LifoAlloc& alloc,
+ ScriptSourceObject** sourceObjectOut /* = nullptr */)
+{
+ MOZ_ASSERT(srcBuf.get());
+ MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
+
+ CompileOptions options(cx, optionsInput);
+ options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
+ options.setIsRunOnce(true);
+
+ RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
+ BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope,
+ TraceLogger_ParserCompileModule);
+ AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
+ return compiler.compileModule();
+}
+
+ModuleObject*
+frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceBufferHolder& srcBuf)
+{
+ if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
+ return nullptr;
+
+ LifoAlloc& alloc = cx->asJSContext()->tempLifoAlloc();
+ RootedModuleObject module(cx, CompileModule(cx, options, srcBuf, alloc));
+ if (!module)
+ return nullptr;
+
+ // This happens in GlobalHelperThreadState::finishModuleParseTask() when a
+ // module is compiled off main thread.
+ if (!ModuleObject::Freeze(cx->asJSContext(), module))
+ return nullptr;
+
+ return module;
+}
+
+bool
+frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
+{
+ MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
+
+ CompileOptions options(cx, lazy->version());
+ options.setMutedErrors(lazy->mutedErrors())
+ .setFileAndLine(lazy->filename(), lazy->lineno())
+ .setColumn(lazy->column())
+ .setNoScriptRval(false)
+ .setSelfHostingMode(false);
+
+ AutoCompilationTraceLogger traceLogger(cx, TraceLogger_ParserCompileLazy, options);
+
+ UsedNameTracker usedNames(cx);
+ if (!usedNames.init())
+ return false;
+ Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
+ /* foldConstants = */ true, usedNames, nullptr, lazy);
+ if (!parser.checkOptions())
+ return false;
+
+ Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
+ MOZ_ASSERT(!lazy->isLegacyGenerator());
+ ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->strict(), lazy->generatorKind(),
+ lazy->asyncKind());
+ if (!pn)
+ return false;
+
+ if (!NameFunctions(cx, pn))
+ return false;
+
+ RootedScriptSource sourceObject(cx, lazy->sourceObject());
+ MOZ_ASSERT(sourceObject);
+
+ Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
+ lazy->begin(), lazy->end()));
+ if (!script)
+ return false;
+
+ if (lazy->isLikelyConstructorWrapper())
+ script->setLikelyConstructorWrapper();
+ if (lazy->hasBeenCloned())
+ script->setHasBeenCloned();
+
+ BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
+ pn->pn_pos, BytecodeEmitter::LazyFunction);
+ if (!bce.init())
+ return false;
+
+ return bce.emitFunctionScript(pn->pn_body);
+}
+
+// Compile a JS function body, which might appear as the value of an event
+// handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
+static bool
+CompileFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyCompileOptions& options,
+ Handle<PropertyNameVector> formals, SourceBufferHolder& srcBuf,
+ HandleScope enclosingScope, GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind)
+{
+ MOZ_ASSERT(!options.isRunOnce);
+
+ // FIXME: make Function pass in two strings and parse them as arguments and
+ // ProgramElements respectively.
+
+ BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, enclosingScope,
+ TraceLogger_ParserCompileFunction);
+ compiler.setSourceArgumentsNotIncluded();
+ return compiler.compileFunctionBody(fun, formals, generatorKind, asyncKind);
+}
+
+bool
+frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun,
+ const ReadOnlyCompileOptions& options,
+ Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf,
+ HandleScope enclosingScope)
+{
+ return CompileFunctionBody(cx, fun, options, formals, srcBuf, enclosingScope, NotGenerator,
+ SyncFunction);
+}
+
+bool
+frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun,
+ const ReadOnlyCompileOptions& options,
+ Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf)
+{
+ RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
+ return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope,
+ NotGenerator, SyncFunction);
+}
+
+bool
+frontend::CompileStarGeneratorBody(JSContext* cx, MutableHandleFunction fun,
+ const ReadOnlyCompileOptions& options,
+ Handle<PropertyNameVector> formals,
+ JS::SourceBufferHolder& srcBuf)
+{
+ RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
+ return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope,
+ StarGenerator, SyncFunction);
+}
+
+bool
+frontend::CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun,
+ const ReadOnlyCompileOptions& options,
+ Handle<PropertyNameVector> formals,
+ JS::SourceBufferHolder& srcBuf)
+{
+ RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
+ return CompileFunctionBody(cx, fun, options, formals, srcBuf, emptyGlobalScope,
+ StarGenerator, AsyncFunction);
+}