JSScript * frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain, HandleScript evalCaller, const CompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */, SourceCompressionToken *extraSct /* = NULL */) { RootedString source(cx, source_); SkipRoot skip(cx, &chars); if (cx->isJSContext()) MaybeCallSourceHandler(cx->asJSContext(), options, chars, length); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(evalCaller, options.compileAndGo); JS_ASSERT_IF(evalCaller, options.forEval); JS_ASSERT_IF(staticLevel != 0, evalCaller); if (!CheckLength(cx, length)) return NULL; JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(options.originPrincipals()); if (!ss) return NULL; if (options.filename && !ss->setFilename(cx, options.filename)) return NULL; JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss)); if (!sourceObject) return NULL; // Saving source is not yet supported when parsing off thread. JS_ASSERT_IF(!cx->isJSContext(), !extraSct && options.sourcePolicy == CompileOptions::NO_SOURCE); SourceCompressionToken *sct = extraSct; Maybe<SourceCompressionToken> mysct; if (cx->isJSContext() && !sct) { mysct.construct(cx->asJSContext()); sct = mysct.addr(); } switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx->asJSContext(), chars, length, false, sct)) return NULL; break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.construct(cx, alloc, options, chars, length, /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) NULL, (LazyScript *) NULL); } Parser<FullParseHandler> parser(cx, alloc, options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : NULL, NULL); parser.sct = sct; parser.ss = ss; Directives directives(options.strictOption); GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); bool savedCallerFun = options.compileAndGo && evalCaller && (evalCaller->function() || evalCaller->savedCallerFun); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, sourceObject, 0, length)); if (!script) return NULL; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter::EmitterMode emitterMode = options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal; BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, options.forEval, evalCaller, !!globalScope, options.lineno, emitterMode); if (!bce.init()) return NULL; // Syntax parsing may cause us to restart processing of top level // statements in the script. Use Maybe<> so that the parse context can be // reset when this occurs. Maybe<ParseContext<FullParseHandler> > pc; pc.construct(&parser, (GenericParseContext *) NULL, (ParseNode *) NULL, &globalsc, (Directives *) NULL, staticLevel, /* bodyid = */ 0); if (!pc.ref().init()) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (evalCaller && evalCaller->strict) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString<CanGC>(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (evalCaller && evalCaller->functionOrCallerFunction()) { /* * 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. */ JSFunction *fun = evalCaller->functionOrCallerFunction(); Directives directives(/* strict = */ fun->strict()); ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(), directives); if (!funbox) return NULL; bce.objectList.add(funbox); } } bool canHaveDirectives = true; for (;;) { TokenKind tt = parser.tokenStream.peekToken(TokenStream::Operand); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } TokenStream::Position pos(parser.keepAtoms); parser.tokenStream.tell(&pos); ParseNode *pn = parser.statement(canHaveDirectives); if (!pn) { if (parser.hadAbortedSyntaxParse()) { // Parsing inner functions lazily may lead the parser into an // unrecoverable state and may require starting over on the top // level statement. Restart the parse; syntax parsing has // already been disabled for the parser and the result will not // be ambiguous. parser.clearAbortedSyntaxParse(); parser.tokenStream.seek(pos); // Destroying the parse context will destroy its free // variables, so check if any deoptimization is needed. if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) return NULL; pc.destroy(); pc.construct(&parser, (GenericParseContext *) NULL, (ParseNode *) NULL, &globalsc, (Directives *) NULL, staticLevel, /* bodyid = */ 0); if (!pc.ref().init()) return NULL; JS_ASSERT(parser.pc == pc.addr()); pn = parser.statement(); } if (!pn) { JS_ASSERT(!parser.hadAbortedSyntaxParse()); return NULL; } } if (canHaveDirectives) { if (!parser.maybeParseDirective(/* stmtList = */ NULL, pn, &canHaveDirectives)) return NULL; } if (!FoldConstants(cx, &pn, &parser)) return NULL; // Inferring names for functions in compiled scripts is currently only // supported while on the main thread. See bug 895395. if (cx->isJSContext() && !NameFunctions(cx->asJSContext(), pn)) return NULL; if (!EmitTree(cx, &bce, pn)) return NULL; parser.handler.freeTree(pn); } if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) return NULL; if (!SetSourceMap(cx, parser.tokenStream, ss)) return NULL; /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return NULL; bce.tellDebuggerAboutCompiledScript(cx); if (sct && !extraSct && !sct->complete()) return NULL; return script; }
UnrootedScript frontend::CompileScript(JSContext *cx, HandleObject scopeChain, AbstractFramePtr callerFrame, const CompileOptions &options, StableCharPtr chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */) { RootedString source(cx, source_); class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(options.filename, options.lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); if (!CheckLength(cx, length)) return UnrootedScript(NULL); JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return UnrootedScript(NULL); ScriptSourceHolder ssh(ss); SourceCompressionToken sct(cx); switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, &sct)) return UnrootedScript(NULL); break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return UnrootedScript(NULL); parser.sct = &sct; GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); ParseContext pc(&parser, &globalsc, staticLevel, /* bodyid = */ 0); if (!pc.init()) return UnrootedScript(NULL); bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame.isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return UnrootedScript(NULL); // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return UnrootedScript(NULL); // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, callerFrame, !!globalScope, options.lineno, options.selfHostingMode); if (!bce.init()) return UnrootedScript(NULL); /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame.script()->strict) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString<CanGC>(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return UnrootedScript(NULL); } if (callerFrame && callerFrame.isFunctionFrame()) { /* * 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. */ JSFunction *fun = callerFrame.fun(); ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict()); if (!funbox) return UnrootedScript(NULL); bce.objectList.add(funbox); } } TokenStream &tokenStream = parser.tokenStream; bool canHaveDirectives = true; for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return UnrootedScript(NULL); } ParseNode *pn = parser.statement(); if (!pn) return UnrootedScript(NULL); if (canHaveDirectives) { if (!parser.maybeParseDirective(pn, &canHaveDirectives)) return UnrootedScript(NULL); } if (!FoldConstants(cx, &pn, &parser)) return UnrootedScript(NULL); if (!NameFunctions(cx, pn)) return UnrootedScript(NULL); if (!EmitTree(cx, &bce, pn)) return UnrootedScript(NULL); parser.freeTree(pn); } if (!SetSourceMap(cx, tokenStream, ss, script)) return UnrootedScript(NULL); // It's an error to use |arguments| in a function that has a rest parameter. if (callerFrame && callerFrame.isFunctionFrame() && callerFrame.fun()->hasRest()) { HandlePropertyName arguments = cx->names().arguments; for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST); return UnrootedScript(NULL); } } } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return UnrootedScript(NULL); if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return UnrootedScript(NULL); bce.tellDebuggerAboutCompiledScript(cx); if (!sct.complete()) return UnrootedScript(NULL); return script; }
JSScript * frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame, const CompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */) { RootedString source(cx, source_); class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(options.filename, options.lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); if (!CheckLength(cx, length)) return NULL; JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return NULL; ScriptSourceHolder ssh(cx->runtime, ss); SourceCompressionToken sct(cx); switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, &sct)) return NULL; break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return NULL; parser.sct = &sct; SharedContext sc(cx, scopeChain, /* funbox = */ NULL, StrictModeFromContext(cx)); ParseContext pc(&parser, &sc, staticLevel, /* bodyid = */ 0); if (!pc.init()) return NULL; bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame->isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return NULL; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). if (!script->bindings.initWithTemporaryStorage(cx, 0, 0, NULL)) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &sc, script, callerFrame, !!globalScope, options.lineno, options.selfHostingMode); if (!bce.init()) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->script()->strictModeCode) sc.strictModeState = StrictMode::STRICT; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (callerFrame && callerFrame->isFunctionFrame()) { /* * 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. */ ObjectBox *funbox = parser.newObjectBox(callerFrame->fun()); if (!funbox) return NULL; if (!bce.objectList.append(funbox)) return NULL; } } ParseNode *pn; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif TokenStream &tokenStream = parser.tokenStream; { ParseNode *stringsAtStart = ListNode::create(PNK_STATEMENTLIST, &parser); if (!stringsAtStart) return NULL; stringsAtStart->makeEmpty(); bool ok = parser.processDirectives(stringsAtStart) && EmitTree(cx, &bce, stringsAtStart); parser.freeTree(stringsAtStart); if (!ok) return NULL; } JS_ASSERT(sc.strictModeState != StrictMode::UNKNOWN); for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } pn = parser.statement(); if (!pn) return NULL; if (!FoldConstants(cx, pn, &parser)) return NULL; if (!NameFunctions(cx, pn)) return NULL; pc.functionList = NULL; if (!EmitTree(cx, &bce, pn)) return NULL; #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif parser.freeTree(pn); } if (!SetSourceMap(cx, tokenStream, ss, script)) return NULL; #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via <script src="http://victim.com/foo.xml">. * For background, see: * * https://bugzilla.mozilla.org/show_bug.cgi?id=336551 */ if (pn && onlyXML && !callerFrame) { parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM); return NULL; } #endif // It's an error to use |arguments| in a function that has a rest parameter. if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) { PropertyName *arguments = cx->runtime->atomState.argumentsAtom; for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST); return NULL; } } } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return NULL; bce.tellDebuggerAboutCompiledScript(cx); return script; }
RawScript frontend::CompileScript(JSContext *cx, HandleObject scopeChain, HandleScript evalCaller, const CompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */, SourceCompressionToken *extraSct /* = NULL */) { RootedString source(cx, source_); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(evalCaller, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, evalCaller); if (!CheckLength(cx, length)) return NULL; JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return NULL; if (options.filename && !ss->setFilename(cx, options.filename)) return NULL; ScriptSourceHolder ssh(ss); SourceCompressionToken mysct(cx); SourceCompressionToken *sct = (extraSct) ? extraSct : &mysct; switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, sct)) return NULL; break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return NULL; parser.sct = sct; GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); ParseContext<FullParseHandler> pc(&parser, NULL, &globalsc, staticLevel, /* bodyid = */ 0); if (!pc.init()) return NULL; bool savedCallerFun = options.compileAndGo && evalCaller && (evalCaller->function() || evalCaller->savedCallerFun); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return NULL; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, evalCaller, !!globalScope, options.lineno, options.selfHostingMode); if (!bce.init()) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (evalCaller && evalCaller->strict) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString<CanGC>(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (evalCaller && evalCaller->functionOrCallerFunction()) { /* * 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. */ JSFunction *fun = evalCaller->functionOrCallerFunction(); ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict()); if (!funbox) return NULL; bce.objectList.add(funbox); } } bool canHaveDirectives = true; for (;;) { TokenKind tt = parser.tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } ParseNode *pn = parser.statement(); if (!pn) return NULL; if (canHaveDirectives) { if (!parser.maybeParseDirective(pn, &canHaveDirectives)) return NULL; } if (!FoldConstants(cx, &pn, &parser)) return NULL; if (!NameFunctions(cx, pn)) return NULL; if (!EmitTree(cx, &bce, pn)) return NULL; parser.handler.freeTree(pn); } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return NULL; if (evalCaller && evalCaller->functionOrCallerFunction()) { // 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, parser, fun)) return NULL; } } for (AtomDefnListMap::Range r = pc.decls().all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { if (!CheckArgumentsWithinEval(cx, parser, fun)) return NULL; } } // 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. if (pc.sc->hasDebuggerStatement()) { RootedObject scope(cx, scopeChain); while (scope->isScope() || scope->isDebugScope()) { if (scope->isCall() && !scope->asCall().isForEval()) { RootedScript script(cx, scope->asCall().callee().nonLazyScript()); if (script->argumentsHasVarBinding()) { if (!JSScript::argumentsOptimizationFailed(cx, script)) return NULL; } } scope = scope->enclosingScope(); } } } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return NULL; bce.tellDebuggerAboutCompiledScript(cx); if (sct == &mysct && !sct->complete()) return NULL; return script; }
JSScript * frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject scopeChain, HandleScript evalCaller, const ReadOnlyCompileOptions &options, const jschar *chars, size_t length, JSString *source_ /* = nullptr */, unsigned staticLevel /* = 0 */, SourceCompressionTask *extraSct /* = nullptr */) { RootedString source(cx, source_); js::TraceLogger *logger = nullptr; if (cx->isJSContext()) logger = TraceLoggerForMainThread(cx->asJSContext()->runtime()); else logger = TraceLoggerForCurrentThread(); uint32_t logId = js::TraceLogCreateTextId(logger, options); js::AutoTraceLog scriptLogger(logger, logId); js::AutoTraceLog typeLogger(logger, TraceLogger::ParserCompileScript); if (cx->isJSContext()) MaybeCallSourceHandler(cx->asJSContext(), options, chars, length); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(evalCaller, options.compileAndGo); JS_ASSERT_IF(evalCaller, options.forEval); JS_ASSERT_IF(staticLevel != 0, evalCaller); if (!CheckLength(cx, length)) return nullptr; JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options)); if (!sourceObject) return nullptr; ScriptSource *ss = sourceObject->source(); SourceCompressionTask mysct(cx); SourceCompressionTask *sct = extraSct ? extraSct : &mysct; switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, sct)) return nullptr; break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.construct(cx, alloc, options, chars, length, /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) nullptr, (LazyScript *) nullptr); } Parser<FullParseHandler> parser(cx, alloc, options, chars, length, /* foldConstants = */ true, canLazilyParse ? &syntaxParser.ref() : nullptr, nullptr); parser.sct = sct; parser.ss = ss; Directives directives(options.strictOption); GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); bool savedCallerFun = options.compileAndGo && evalCaller && evalCaller->functionOrCallerFunction(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, sourceObject, 0, length)); if (!script) return nullptr; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter::EmitterMode emitterMode = options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal; BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script, options.forEval, evalCaller, !!globalScope, options.lineno, emitterMode); if (!bce.init()) return nullptr; // Syntax parsing may cause us to restart processing of top level // statements in the script. Use Maybe<> so that the parse context can be // reset when this occurs. Maybe<ParseContext<FullParseHandler> > pc; pc.construct(&parser, (GenericParseContext *) nullptr, (ParseNode *) nullptr, &globalsc, (Directives *) nullptr, staticLevel, /* bodyid = */ 0, /* blockScopeDepth = */ 0); if (!pc.ref().init(parser.tokenStream)) return nullptr; /* If this is a direct call to eval, inherit the caller's strictness. */ if (evalCaller && evalCaller->strict()) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return nullptr; } if (evalCaller && evalCaller->functionOrCallerFunction()) { /* * 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. */ JSFunction *fun = evalCaller->functionOrCallerFunction(); Directives directives(/* strict = */ fun->strict()); ObjectBox *funbox = parser.newFunctionBox(/* fn = */ nullptr, fun, pc.addr(), directives, fun->generatorKind()); if (!funbox) return nullptr; bce.objectList.add(funbox); } } bool canHaveDirectives = true; for (;;) { TokenKind tt = parser.tokenStream.peekToken(TokenStream::Operand); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return nullptr; } TokenStream::Position pos(parser.keepAtoms); parser.tokenStream.tell(&pos); ParseNode *pn = parser.statement(canHaveDirectives); if (!pn) { if (parser.hadAbortedSyntaxParse()) { // Parsing inner functions lazily may lead the parser into an // unrecoverable state and may require starting over on the top // level statement. Restart the parse; syntax parsing has // already been disabled for the parser and the result will not // be ambiguous. parser.clearAbortedSyntaxParse(); parser.tokenStream.seek(pos); // Destroying the parse context will destroy its free // variables, so check if any deoptimization is needed. if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) return nullptr; pc.destroy(); pc.construct(&parser, (GenericParseContext *) nullptr, (ParseNode *) nullptr, &globalsc, (Directives *) nullptr, staticLevel, /* bodyid = */ 0, script->bindings.numBlockScoped()); if (!pc.ref().init(parser.tokenStream)) return nullptr; JS_ASSERT(parser.pc == pc.addr()); pn = parser.statement(); } if (!pn) { JS_ASSERT(!parser.hadAbortedSyntaxParse()); return nullptr; } } // Accumulate the maximum block scope depth, so that EmitTree can assert // when emitting JSOP_GETLOCAL that the local is indeed within the fixed // part of the stack frame. script->bindings.updateNumBlockScoped(pc.ref().blockScopeDepth); if (canHaveDirectives) { if (!parser.maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives)) return nullptr; } if (!FoldConstants(cx, &pn, &parser)) return nullptr; if (!NameFunctions(cx, pn)) return nullptr; if (!EmitTree(cx, &bce, pn)) return nullptr; parser.handler.freeTree(pn); } if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, pc.ref())) return nullptr; if (!SetDisplayURL(cx, parser.tokenStream, ss)) return nullptr; if (!SetSourceMap(cx, parser.tokenStream, ss)) return nullptr; /* * 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()) { if (!ss->setSourceMapURL(cx, options.sourceMapURL())) return nullptr; } /* * Nowadays the threaded interpreter needs a last return instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_RETRVAL) < 0) return nullptr; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). They may have block-scoped // locals, however, which are allocated to the fixed part of the stack // frame. InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, nullptr, pc.ref().blockScopeDepth)) return nullptr; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return nullptr; bce.tellDebuggerAboutCompiledScript(cx); if (sct && !extraSct && !sct->complete()) return nullptr; return script; }
JSScript* frontend::CompileScript(ExclusiveContext* cx, LifoAlloc* alloc, HandleObject scopeChain, HandleScript evalCaller, Handle<StaticEvalObject*> evalStaticScope, const ReadOnlyCompileOptions& options, SourceBufferHolder& srcBuf, JSString* source_ /* = nullptr */, unsigned staticLevel /* = 0 */, SourceCompressionTask* extraSct /* = nullptr */) { MOZ_ASSERT(srcBuf.get()); RootedString source(cx, source_); js::TraceLoggerThread* logger = nullptr; if (cx->isJSContext()) logger = TraceLoggerForMainThread(cx->asJSContext()->runtime()); else logger = TraceLoggerForCurrentThread(); js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options); js::AutoTraceLog scriptLogger(logger, event); js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileScript); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ MOZ_ASSERT_IF(evalCaller, options.compileAndGo); MOZ_ASSERT_IF(evalCaller, options.forEval); MOZ_ASSERT_IF(evalCaller && evalCaller->strict(), options.strictOption); MOZ_ASSERT_IF(staticLevel != 0, evalCaller); if (!CheckLength(cx, srcBuf)) return nullptr; MOZ_ASSERT_IF(staticLevel != 0, !options.sourceIsLazy); RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options)); if (!sourceObject) return nullptr; ScriptSource* ss = sourceObject->source(); SourceCompressionTask mysct(cx); SourceCompressionTask* sct = extraSct ? extraSct : &mysct; if (!cx->compartment()->options().discardSource()) { if (options.sourceIsLazy) ss->setSourceRetrievable(); else if (!ss->setSourceCopy(cx, srcBuf, false, sct)) return nullptr; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (canLazilyParse) { syntaxParser.emplace(cx, alloc, options, srcBuf.get(), srcBuf.length(), /* foldConstants = */ false, (Parser<SyntaxParseHandler>*) nullptr, (LazyScript*) nullptr); if (!syntaxParser->checkOptions()) return nullptr; } Parser<FullParseHandler> parser(cx, alloc, options, srcBuf.get(), srcBuf.length(), /* foldConstants = */ true, canLazilyParse ? syntaxParser.ptr() : nullptr, nullptr); parser.sct = sct; parser.ss = ss; if (!parser.checkOptions()) return nullptr; Directives directives(options.strictOption); GlobalSharedContext globalsc(cx, scopeChain, directives, options.extraWarningsOption); bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction(); Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun, options, staticLevel, sourceObject, 0, srcBuf.length())); if (!script) return nullptr; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject* globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr; MOZ_ASSERT_IF(globalScope, globalScope->isNative()); MOZ_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter::EmitterMode emitterMode = options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal; BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script, /* lazyScript = */ js::NullPtr(), options.forEval, evalCaller, evalStaticScope, !!globalScope, options.lineno, emitterMode); if (!bce.init()) return nullptr; // Syntax parsing may cause us to restart processing of top level // statements in the script. Use Maybe<> so that the parse context can be // reset when this occurs. Maybe<ParseContext<FullParseHandler> > pc; pc.emplace(&parser, (GenericParseContext*) nullptr, (ParseNode*) nullptr, &globalsc, (Directives*) nullptr, staticLevel, /* bodyid = */ 0, /* blockScopeDepth = */ 0); if (!pc->init(parser.tokenStream)) return nullptr; if (savedCallerFun) { /* * 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. */ JSFunction* fun = evalCaller->functionOrCallerFunction(); MOZ_ASSERT_IF(fun->strict(), options.strictOption); Directives directives(/* strict = */ options.strictOption); ObjectBox* funbox = parser.newFunctionBox(/* fn = */ nullptr, fun, pc.ptr(), directives, fun->generatorKind()); if (!funbox) return nullptr; bce.objectList.add(funbox); } bool canHaveDirectives = true; for (;;) { TokenKind tt; if (!parser.tokenStream.peekToken(&tt, TokenStream::Operand)) return nullptr; if (tt == TOK_EOF) break; TokenStream::Position pos(parser.keepAtoms); parser.tokenStream.tell(&pos); ParseNode* pn = parser.statement(canHaveDirectives); if (!pn) { if (parser.hadAbortedSyntaxParse()) { // Parsing inner functions lazily may lead the parser into an // unrecoverable state and may require starting over on the top // level statement. Restart the parse; syntax parsing has // already been disabled for the parser and the result will not // be ambiguous. parser.clearAbortedSyntaxParse(); parser.tokenStream.seek(pos); // Destroying the parse context will destroy its free // variables, so check if any deoptimization is needed. if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, *pc)) return nullptr; pc.reset(); pc.emplace(&parser, (GenericParseContext*) nullptr, (ParseNode*) nullptr, &globalsc, (Directives*) nullptr, staticLevel, /* bodyid = */ 0, script->bindings.numBlockScoped()); if (!pc->init(parser.tokenStream)) return nullptr; MOZ_ASSERT(parser.pc == pc.ptr()); pn = parser.statement(); } if (!pn) { MOZ_ASSERT(!parser.hadAbortedSyntaxParse()); return nullptr; } } // Accumulate the maximum block scope depth, so that EmitTree can assert // when emitting JSOP_GETLOCAL that the local is indeed within the fixed // part of the stack frame. script->bindings.updateNumBlockScoped(pc->blockScopeDepth); if (canHaveDirectives) { if (!parser.maybeParseDirective(/* stmtList = */ nullptr, pn, &canHaveDirectives)) return nullptr; } if (!FoldConstants(cx, &pn, &parser)) return nullptr; if (!NameFunctions(cx, pn)) return nullptr; if (!bce.updateLocalsToFrameSlots()) return nullptr; if (!EmitTree(cx, &bce, pn)) return nullptr; parser.handler.freeTree(pn); } if (!MaybeCheckEvalFreeVariables(cx, evalCaller, scopeChain, parser, *pc)) return nullptr; if (!SetDisplayURL(cx, parser.tokenStream, ss)) return nullptr; if (!SetSourceMap(cx, parser.tokenStream, ss)) return nullptr; /* * 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 (ss->hasSourceMapURL()) { if(!parser.report(ParseWarning, false, nullptr, JSMSG_ALREADY_HAS_PRAGMA, ss->filename(), "//# sourceMappingURL")) return nullptr; } if (!ss->setSourceMapURL(cx, options.sourceMapURL())) return nullptr; } /* * Nowadays the threaded interpreter needs a last return instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_RETRVAL) < 0) return nullptr; // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). They may have block-scoped // locals, however, which are allocated to the fixed part of the stack // frame. InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, 0, pc->blockScopeDepth, 0, 0, nullptr)) { return nullptr; } if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return nullptr; // Note that this marking must happen before we tell Debugger // about the new script, in case Debugger delazifies the script's // inner functions. if (options.forEval) MarkFunctionsWithinEvalScript(script); bce.tellDebuggerAboutCompiledScript(cx); if (sct && !extraSct && !sct->complete()) return nullptr; MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending()); return script; }