michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "frontend/BytecodeCompiler.h" michael@0: michael@0: #include "jscntxt.h" michael@0: #include "jsscript.h" michael@0: michael@0: #include "frontend/BytecodeEmitter.h" michael@0: #include "frontend/FoldConstants.h" michael@0: #include "frontend/NameFunctions.h" michael@0: #include "frontend/Parser.h" michael@0: #include "jit/AsmJSLink.h" michael@0: #include "vm/GlobalObject.h" michael@0: #include "vm/TraceLogging.h" michael@0: michael@0: #include "jsobjinlines.h" michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "frontend/Parser-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::frontend; michael@0: using mozilla::Maybe; michael@0: michael@0: static bool michael@0: CheckLength(ExclusiveContext *cx, SourceBufferHolder &srcBuf) michael@0: { michael@0: // Note this limit is simply so we can store sourceStart and sourceEnd in michael@0: // JSScript as 32-bits. It could be lifted fairly easily, since the compiler michael@0: // is using size_t internally already. michael@0: if (srcBuf.length() > UINT32_MAX) { michael@0: if (cx->isJSContext()) michael@0: JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr, michael@0: JSMSG_SOURCE_TOO_LONG); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: SetDisplayURL(ExclusiveContext *cx, TokenStream &tokenStream, ScriptSource *ss) michael@0: { michael@0: if (tokenStream.hasDisplayURL()) { michael@0: if (!ss->setDisplayURL(cx, tokenStream.displayURL())) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: SetSourceMap(ExclusiveContext *cx, TokenStream &tokenStream, ScriptSource *ss) michael@0: { michael@0: if (tokenStream.hasSourceMapURL()) { michael@0: if (!ss->setSourceMapURL(cx, tokenStream.sourceMapURL())) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: CheckArgumentsWithinEval(JSContext *cx, Parser &parser, HandleFunction fun) michael@0: { michael@0: if (fun->hasRest()) { michael@0: // It's an error to use |arguments| in a function that has a rest michael@0: // parameter. michael@0: parser.report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST); michael@0: return false; michael@0: } michael@0: michael@0: // Force construction of arguments objects for functions that use michael@0: // |arguments| within an eval. michael@0: RootedScript script(cx, fun->getOrCreateScript(cx)); michael@0: if (!script) michael@0: return false; michael@0: if (script->argumentsHasVarBinding()) { michael@0: if (!JSScript::argumentsOptimizationFailed(cx, script)) michael@0: return false; michael@0: } michael@0: michael@0: // It's an error to use |arguments| in a legacy generator expression. michael@0: if (script->isGeneratorExp() && script->isLegacyGenerator()) { michael@0: parser.report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: MaybeCheckEvalFreeVariables(ExclusiveContext *cxArg, HandleScript evalCaller, HandleObject scopeChain, michael@0: Parser &parser, michael@0: ParseContext &pc) michael@0: { michael@0: if (!evalCaller || !evalCaller->functionOrCallerFunction()) michael@0: return true; michael@0: michael@0: // Eval scripts are only compiled on the main thread. michael@0: JSContext *cx = cxArg->asJSContext(); michael@0: michael@0: // Watch for uses of 'arguments' within the evaluated script, both as michael@0: // free variables and as variables redeclared with 'var'. michael@0: RootedFunction fun(cx, evalCaller->functionOrCallerFunction()); michael@0: HandlePropertyName arguments = cx->names().arguments; michael@0: for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) { michael@0: if (r.front().key() == arguments) { michael@0: if (!CheckArgumentsWithinEval(cx, parser, fun)) michael@0: return false; michael@0: } michael@0: } michael@0: for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) { michael@0: if (r.front().key() == arguments) { michael@0: if (!CheckArgumentsWithinEval(cx, parser, fun)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // If the eval'ed script contains any debugger statement, force construction michael@0: // of arguments objects for the caller script and any other scripts it is michael@0: // transitively nested inside. The debugger can access any variable on the michael@0: // scope chain. michael@0: if (pc.sc->hasDebuggerStatement()) { michael@0: RootedObject scope(cx, scopeChain); michael@0: while (scope->is() || scope->is()) { michael@0: if (scope->is() && !scope->as().isForEval()) { michael@0: RootedScript script(cx, scope->as().callee().getOrCreateScript(cx)); michael@0: if (!script) michael@0: return false; michael@0: if (script->argumentsHasVarBinding()) { michael@0: if (!JSScript::argumentsOptimizationFailed(cx, script)) michael@0: return false; michael@0: } michael@0: } michael@0: scope = scope->enclosingScope(); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static inline bool michael@0: CanLazilyParse(ExclusiveContext *cx, const ReadOnlyCompileOptions &options) michael@0: { michael@0: return options.canLazilyParse && michael@0: options.compileAndGo && michael@0: !cx->compartment()->options().discardSource() && michael@0: !options.sourceIsLazy && michael@0: !(cx->compartment()->debugMode() && michael@0: cx->compartment()->runtimeFromAnyThread()->debugHooks.newScriptHook); michael@0: } michael@0: michael@0: static void michael@0: MarkFunctionsWithinEvalScript(JSScript *script) michael@0: { michael@0: // Mark top level functions in an eval script as being within an eval and, michael@0: // if applicable, inside a with statement. michael@0: michael@0: if (!script->hasObjects()) michael@0: return; michael@0: michael@0: ObjectArray *objects = script->objects(); michael@0: size_t start = script->innerObjectsStart(); michael@0: michael@0: for (size_t i = start; i < objects->length; i++) { michael@0: JSObject *obj = objects->vector[i]; michael@0: if (obj->is()) { michael@0: JSFunction *fun = &obj->as(); michael@0: if (fun->hasScript()) michael@0: fun->nonLazyScript()->setDirectlyInsideEval(); michael@0: else if (fun->isInterpretedLazy()) michael@0: fun->lazyScript()->setDirectlyInsideEval(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: frontend::MaybeCallSourceHandler(JSContext *cx, const ReadOnlyCompileOptions &options, michael@0: SourceBufferHolder &srcBuf) michael@0: { michael@0: JSSourceHandler listener = cx->runtime()->debugHooks.sourceHandler; michael@0: void *listenerData = cx->runtime()->debugHooks.sourceHandlerData; michael@0: michael@0: if (listener) { michael@0: void *listenerTSData; michael@0: listener(options.filename(), options.lineno, srcBuf.get(), srcBuf.length(), michael@0: &listenerTSData, listenerData); michael@0: } michael@0: } michael@0: michael@0: ScriptSourceObject * michael@0: frontend::CreateScriptSourceObject(ExclusiveContext *cx, const ReadOnlyCompileOptions &options) michael@0: { michael@0: ScriptSource *ss = cx->new_(); michael@0: if (!ss) michael@0: return nullptr; michael@0: ScriptSourceHolder ssHolder(ss); michael@0: michael@0: if (!ss->initFromOptions(cx, options)) michael@0: return nullptr; michael@0: michael@0: return ScriptSourceObject::create(cx, ss, options); michael@0: } michael@0: michael@0: JSScript * michael@0: frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain, michael@0: HandleScript evalCaller, michael@0: const ReadOnlyCompileOptions &options, michael@0: SourceBufferHolder &srcBuf, michael@0: JSString *source_ /* = nullptr */, michael@0: unsigned staticLevel /* = 0 */, michael@0: SourceCompressionTask *extraSct /* = nullptr */) michael@0: { michael@0: JS_ASSERT(srcBuf.get()); michael@0: michael@0: RootedString source(cx, source_); michael@0: michael@0: js::TraceLogger *logger = nullptr; michael@0: if (cx->isJSContext()) michael@0: logger = TraceLoggerForMainThread(cx->asJSContext()->runtime()); michael@0: else michael@0: logger = TraceLoggerForCurrentThread(); michael@0: uint32_t logId = js::TraceLogCreateTextId(logger, options); michael@0: js::AutoTraceLog scriptLogger(logger, logId); michael@0: js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileScript); michael@0: michael@0: if (cx->isJSContext()) michael@0: MaybeCallSourceHandler(cx->asJSContext(), options, srcBuf); michael@0: michael@0: /* michael@0: * The scripted callerFrame can only be given for compile-and-go scripts michael@0: * and non-zero static level requires callerFrame. michael@0: */ michael@0: JS_ASSERT_IF(evalCaller, options.compileAndGo); michael@0: JS_ASSERT_IF(evalCaller, options.forEval); michael@0: JS_ASSERT_IF(staticLevel != 0, evalCaller); michael@0: michael@0: if (!CheckLength(cx, srcBuf)) michael@0: return nullptr; michael@0: JS_ASSERT_IF(staticLevel != 0, !options.sourceIsLazy); michael@0: michael@0: RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options)); michael@0: if (!sourceObject) michael@0: return nullptr; michael@0: michael@0: ScriptSource *ss = sourceObject->source(); michael@0: michael@0: SourceCompressionTask mysct(cx); michael@0: SourceCompressionTask *sct = extraSct ? extraSct : &mysct; michael@0: michael@0: if (!cx->compartment()->options().discardSource()) { michael@0: if (options.sourceIsLazy) michael@0: ss->setSourceRetrievable(); michael@0: else if (!ss->setSourceCopy(cx, srcBuf, false, sct)) michael@0: return nullptr; michael@0: } michael@0: michael@0: bool canLazilyParse = CanLazilyParse(cx, options); michael@0: michael@0: Maybe > syntaxParser; michael@0: if (canLazilyParse) { michael@0: syntaxParser.construct(cx, alloc, options, srcBuf.get(), srcBuf.length(), michael@0: /* foldConstants = */ false, michael@0: (Parser *) nullptr, michael@0: (LazyScript *) nullptr); michael@0: } michael@0: michael@0: Parser parser(cx, alloc, options, srcBuf.get(), srcBuf.length(), michael@0: /* foldConstants = */ true, michael@0: canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr); michael@0: parser.sct = sct; michael@0: parser.ss = ss; michael@0: michael@0: Directives directives(options.strictOption); michael@0: GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); michael@0: michael@0: bool savedCallerFun = options.compileAndGo && michael@0: evalCaller && evalCaller->functionOrCallerFunction(); michael@0: Rooted script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, michael@0: options, staticLevel, sourceObject, 0, michael@0: srcBuf.length())); michael@0: if (!script) michael@0: return nullptr; michael@0: michael@0: // We can specialize a bit for the given scope chain if that scope chain is the global object. michael@0: JSObject *globalScope = michael@0: scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr; michael@0: JS_ASSERT_IF(globalScope, globalScope->isNative()); michael@0: JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); michael@0: michael@0: BytecodeEmitter::EmitterMode emitterMode = michael@0: options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal; michael@0: BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script, options.forEval, michael@0: evalCaller, !!globalScope, options.lineno, emitterMode); michael@0: if (!bce.init()) michael@0: return nullptr; michael@0: michael@0: // Syntax parsing may cause us to restart processing of top level michael@0: // statements in the script. Use Maybe<> so that the parse context can be michael@0: // reset when this occurs. michael@0: Maybe > pc; michael@0: michael@0: pc.construct(&parser, (GenericParseContext *) nullptr, (ParseNode *) nullptr, &globalsc, michael@0: (Directives *) nullptr, staticLevel, /* bodyid = */ 0, michael@0: /* blockScopeDepth = */ 0); michael@0: if (!pc.ref().init(parser.tokenStream)) michael@0: return nullptr; michael@0: michael@0: /* If this is a direct call to eval, inherit the caller's strictness. */ michael@0: if (evalCaller && evalCaller->strict()) michael@0: globalsc.strict = true; michael@0: michael@0: if (options.compileAndGo) { michael@0: if (source) { michael@0: /* michael@0: * Save eval program source in script->atoms[0] for the michael@0: * eval cache (see EvalCacheLookup in jsobj.cpp). michael@0: */ michael@0: JSAtom *atom = AtomizeString(cx, source); michael@0: jsatomid _; michael@0: if (!atom || !bce.makeAtomIndex(atom, &_)) michael@0: return nullptr; michael@0: } michael@0: michael@0: if (evalCaller && evalCaller->functionOrCallerFunction()) { michael@0: /* michael@0: * An eval script in a caller frame needs to have its enclosing michael@0: * function captured in case it refers to an upvar, and someone michael@0: * wishes to decompile it while it's running. michael@0: */ michael@0: JSFunction *fun = evalCaller->functionOrCallerFunction(); michael@0: Directives directives(/* strict = */ fun->strict()); michael@0: ObjectBox *funbox = parser.newFunctionBox(/* fn = */ nullptr, fun, pc.addr(), michael@0: directives, fun->generatorKind()); michael@0: if (!funbox) michael@0: return nullptr; michael@0: bce.objectList.add(funbox); michael@0: } michael@0: } michael@0: michael@0: bool canHaveDirectives = true; michael@0: for (;;) { michael@0: TokenKind tt = parser.tokenStream.peekToken(TokenStream::Operand); michael@0: if (tt <= TOK_EOF) { michael@0: if (tt == TOK_EOF) michael@0: break; michael@0: JS_ASSERT(tt == TOK_ERROR); michael@0: return nullptr; michael@0: } michael@0: michael@0: TokenStream::Position pos(parser.keepAtoms); michael@0: parser.tokenStream.tell(&pos); michael@0: michael@0: ParseNode *pn = parser.statement(canHaveDirectives); michael@0: if (!pn) { michael@0: if (parser.hadAbortedSyntaxParse()) { michael@0: // Parsing inner functions lazily may lead the parser into an michael@0: // unrecoverable state and may require starting over on the top michael@0: // level statement. Restart the parse; syntax parsing has michael@0: // already been disabled for the parser and the result will not michael@0: // be ambiguous. michael@0: parser.clearAbortedSyntaxParse(); michael@0: parser.tokenStream.seek(pos); michael@0: michael@0: // Destroying the parse context will destroy its free michael@0: // variables, so check if any deoptimization is needed. michael@0: if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) michael@0: return nullptr; michael@0: michael@0: pc.destroy(); michael@0: pc.construct(&parser, (GenericParseContext *) nullptr, (ParseNode *) nullptr, michael@0: &globalsc, (Directives *) nullptr, staticLevel, /* bodyid = */ 0, michael@0: script->bindings.numBlockScoped()); michael@0: if (!pc.ref().init(parser.tokenStream)) michael@0: return nullptr; michael@0: JS_ASSERT(parser.pc == pc.addr()); michael@0: michael@0: pn = parser.statement(); michael@0: } michael@0: if (!pn) { michael@0: JS_ASSERT(!parser.hadAbortedSyntaxParse()); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: // Accumulate the maximum block scope depth, so that EmitTree can assert michael@0: // when emitting JSOP_GETLOCAL that the local is indeed within the fixed michael@0: // part of the stack frame. michael@0: script->bindings.updateNumBlockScoped(pc.ref().blockScopeDepth); michael@0: michael@0: if (canHaveDirectives) { michael@0: if (!parser.maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives)) michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!FoldConstants(cx, &pn, &parser)) michael@0: return nullptr; michael@0: michael@0: if (!NameFunctions(cx, pn)) michael@0: return nullptr; michael@0: michael@0: if (!EmitTree(cx, &bce, pn)) michael@0: return nullptr; michael@0: michael@0: parser.handler.freeTree(pn); michael@0: } michael@0: michael@0: if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) michael@0: return nullptr; michael@0: michael@0: if (!SetDisplayURL(cx, parser.tokenStream, ss)) michael@0: return nullptr; michael@0: michael@0: if (!SetSourceMap(cx, parser.tokenStream, ss)) michael@0: return nullptr; michael@0: michael@0: /* michael@0: * Source map URLs passed as a compile option (usually via a HTTP source map michael@0: * header) override any source map urls passed as comment pragmas. michael@0: */ michael@0: if (options.sourceMapURL()) { michael@0: if (!ss->setSourceMapURL(cx, options.sourceMapURL())) michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * Nowadays the threaded interpreter needs a last return instruction, so we michael@0: * do have to emit that here. michael@0: */ michael@0: if (Emit1(cx, &bce, JSOP_RETRVAL) < 0) michael@0: return nullptr; michael@0: michael@0: // Global/eval script bindings are always empty (all names are added to the michael@0: // scope dynamically via JSOP_DEFFUN/VAR). They may have block-scoped michael@0: // locals, however, which are allocated to the fixed part of the stack michael@0: // frame. michael@0: InternalHandle bindings(script, &script->bindings); michael@0: if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, nullptr, michael@0: pc.ref().blockScopeDepth)) michael@0: return nullptr; michael@0: michael@0: if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) michael@0: return nullptr; michael@0: michael@0: // Note that this marking must happen before we tell Debugger michael@0: // about the new script, in case Debugger delazifies the script's michael@0: // inner functions. michael@0: if (options.forEval) michael@0: MarkFunctionsWithinEvalScript(script); michael@0: michael@0: bce.tellDebuggerAboutCompiledScript(cx); michael@0: michael@0: if (sct && !extraSct && !sct->complete()) michael@0: return nullptr; michael@0: michael@0: return script; michael@0: } michael@0: michael@0: bool michael@0: frontend::CompileLazyFunction(JSContext *cx, Handle lazy, const jschar *chars, size_t length) michael@0: { michael@0: JS_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment()); michael@0: michael@0: CompileOptions options(cx, lazy->version()); michael@0: options.setOriginPrincipals(lazy->originPrincipals()) michael@0: .setFileAndLine(lazy->source()->filename(), lazy->lineno()) michael@0: .setColumn(lazy->column()) michael@0: .setCompileAndGo(true) michael@0: .setNoScriptRval(false) michael@0: .setSelfHostingMode(false); michael@0: michael@0: js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime()); michael@0: uint32_t logId = js::TraceLogCreateTextId(logger, options); michael@0: js::AutoTraceLog scriptLogger(logger, logId); michael@0: js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileLazy); michael@0: michael@0: Parser parser(cx, &cx->tempLifoAlloc(), options, chars, length, michael@0: /* foldConstants = */ true, nullptr, lazy); michael@0: michael@0: uint32_t staticLevel = lazy->staticLevel(cx); michael@0: michael@0: Rooted fun(cx, lazy->functionNonDelazifying()); michael@0: JS_ASSERT(!lazy->isLegacyGenerator()); michael@0: ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(), michael@0: lazy->generatorKind()); michael@0: if (!pn) michael@0: return false; michael@0: michael@0: if (!NameFunctions(cx, pn)) michael@0: return false; michael@0: michael@0: RootedObject enclosingScope(cx, lazy->enclosingScope()); michael@0: RootedScriptSource sourceObject(cx, lazy->sourceObject()); michael@0: JS_ASSERT(sourceObject); michael@0: michael@0: Rooted script(cx, JSScript::Create(cx, enclosingScope, false, michael@0: options, staticLevel, michael@0: sourceObject, lazy->begin(), lazy->end())); michael@0: if (!script) michael@0: return false; michael@0: michael@0: script->bindings = pn->pn_funbox->bindings; michael@0: michael@0: if (lazy->directlyInsideEval()) michael@0: script->setDirectlyInsideEval(); michael@0: if (lazy->usesArgumentsAndApply()) michael@0: script->setUsesArgumentsAndApply(); michael@0: if (lazy->hasBeenCloned()) michael@0: script->setHasBeenCloned(); michael@0: michael@0: BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, options.forEval, michael@0: /* evalCaller = */ NullPtr(), /* hasGlobalScope = */ true, michael@0: options.lineno, BytecodeEmitter::LazyFunction); michael@0: if (!bce.init()) michael@0: return false; michael@0: michael@0: if (lazy->treatAsRunOnce()) michael@0: bce.lazyRunOnceLambda = true; michael@0: michael@0: return EmitFunctionScript(cx, &bce, pn->pn_body); michael@0: } michael@0: michael@0: // Compile a JS function body, which might appear as the value of an event michael@0: // handler attribute in an HTML tag, or in a Function() constructor. michael@0: static bool michael@0: CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyCompileOptions &options, michael@0: const AutoNameVector &formals, SourceBufferHolder &srcBuf, michael@0: GeneratorKind generatorKind) michael@0: { michael@0: js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime()); michael@0: uint32_t logId = js::TraceLogCreateTextId(logger, options); michael@0: js::AutoTraceLog scriptLogger(logger, logId); michael@0: js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileFunction); michael@0: michael@0: // FIXME: make Function pass in two strings and parse them as arguments and michael@0: // ProgramElements respectively. michael@0: michael@0: MaybeCallSourceHandler(cx, options, srcBuf); michael@0: michael@0: if (!CheckLength(cx, srcBuf)) michael@0: return false; michael@0: michael@0: RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options)); michael@0: if (!sourceObject) michael@0: return nullptr; michael@0: ScriptSource *ss = sourceObject->source(); michael@0: michael@0: SourceCompressionTask sct(cx); michael@0: JS_ASSERT(!options.sourceIsLazy); michael@0: if (!cx->compartment()->options().discardSource()) { michael@0: if (!ss->setSourceCopy(cx, srcBuf, true, &sct)) michael@0: return false; michael@0: } michael@0: michael@0: bool canLazilyParse = CanLazilyParse(cx, options); michael@0: michael@0: Maybe > syntaxParser; michael@0: if (canLazilyParse) { michael@0: syntaxParser.construct(cx, &cx->tempLifoAlloc(), michael@0: options, srcBuf.get(), srcBuf.length(), michael@0: /* foldConstants = */ false, michael@0: (Parser *) nullptr, michael@0: (LazyScript *) nullptr); michael@0: } michael@0: michael@0: JS_ASSERT(!options.forEval); michael@0: michael@0: Parser parser(cx, &cx->tempLifoAlloc(), michael@0: options, srcBuf.get(), srcBuf.length(), michael@0: /* foldConstants = */ true, michael@0: canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr); michael@0: parser.sct = &sct; michael@0: parser.ss = ss; michael@0: michael@0: JS_ASSERT(fun); michael@0: JS_ASSERT(fun->isTenured()); michael@0: michael@0: fun->setArgCount(formals.length()); michael@0: michael@0: // Speculatively parse using the default directives implied by the context. michael@0: // If a directive is encountered (e.g., "use strict") that changes how the michael@0: // function should have been parsed, we backup and reparse with the new set michael@0: // of directives. michael@0: Directives directives(options.strictOption); michael@0: michael@0: TokenStream::Position start(parser.keepAtoms); michael@0: parser.tokenStream.tell(&start); michael@0: michael@0: ParseNode *fn; michael@0: while (true) { michael@0: Directives newDirectives = directives; michael@0: fn = parser.standaloneFunctionBody(fun, formals, generatorKind, directives, &newDirectives); michael@0: if (fn) michael@0: break; michael@0: michael@0: if (parser.hadAbortedSyntaxParse()) { michael@0: // Hit some unrecoverable ambiguity during an inner syntax parse. michael@0: // Syntax parsing has now been disabled in the parser, so retry michael@0: // the parse. michael@0: parser.clearAbortedSyntaxParse(); michael@0: } else { michael@0: if (parser.tokenStream.hadError() || directives == newDirectives) michael@0: return false; michael@0: michael@0: // Assignment must be monotonic to prevent reparsing iloops michael@0: JS_ASSERT_IF(directives.strict(), newDirectives.strict()); michael@0: JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); michael@0: directives = newDirectives; michael@0: } michael@0: michael@0: parser.tokenStream.seek(start); michael@0: } michael@0: michael@0: if (!NameFunctions(cx, fn)) michael@0: return false; michael@0: michael@0: if (fn->pn_funbox->function()->isInterpreted()) { michael@0: JS_ASSERT(fun == fn->pn_funbox->function()); michael@0: michael@0: Rooted script(cx, JSScript::Create(cx, js::NullPtr(), false, options, michael@0: /* staticLevel = */ 0, sourceObject, michael@0: /* sourceStart = */ 0, srcBuf.length())); michael@0: if (!script) michael@0: return false; michael@0: michael@0: script->bindings = fn->pn_funbox->bindings; michael@0: michael@0: /* michael@0: * The reason for checking fun->environment() below is that certain michael@0: * consumers of JS::CompileFunction, namely michael@0: * EventListenerManager::CompileEventHandlerInternal, passes in a michael@0: * nullptr environment. This compiled function is never used, but michael@0: * instead is cloned immediately onto the right scope chain. michael@0: */ michael@0: BytecodeEmitter funbce(/* parent = */ nullptr, &parser, fn->pn_funbox, script, michael@0: /* insideEval = */ false, /* evalCaller = */ js::NullPtr(), michael@0: fun->environment() && fun->environment()->is(), michael@0: options.lineno); michael@0: if (!funbce.init()) michael@0: return false; michael@0: michael@0: if (!EmitFunctionScript(cx, &funbce, fn->pn_body)) michael@0: return false; michael@0: } else { michael@0: fun.set(fn->pn_funbox->function()); michael@0: JS_ASSERT(IsAsmJSModuleNative(fun->native())); michael@0: } michael@0: michael@0: if (!SetDisplayURL(cx, parser.tokenStream, ss)) michael@0: return false; michael@0: michael@0: if (!SetSourceMap(cx, parser.tokenStream, ss)) michael@0: return false; michael@0: michael@0: if (!sct.complete()) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, michael@0: const ReadOnlyCompileOptions &options, michael@0: const AutoNameVector &formals, JS::SourceBufferHolder &srcBuf) michael@0: { michael@0: return CompileFunctionBody(cx, fun, options, formals, srcBuf, NotGenerator); michael@0: } michael@0: michael@0: bool michael@0: frontend::CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun, michael@0: const ReadOnlyCompileOptions &options, const AutoNameVector &formals, michael@0: JS::SourceBufferHolder &srcBuf) michael@0: { michael@0: return CompileFunctionBody(cx, fun, options, formals, srcBuf, StarGenerator); michael@0: }