JS_GetScriptSourceMap(JSContext *cx, JSScript *script) { ScriptSource *source = script->scriptSource(); JS_ASSERT(source); return source->hasSourceMapURL() ? source->sourceMapURL() : nullptr; }
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; }