// 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. bool frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length) { // FIXME: make Function pass in two strings and parse them as arguments and // ProgramElements respectively. SkipRoot skip(cx, &chars); MaybeCallSourceHandler(cx, options, chars, length); if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(options.originPrincipals()); if (!ss) return false; if (options.filename && !ss->setFilename(cx, options.filename)) return false; JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss)); if (!sourceObject) return false; SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.construct(cx, &cx->tempLifoAlloc(), options, chars, length, /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) NULL, (LazyScript *) NULL); } JS_ASSERT(!options.forEval); Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : NULL, NULL); parser.sct = &sct; parser.ss = ss; JS_ASSERT(fun); JS_ASSERT(fun->isTenured()); fun->setArgCount(formals.length()); // 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. Directives directives(options.strictOption); TokenStream::Position start(parser.keepAtoms); parser.tokenStream.tell(&start); ParseNode *fn; while (true) { Directives newDirectives = directives; fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives); if (fn) break; 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; // Assignment must be monotonic to prevent reparsing iloops JS_ASSERT_IF(directives.strict(), newDirectives.strict()); JS_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); directives = newDirectives; } parser.tokenStream.seek(start); } if (!NameFunctions(cx, fn)) return false; if (fn->pn_funbox->function()->isInterpreted()) { JS_ASSERT(fun == fn->pn_funbox->function()); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, /* staticLevel = */ 0, sourceObject, /* sourceStart = */ 0, length)); if (!script) return false; script->bindings = fn->pn_funbox->bindings; /* * The reason for checking fun->environment() below is that certain * consumers of JS::CompileFunction, namely * nsEventListenerManager::CompileEventHandlerInternal, passes in a * NULL environment. This compiled function is never used, but instead * is cloned immediately onto the right scope chain. */ BytecodeEmitter funbce(/* parent = */ NULL, &parser, fn->pn_funbox, script, /* insideEval = */ false, /* evalCaller = */ NullPtr(), fun->environment() && fun->environment()->is<GlobalObject>(), options.lineno); if (!funbce.init()) return false; if (!EmitFunctionScript(cx, &funbce, fn->pn_body)) return false; } else { fun.set(fn->pn_funbox->function()); JS_ASSERT(IsAsmJSModuleNative(fun->native())); } if (!SetSourceMap(cx, parser.tokenStream, ss)) return false; if (!sct.complete()) return false; return true; }
// 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, const AutoNameVector &formals, SourceBufferHolder &srcBuf, HandleObject enclosingScope, GeneratorKind generatorKind) { js::TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime()); uint32_t logId = js::TraceLogCreateTextId(logger, options); js::AutoTraceLog scriptLogger(logger, logId); js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileFunction); // FIXME: make Function pass in two strings and parse them as arguments and // ProgramElements respectively. if (!CheckLength(cx, srcBuf)) return false; RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options)); if (!sourceObject) return false; ScriptSource *ss = sourceObject->source(); SourceCompressionTask sct(cx); MOZ_ASSERT(!options.sourceIsLazy); if (!cx->compartment()->options().discardSource()) { if (!ss->setSourceCopy(cx, srcBuf, true, &sct)) return false; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.emplace(cx, &cx->tempLifoAlloc(), options, srcBuf.get(), srcBuf.length(), /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) nullptr, (LazyScript *) nullptr); } MOZ_ASSERT(!options.forEval); Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, srcBuf.get(), srcBuf.length(), /* foldConstants = */ true, canLazilyParse ? syntaxParser.ptr() : nullptr, nullptr); parser.sct = &sct; parser.ss = ss; MOZ_ASSERT(fun); MOZ_ASSERT(fun->isTenured()); fun->setArgCount(formals.length()); // 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. Directives directives(options.strictOption); TokenStream::Position start(parser.keepAtoms); parser.tokenStream.tell(&start); ParseNode *fn; while (true) { Directives newDirectives = directives; fn = parser.standaloneFunctionBody(fun, formals, generatorKind, directives, &newDirectives); if (fn) break; 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; // Assignment must be monotonic to prevent reparsing iloops MOZ_ASSERT_IF(directives.strict(), newDirectives.strict()); MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); directives = newDirectives; } parser.tokenStream.seek(start); } if (!NameFunctions(cx, fn)) return false; if (!SetDisplayURL(cx, parser.tokenStream, ss)) return false; if (!SetSourceMap(cx, parser.tokenStream, ss)) return false; if (fn->pn_funbox->function()->isInterpreted()) { MOZ_ASSERT(fun == fn->pn_funbox->function()); Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options, /* staticLevel = */ 0, sourceObject, /* sourceStart = */ 0, srcBuf.length())); if (!script) return false; script->bindings = fn->pn_funbox->bindings; /* * The reason for checking fun->environment() below is that certain * consumers of JS::CompileFunction, namely * EventListenerManager::CompileEventHandlerInternal, passes in a * nullptr environment. This compiled function is never used, but * instead is cloned immediately onto the right scope chain. */ BytecodeEmitter funbce(/* parent = */ nullptr, &parser, fn->pn_funbox, script, /* lazyScript = */ js::NullPtr(), /* insideEval = */ false, /* evalCaller = */ js::NullPtr(), fun->environment() && fun->environment()->is<GlobalObject>(), options.lineno); if (!funbce.init()) return false; if (!EmitFunctionScript(cx, &funbce, fn->pn_body)) return false; } else { fun.set(fn->pn_funbox->function()); MOZ_ASSERT(IsAsmJSModuleNative(fun->native())); } if (!sct.complete()) return false; return true; }