blob: 22519caa8ad625e34dd3688339a8e8b316367d37 [file] [log] [blame]
/* -*- 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 "jscntxt.h"
#include "jsscript.h"
#include "asmjs/AsmJSLink.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 "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "frontend/Parser-inl.h"
#include "vm/ScopeObject-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,
Handle<ScopeObject*> enclosingStaticScope,
TraceLoggerTextId logId);
// Call setters for optional arguments.
void maybeSetSourceCompressor(SourceCompressionTask* sourceCompressor);
void setSourceArgumentsNotIncluded();
JSScript* compileScript(HandleObject scopeChain, HandleScript evalCaller);
ModuleObject* compileModule();
bool compileFunctionBody(MutableHandleFunction fun, Handle<PropertyNameVector> formals,
GeneratorKind generatorKind);
ScriptSourceObject* sourceObjectPtr() const;
private:
bool checkLength();
bool createScriptSource();
bool maybeCompressSource();
bool canLazilyParse();
bool createParser();
bool createSourceAndParser();
bool createScript(HandleObject staticScope, bool savedCallerFun = false);
bool createEmitter(SharedContext* sharedContext, HandleScript evalCaller = nullptr,
bool insideNonGlobalEval = false);
bool isEvalCompilationUnit();
bool isNonGlobalEvalCompilationUnit();
bool isNonSyntacticCompilationUnit();
bool saveCallerFun(HandleScript evalCaller);
bool handleParseFailure(const Directives& newDirectives);
bool prepareAndEmitTree(ParseNode** pn);
bool checkArgumentsWithinEval(JSContext* cx, HandleFunction fun);
bool maybeCheckEvalFreeVariables(HandleScript evalCaller, HandleObject scopeChain,
ParseContext<FullParseHandler>& pc);
bool maybeSetDisplayURL(TokenStream& tokenStream);
bool maybeSetSourceMap(TokenStream& tokenStream);
bool maybeSetSourceMapFromOptions();
bool emitFinalReturn();
bool initGlobalOrEvalBindings(ParseContext<FullParseHandler>& pc);
bool maybeCompleteCompressSource();
AutoCompilationTraceLogger traceLogger;
AutoKeepAtoms keepAtoms;
ExclusiveContext* cx;
LifoAlloc* alloc;
const ReadOnlyCompileOptions& options;
SourceBufferHolder& sourceBuffer;
Rooted<ScopeObject*> enclosingStaticScope;
bool sourceArgumentsNotIncluded;
RootedScriptSource sourceObject;
ScriptSource* scriptSource;
Maybe<SourceCompressionTask> maybeSourceCompressor;
SourceCompressionTask* sourceCompressor;
Maybe<Parser<SyntaxParseHandler>> syntaxParser;
Maybe<Parser<FullParseHandler>> parser;
Directives directives;
TokenStream::Position startPosition;
RootedScript script;
Maybe<BytecodeEmitter> emitter;
};
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,
Handle<ScopeObject*> enclosingStaticScope,
TraceLoggerTextId logId)
: traceLogger(cx, logId, options),
keepAtoms(cx->perThreadData),
cx(cx),
alloc(alloc),
options(options),
sourceBuffer(sourceBuffer),
enclosingStaticScope(cx, enclosingStaticScope),
sourceArgumentsNotIncluded(false),
sourceObject(cx),
scriptSource(nullptr),
sourceCompressor(nullptr),
directives(options.strictOption),
startPosition(keepAtoms),
script(cx)
{
}
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_ReportErrorNumber(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()->options().discardSource()) {
if (options.sourceIsLazy) {
scriptSource->setSourceRetrievable();
} else if (!scriptSource->setSourceCopy(cx, sourceBuffer, sourceArgumentsNotIncluded,
sourceCompressor))
{
return false;
}
}
return true;
}
bool
BytecodeCompiler::canLazilyParse()
{
return options.canLazilyParse &&
!HasNonSyntacticStaticScopeChain(enclosingStaticScope) &&
!cx->compartment()->options().disableLazyParsing() &&
!cx->compartment()->options().discardSource() &&
!options.sourceIsLazy &&
!cx->lcovEnabled();
}
bool
BytecodeCompiler::createParser()
{
if (canLazilyParse()) {
syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
/* foldConstants = */ false, (Parser<SyntaxParseHandler>*) nullptr,
(LazyScript*) nullptr);
if (!syntaxParser->checkOptions())
return false;
}
parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
/* foldConstants = */ true, 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(HandleObject staticScope, bool savedCallerFun)
{
script = JSScript::Create(cx, staticScope, savedCallerFun, options,
sourceObject, /* sourceStart = */ 0, sourceBuffer.length());
return script != nullptr;
}
bool
BytecodeCompiler::createEmitter(SharedContext* sharedContext, HandleScript evalCaller,
bool insideNonGlobalEval)
{
BytecodeEmitter::EmitterMode emitterMode =
options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
emitter.emplace(/* parent = */ nullptr, parser.ptr(), sharedContext, script,
/* lazyScript = */ nullptr, options.forEval, evalCaller,
insideNonGlobalEval, options.lineno, emitterMode);
return emitter->init();
}
bool
BytecodeCompiler::isEvalCompilationUnit()
{
return enclosingStaticScope->is<StaticEvalObject>();
}
bool
BytecodeCompiler::isNonGlobalEvalCompilationUnit()
{
if (!isEvalCompilationUnit())
return false;
StaticEvalObject& eval = enclosingStaticScope->as<StaticEvalObject>();
JSObject* enclosing = eval.enclosingScopeForStaticScopeIter();
return !IsStaticGlobalLexicalScope(enclosing);
}
bool
BytecodeCompiler::isNonSyntacticCompilationUnit()
{
return enclosingStaticScope->is<StaticNonSyntacticScopeObjects>();
}
bool
BytecodeCompiler::saveCallerFun(HandleScript evalCaller)
{
/*
* An eval script in a caller frame needs to have its enclosing
* function captured in case it refers to an upvar, and someone
* wishes to decompile it while it's running.
*
* This ends up as script->objects()->vector[0] in the compiled script.
*/
RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
MOZ_ASSERT_IF(fun->strict(), options.strictOption);
Directives directives(/* strict = */ options.strictOption);
ObjectBox* funbox = parser->newFunctionBox(/* fn = */ nullptr, fun,
directives, fun->generatorKind(),
enclosingStaticScope);
if (!funbox)
return false;
emitter->objectList.add(funbox);
return true;
}
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::prepareAndEmitTree(ParseNode** ppn)
{
if (!FoldConstants(cx, ppn, parser.ptr()) ||
!NameFunctions(cx, *ppn) ||
!emitter->updateLocalsToFrameSlots() ||
!emitter->emitTree(*ppn))
{
return false;
}
return true;
}
bool
BytecodeCompiler::maybeSetDisplayURL(TokenStream& tokenStream)
{
if (tokenStream.hasDisplayURL()) {
if (!scriptSource->setDisplayURL(cx, tokenStream.displayURL()))
return false;
}
return true;
}
bool
BytecodeCompiler::maybeSetSourceMap(TokenStream& tokenStream)
{
if (tokenStream.hasSourceMapURL()) {
MOZ_ASSERT(!scriptSource->hasSourceMapURL());
if (!scriptSource->setSourceMapURL(cx, tokenStream.sourceMapURL()))
return false;
}
return true;
}
bool
BytecodeCompiler::maybeSetSourceMapFromOptions()
{
/*
* Source map URLs passed as a compile option (usually via a HTTP source map
* header) override any source map urls passed as comment pragmas.
*/
if (options.sourceMapURL()) {
// Warn about the replacement, but use the new one.
if (scriptSource->hasSourceMapURL()) {
if(!parser->report(ParseWarning, false, nullptr, JSMSG_ALREADY_HAS_PRAGMA,
scriptSource->filename(), "//# sourceMappingURL"))
return false;
}
if (!scriptSource->setSourceMapURL(cx, options.sourceMapURL()))
return false;
}
return true;
}
bool
BytecodeCompiler::checkArgumentsWithinEval(JSContext* cx, HandleFunction fun)
{
RootedScript script(cx, fun->getOrCreateScript(cx));
if (!script)
return false;
// It's an error to use |arguments| in a legacy generator expression.
if (script->isGeneratorExp() && script->isLegacyGenerator()) {
parser->report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
return false;
}
return true;
}
bool
BytecodeCompiler::maybeCheckEvalFreeVariables(HandleScript evalCaller, HandleObject scopeChain,
ParseContext<FullParseHandler>& pc)
{
if (!evalCaller || !evalCaller->functionOrCallerFunction())
return true;
// Eval scripts are only compiled on the main thread.
JSContext* cx = this->cx->asJSContext();
// Watch for uses of 'arguments' within the evaluated script, both as
// free variables and as variables redeclared with 'var'.
RootedFunction fun(cx, evalCaller->functionOrCallerFunction());
HandlePropertyName arguments = cx->names().arguments;
for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
if (r.front().key() == arguments) {
if (!checkArgumentsWithinEval(cx, fun))
return false;
}
}
for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) {
if (r.front().key() == arguments) {
if (!checkArgumentsWithinEval(cx, fun))
return false;
}
}
// 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 (pc.sc->hasDebuggerStatement()) {
RootedObject scope(cx, scopeChain);
while (scope->is<ScopeObject>() || scope->is<DebugScopeObject>()) {
if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
RootedScript script(cx, scope->as<CallObject>().callee().getOrCreateScript(cx));
if (!script)
return false;
if (script->argumentsHasVarBinding()) {
if (!JSScript::argumentsOptimizationFailed(cx, script))
return false;
}
}
scope = scope->enclosingScope();
}
}
return true;
}
bool
BytecodeCompiler::emitFinalReturn()
{
/*
* Nowadays the threaded interpreter needs a last return instruction, so we
* do have to emit that here.
*/
return emitter->emit1(JSOP_RETRVAL);
}
bool
BytecodeCompiler::initGlobalOrEvalBindings(ParseContext<FullParseHandler>& pc)
{
Rooted<Bindings> bindings(cx, script->bindings);
if (!pc.generateBindings(cx, parser->tokenStream, *alloc, &bindings))
return false;
script->bindings = bindings;
return true;
}
bool
BytecodeCompiler::maybeCompleteCompressSource()
{
return !maybeSourceCompressor || maybeSourceCompressor->complete();
}
JSScript*
BytecodeCompiler::compileScript(HandleObject scopeChain, HandleScript evalCaller)
{
if (!createSourceAndParser())
return nullptr;
RootedFunction savedCallerFun(cx);
if (evalCaller)
savedCallerFun = evalCaller->functionOrCallerFunction();
if (!createScript(enclosingStaticScope, savedCallerFun))
return nullptr;
GlobalSharedContext globalsc(cx, enclosingStaticScope, directives, options.extraWarningsOption,
savedCallerFun);
if (!createEmitter(&globalsc, evalCaller, isNonGlobalEvalCompilationUnit()))
return nullptr;
if (savedCallerFun && !saveCallerFun(evalCaller))
return nullptr;
for (;;) {
ParseContext<FullParseHandler> pc(parser.ptr(),
/* parent = */ nullptr,
/* maybeFunction = */ nullptr,
&globalsc,
/* newDirectives = */ nullptr);
if (!pc.init(*parser))
return nullptr;
ParseNode* pn;
if (isEvalCompilationUnit())
pn = parser->evalBody();
else
pn = parser->globalBody();
// Successfully parsed. Emit the script.
if (pn) {
if (!initGlobalOrEvalBindings(pc))
return nullptr;
if (!maybeCheckEvalFreeVariables(evalCaller, scopeChain, pc))
return nullptr;
if (!prepareAndEmitTree(&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;
}
if (!maybeSetDisplayURL(parser->tokenStream) ||
!maybeSetSourceMap(parser->tokenStream) ||
!maybeSetSourceMapFromOptions() ||
!emitFinalReturn() ||
!JSScript::fullyInitFromEmitter(cx, script, emitter.ptr()))
{
return nullptr;
}
emitter->tellDebuggerAboutCompiledScript(cx);
if (!maybeCompleteCompressSource())
return nullptr;
MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
return script;
}
ModuleObject* BytecodeCompiler::compileModule()
{
if (!createSourceAndParser())
return nullptr;
Rooted<ModuleObject*> module(cx, ModuleObject::create(cx, enclosingStaticScope));
if (!module)
return nullptr;
if (!createScript(module))
return nullptr;
module->init(script);
ParseNode* pn = parser->standaloneModule(module);
if (!pn)
return nullptr;
if (!NameFunctions(cx, pn) ||
!maybeSetDisplayURL(parser->tokenStream) ||
!maybeSetSourceMap(parser->tokenStream))
{
return nullptr;
}
script->bindings = pn->pn_modulebox->bindings;
RootedModuleEnvironmentObject dynamicScope(cx, ModuleEnvironmentObject::create(cx, module));
if (!dynamicScope)
return nullptr;
module->setInitialEnvironment(dynamicScope);
if (!createEmitter(pn->pn_modulebox) ||
!emitter->emitModuleScript(pn->pn_body))
{
return nullptr;
}
ModuleBuilder builder(cx->asJSContext(), module);
if (!builder.buildAndInit(pn))
return nullptr;
parser->handler.freeTree(pn);
if (!maybeCompleteCompressSource())
return nullptr;
MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
return module;
}
bool
BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
Handle<PropertyNameVector> formals,
GeneratorKind generatorKind)
{
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, formals, generatorKind, directives,
&newDirectives, enclosingStaticScope);
if (!fn && !handleParseFailure(newDirectives))
return false;
} while (!fn);
if (!NameFunctions(cx, fn) ||
!maybeSetDisplayURL(parser->tokenStream) ||
!maybeSetSourceMap(parser->tokenStream))
{
return false;
}
if (fn->pn_funbox->function()->isInterpreted()) {
MOZ_ASSERT(fun == fn->pn_funbox->function());
if (!createScript(enclosingStaticScope))
return false;
script->bindings = fn->pn_funbox->bindings;
if (!createEmitter(fn->pn_funbox) ||
!emitter->emitFunctionScript(fn->pn_body))
{
return false;
}
} else {
fun.set(fn->pn_funbox->function());
MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
}
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;
}
JSScript*
frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject scopeChain,
Handle<ScopeObject*> enclosingStaticScope,
HandleScript evalCaller,
const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf,
JSString* source_ /* = nullptr */,
SourceCompressionTask* extraSct /* = nullptr */,
ScriptSourceObject** sourceObjectOut /* = nullptr */)
{
MOZ_ASSERT(srcBuf.get());
/*
* The scripted callerFrame can only be given for compile-and-go scripts
* and non-zero static level requires callerFrame.
*/
MOZ_ASSERT_IF(evalCaller, options.isRunOnce);
MOZ_ASSERT_IF(evalCaller, options.forEval);
MOZ_ASSERT_IF(evalCaller && evalCaller->strict(), options.strictOption);
MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingStaticScope,
TraceLogger_ParserCompileScript);
compiler.maybeSetSourceCompressor(extraSct);
JSScript* script = compiler.compileScript(scopeChain, evalCaller);
// frontend::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.
if (sourceObjectOut)
*sourceObjectOut = compiler.sourceObjectPtr();
return script;
}
ModuleObject*
frontend::CompileModule(JSContext* cx, HandleObject obj,
const ReadOnlyCompileOptions& optionsInput,
SourceBufferHolder& srcBuf)
{
MOZ_ASSERT(srcBuf.get());
CompileOptions options(cx, optionsInput);
options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
options.setIsRunOnce(true);
Rooted<ScopeObject*> staticScope(cx, &cx->global()->lexicalScope().staticBlock());
BytecodeCompiler compiler(cx, &cx->tempLifoAlloc(), options, srcBuf, staticScope,
TraceLogger_ParserCompileModule);
return compiler.compileModule();
}
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);
Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length,
/* foldConstants = */ true, 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());
if (!pn)
return false;
if (!NameFunctions(cx, pn))
return false;
RootedObject enclosingScope(cx, lazy->enclosingScope());
RootedScriptSource sourceObject(cx, lazy->sourceObject());
MOZ_ASSERT(sourceObject);
Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
sourceObject, lazy->begin(), lazy->end()));
if (!script)
return false;
script->bindings = pn->pn_funbox->bindings;
if (lazy->isLikelyConstructorWrapper())
script->setLikelyConstructorWrapper();
if (lazy->hasBeenCloned())
script->setHasBeenCloned();
/*
* We just pass false for insideNonGlobalEval and insideEval, because we
* don't actually know whether we are or not. The only consumer of those
* booleans is TryConvertFreeName, and it has special machinery to avoid
* doing bad things when a lazy function is inside eval.
*/
MOZ_ASSERT(!options.forEval);
BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
/* insideEval = */ false, /* evalCaller = */ nullptr,
/* insideNonGlobalEval = */ false, options.lineno,
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,
Handle<ScopeObject*> enclosingStaticScope, GeneratorKind generatorKind)
{
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, enclosingStaticScope,
TraceLogger_ParserCompileFunction);
compiler.setSourceArgumentsNotIncluded();
return compiler.compileFunctionBody(fun, formals, generatorKind);
}
bool
frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf,
Handle<ScopeObject*> enclosingStaticScope)
{
return CompileFunctionBody(cx, fun, options, formals, srcBuf,
enclosingStaticScope, NotGenerator);
}
bool
frontend::CompileFunctionBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf)
{
Rooted<ScopeObject*> staticLexical(cx, &cx->global()->lexicalScope().staticBlock());
return CompileFunctionBody(cx, fun, options, formals, srcBuf, staticLexical, NotGenerator);
}
bool
frontend::CompileStarGeneratorBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
Handle<PropertyNameVector> formals,
JS::SourceBufferHolder& srcBuf)
{
Rooted<ScopeObject*> staticLexical(cx, &cx->global()->lexicalScope().staticBlock());
return CompileFunctionBody(cx, fun, options, formals, srcBuf, staticLexical, StarGenerator);
}