LCOV - code coverage report
Current view: top level - js/src/frontend - BytecodeCompiler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 197 339 58.1 %
Date: 2017-07-14 16:53:18 Functions: 24 34 70.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "frontend/BytecodeCompiler.h"
       8             : 
       9             : #include "mozilla/IntegerPrintfMacros.h"
      10             : #include "mozilla/Maybe.h"
      11             : 
      12             : #include "jscntxt.h"
      13             : #include "jsscript.h"
      14             : 
      15             : #include "builtin/ModuleObject.h"
      16             : #include "frontend/BytecodeEmitter.h"
      17             : #include "frontend/FoldConstants.h"
      18             : #include "frontend/NameFunctions.h"
      19             : #include "frontend/Parser.h"
      20             : #include "vm/GlobalObject.h"
      21             : #include "vm/TraceLogging.h"
      22             : #include "wasm/AsmJS.h"
      23             : 
      24             : #include "jsobjinlines.h"
      25             : #include "jsscriptinlines.h"
      26             : 
      27             : #include "vm/EnvironmentObject-inl.h"
      28             : 
      29             : using namespace js;
      30             : using namespace js::frontend;
      31             : using mozilla::Maybe;
      32             : using mozilla::Nothing;
      33             : 
      34             : // The BytecodeCompiler class contains resources common to compiling scripts and
      35             : // function bodies.
      36         284 : class MOZ_STACK_CLASS BytecodeCompiler
      37             : {
      38             :   public:
      39             :     // Construct an object passing mandatory arguments.
      40             :     BytecodeCompiler(JSContext* cx,
      41             :                      LifoAlloc& alloc,
      42             :                      const ReadOnlyCompileOptions& options,
      43             :                      SourceBufferHolder& sourceBuffer,
      44             :                      HandleScope enclosingScope);
      45             : 
      46             :     JSScript* compileGlobalScript(ScopeKind scopeKind);
      47             :     JSScript* compileEvalScript(HandleObject environment, HandleScope enclosingScope);
      48             :     ModuleObject* compileModule();
      49             :     bool compileStandaloneFunction(MutableHandleFunction fun, GeneratorKind generatorKind,
      50             :                                    FunctionAsyncKind asyncKind,
      51             :                                    const Maybe<uint32_t>& parameterListEnd);
      52             : 
      53             :     ScriptSourceObject* sourceObjectPtr() const;
      54             :     SourceCompressionTask* sourceCompressionTask() const;
      55             : 
      56             :   private:
      57             :     JSScript* compileScript(HandleObject environment, SharedContext* sc);
      58             :     bool checkLength();
      59             :     bool createScriptSource(const Maybe<uint32_t>& parameterListEnd);
      60             :     bool canLazilyParse();
      61             :     bool createParser();
      62             :     bool createSourceAndParser(const Maybe<uint32_t>& parameterListEnd = Nothing());
      63             : 
      64             :     // If toString{Start,End} are not explicitly passed, assume the script's
      65             :     // offsets in the source used to parse it are the same as what should be
      66             :     // used to compute its Function.prototype.toString() value.
      67             :     bool createScript();
      68             :     bool createScript(uint32_t toStringStart, uint32_t toStringEnd);
      69             : 
      70             :     bool emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext);
      71             :     bool handleParseFailure(const Directives& newDirectives);
      72             :     bool deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment);
      73             : 
      74             :     AutoKeepAtoms keepAtoms;
      75             : 
      76             :     JSContext* cx;
      77             :     LifoAlloc& alloc;
      78             :     const ReadOnlyCompileOptions& options;
      79             :     SourceBufferHolder& sourceBuffer;
      80             : 
      81             :     RootedScope enclosingScope;
      82             : 
      83             :     RootedScriptSource sourceObject;
      84             :     ScriptSource* scriptSource;
      85             : 
      86             :     Maybe<UsedNameTracker> usedNames;
      87             :     Maybe<Parser<SyntaxParseHandler, char16_t>> syntaxParser;
      88             :     Maybe<Parser<FullParseHandler, char16_t>> parser;
      89             : 
      90             :     Directives directives;
      91             :     TokenStream::Position startPosition;
      92             : 
      93             :     RootedScript script;
      94             : };
      95             : 
      96           0 : AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
      97           0 :                                            const char* filename, size_t line, size_t column)
      98             : #ifdef JS_TRACE_LOGGING
      99           0 :   : logger_(TraceLoggerForCurrentThread(cx))
     100             : {
     101           0 :     frontendEvent_.emplace(TraceLogger_Frontend, filename, line, column);
     102           0 :     frontendLog_.emplace(logger_, *frontendEvent_);
     103           0 :     typeLog_.emplace(logger_, id);
     104           0 : }
     105             : #else
     106             : { }
     107             : #endif
     108             : 
     109        7646 : AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
     110        7646 :                                            const TokenStreamAnyChars& tokenStream)
     111             : #ifdef JS_TRACE_LOGGING
     112        7646 :   : logger_(TraceLoggerForCurrentThread(cx))
     113             : {
     114             :     // If the tokenizer hasn't yet gotten any tokens, use the line and column
     115             :     // numbers from CompileOptions.
     116             :     uint32_t line, column;
     117        7646 :     if (tokenStream.isCurrentTokenType(TOK_EOF) && !tokenStream.isEOF()) {
     118        1676 :         line = tokenStream.options().lineno;
     119        1676 :         column = tokenStream.options().column;
     120             :     } else {
     121        5970 :         uint32_t offset = tokenStream.currentToken().pos.begin;
     122        5970 :         tokenStream.srcCoords.lineNumAndColumnIndex(offset, &line, &column);
     123             :     }
     124        7646 :     frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(), line, column);
     125        7646 :     frontendLog_.emplace(logger_, *frontendEvent_);
     126        7646 :     typeLog_.emplace(logger_, id);
     127        7646 : }
     128             : #else
     129             : { }
     130             : #endif
     131             : 
     132        5404 : AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
     133             :                                            const TokenStreamAnyChars& tokenStream,
     134        5404 :                                            FunctionBox* funbox)
     135             : #ifdef JS_TRACE_LOGGING
     136        5404 :   : logger_(TraceLoggerForCurrentThread(cx))
     137             : {
     138       10808 :     frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(),
     139        5404 :                            funbox->startLine, funbox->startColumn);
     140        5404 :     frontendLog_.emplace(logger_, *frontendEvent_);
     141        5404 :     typeLog_.emplace(logger_, id);
     142        5404 : }
     143             : #else
     144             : { }
     145             : #endif
     146             : 
     147         268 : AutoFrontendTraceLog::AutoFrontendTraceLog(JSContext* cx, const TraceLoggerTextId id,
     148         268 :                                            const TokenStreamAnyChars& tokenStream, ParseNode* pn)
     149             : #ifdef JS_TRACE_LOGGING
     150         268 :   : logger_(TraceLoggerForCurrentThread(cx))
     151             : {
     152             :     uint32_t line, column;
     153         268 :     tokenStream.srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
     154         268 :     frontendEvent_.emplace(TraceLogger_Frontend, tokenStream.getFilename(), line, column);
     155         268 :     frontendLog_.emplace(logger_, *frontendEvent_);
     156         268 :     typeLog_.emplace(logger_, id);
     157         268 : }
     158             : #else
     159             : { }
     160             : #endif
     161             : 
     162         284 : BytecodeCompiler::BytecodeCompiler(JSContext* cx,
     163             :                                    LifoAlloc& alloc,
     164             :                                    const ReadOnlyCompileOptions& options,
     165             :                                    SourceBufferHolder& sourceBuffer,
     166         284 :                                    HandleScope enclosingScope)
     167             :   : keepAtoms(cx),
     168             :     cx(cx),
     169             :     alloc(alloc),
     170             :     options(options),
     171             :     sourceBuffer(sourceBuffer),
     172             :     enclosingScope(cx, enclosingScope),
     173             :     sourceObject(cx),
     174             :     scriptSource(nullptr),
     175         284 :     directives(options.strictOption),
     176             :     startPosition(keepAtoms),
     177         568 :     script(cx)
     178             : {
     179         284 :     MOZ_ASSERT(sourceBuffer.get());
     180         284 : }
     181             : 
     182             : bool
     183         284 : BytecodeCompiler::checkLength()
     184             : {
     185             :     // Note this limit is simply so we can store sourceStart and sourceEnd in
     186             :     // JSScript as 32-bits. It could be lifted fairly easily, since the compiler
     187             :     // is using size_t internally already.
     188         284 :     if (sourceBuffer.length() > UINT32_MAX) {
     189           0 :         if (!cx->helperThread())
     190           0 :             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     191           0 :                                       JSMSG_SOURCE_TOO_LONG);
     192           0 :         return false;
     193             :     }
     194         284 :     return true;
     195             : }
     196             : 
     197             : bool
     198         284 : BytecodeCompiler::createScriptSource(const Maybe<uint32_t>& parameterListEnd)
     199             : {
     200         284 :     if (!checkLength())
     201           0 :         return false;
     202             : 
     203         284 :     sourceObject = CreateScriptSourceObject(cx, options, parameterListEnd);
     204         284 :     if (!sourceObject)
     205           0 :         return false;
     206             : 
     207         284 :     scriptSource = sourceObject->source();
     208             : 
     209         284 :     if (!cx->compartment()->behaviors().discardSource()) {
     210         281 :         if (options.sourceIsLazy) {
     211          64 :             scriptSource->setSourceRetrievable();
     212         217 :         } else if (!scriptSource->setSourceCopy(cx, sourceBuffer)) {
     213           0 :             return false;
     214             :         }
     215             :     }
     216             : 
     217         284 :     return true;
     218             : }
     219             : 
     220             : bool
     221         284 : BytecodeCompiler::canLazilyParse()
     222             : {
     223         565 :     return options.canLazilyParse &&
     224         575 :            !(enclosingScope && enclosingScope->hasOnChain(ScopeKind::NonSyntactic)) &&
     225         554 :            !cx->compartment()->behaviors().disableLazyParsing() &&
     226         554 :            !cx->compartment()->behaviors().discardSource() &&
     227         774 :            !options.sourceIsLazy &&
     228         497 :            !cx->lcovEnabled();
     229             : }
     230             : 
     231             : bool
     232         284 : BytecodeCompiler::createParser()
     233             : {
     234         284 :     usedNames.emplace(cx);
     235         284 :     if (!usedNames->init())
     236           0 :         return false;
     237             : 
     238         284 :     if (canLazilyParse()) {
     239         426 :         syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
     240         213 :                              /* foldConstants = */ false, *usedNames, nullptr, nullptr);
     241             : 
     242         213 :         if (!syntaxParser->checkOptions())
     243           0 :             return false;
     244             :     }
     245             : 
     246        1136 :     parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
     247         852 :                    /* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr);
     248         284 :     parser->ss = scriptSource;
     249         284 :     if (!parser->checkOptions())
     250           0 :         return false;
     251             : 
     252         284 :     parser->tokenStream.tell(&startPosition);
     253         284 :     return true;
     254             : }
     255             : 
     256             : bool
     257         284 : BytecodeCompiler::createSourceAndParser(const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
     258             : {
     259         568 :     return createScriptSource(parameterListEnd) &&
     260         568 :            createParser();
     261             : }
     262             : 
     263             : bool
     264         269 : BytecodeCompiler::createScript()
     265             : {
     266         269 :     return createScript(0, sourceBuffer.length());
     267             : }
     268             : 
     269             : bool
     270         284 : BytecodeCompiler::createScript(uint32_t toStringStart, uint32_t toStringEnd)
     271             : {
     272         568 :     script = JSScript::Create(cx, options,
     273         284 :                               sourceObject, /* sourceStart = */ 0, sourceBuffer.length(),
     274         284 :                               toStringStart, toStringEnd);
     275         284 :     return script != nullptr;
     276             : }
     277             : 
     278             : bool
     279         284 : BytecodeCompiler::emplaceEmitter(Maybe<BytecodeEmitter>& emitter, SharedContext* sharedContext)
     280             : {
     281             :     BytecodeEmitter::EmitterMode emitterMode =
     282         284 :         options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
     283         568 :     emitter.emplace(/* parent = */ nullptr, parser.ptr(), sharedContext, script,
     284         568 :                     /* lazyScript = */ nullptr, options.lineno, emitterMode);
     285         284 :     return emitter->init();
     286             : }
     287             : 
     288             : bool
     289           0 : BytecodeCompiler::handleParseFailure(const Directives& newDirectives)
     290             : {
     291           0 :     if (parser->hadAbortedSyntaxParse()) {
     292             :         // Hit some unrecoverable ambiguity during an inner syntax parse.
     293             :         // Syntax parsing has now been disabled in the parser, so retry
     294             :         // the parse.
     295           0 :         parser->clearAbortedSyntaxParse();
     296           0 :     } else if (parser->tokenStream.hadError() || directives == newDirectives) {
     297           0 :         return false;
     298             :     }
     299             : 
     300           0 :     parser->tokenStream.seek(startPosition);
     301             : 
     302             :     // Assignment must be monotonic to prevent reparsing iloops
     303           0 :     MOZ_ASSERT_IF(directives.strict(), newDirectives.strict());
     304           0 :     MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS());
     305           0 :     directives = newDirectives;
     306           0 :     return true;
     307             : }
     308             : 
     309             : bool
     310           0 : BytecodeCompiler::deoptimizeArgumentsInEnclosingScripts(JSContext* cx, HandleObject environment)
     311             : {
     312           0 :     RootedObject env(cx, environment);
     313           0 :     while (env->is<EnvironmentObject>() || env->is<DebugEnvironmentProxy>()) {
     314           0 :         if (env->is<CallObject>()) {
     315           0 :             RootedFunction fun(cx, &env->as<CallObject>().callee());
     316           0 :             RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
     317           0 :             if (!script)
     318           0 :                 return false;
     319           0 :             if (script->argumentsHasVarBinding()) {
     320           0 :                 if (!JSScript::argumentsOptimizationFailed(cx, script))
     321           0 :                     return false;
     322             :             }
     323             :         }
     324           0 :         env = env->enclosingEnvironment();
     325             :     }
     326             : 
     327           0 :     return true;
     328             : }
     329             : 
     330             : JSScript*
     331         269 : BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
     332             : {
     333         269 :     if (!createSourceAndParser())
     334           0 :         return nullptr;
     335             : 
     336         269 :     if (!createScript())
     337           0 :         return nullptr;
     338             : 
     339         538 :     Maybe<BytecodeEmitter> emitter;
     340         269 :     if (!emplaceEmitter(emitter, sc))
     341           0 :         return nullptr;
     342             : 
     343             :     for (;;) {
     344             :         ParseNode* pn;
     345         269 :         if (sc->isEvalContext())
     346           2 :             pn = parser->evalBody(sc->asEvalContext());
     347             :         else
     348         267 :             pn = parser->globalBody(sc->asGlobalContext());
     349             : 
     350             :         // Successfully parsed. Emit the script.
     351         268 :         if (pn) {
     352         268 :             if (sc->isEvalContext() && sc->hasDebuggerStatement() && !cx->helperThread()) {
     353             :                 // If the eval'ed script contains any debugger statement, force construction
     354             :                 // of arguments objects for the caller script and any other scripts it is
     355             :                 // transitively nested inside. The debugger can access any variable on the
     356             :                 // scope chain.
     357           0 :                 if (!deoptimizeArgumentsInEnclosingScripts(cx, environment))
     358           0 :                     return nullptr;
     359             :             }
     360         268 :             if (!emitter->emitScript(pn))
     361           0 :                 return nullptr;
     362         269 :             if (!NameFunctions(cx, pn))
     363           0 :                 return nullptr;
     364         269 :             parser->handler.freeTree(pn);
     365             : 
     366         269 :             break;
     367             :         }
     368             : 
     369             :         // Maybe we aborted a syntax parse. See if we can try again.
     370           0 :         if (!handleParseFailure(directives))
     371           0 :             return nullptr;
     372             : 
     373             :         // Reset UsedNameTracker state before trying again.
     374           0 :         usedNames->reset();
     375           0 :     }
     376             : 
     377             :     // We have just finished parsing the source. Inform the source so that we
     378             :     // can compute statistics (e.g. how much time our functions remain lazy).
     379         269 :     script->scriptSource()->recordParseEnded();
     380             : 
     381             :     // Enqueue an off-thread source compression task after finishing parsing.
     382         269 :     if (!scriptSource->tryCompressOffThread(cx))
     383           0 :         return nullptr;
     384             : 
     385         269 :     MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
     386             : 
     387         269 :     return script;
     388             : }
     389             : 
     390             : JSScript*
     391         267 : BytecodeCompiler::compileGlobalScript(ScopeKind scopeKind)
     392             : {
     393         534 :     GlobalSharedContext globalsc(cx, scopeKind, directives, options.extraWarningsOption);
     394         534 :     return compileScript(nullptr, &globalsc);
     395             : }
     396             : 
     397             : JSScript*
     398           2 : BytecodeCompiler::compileEvalScript(HandleObject environment, HandleScope enclosingScope)
     399             : {
     400             :     EvalSharedContext evalsc(cx, environment, enclosingScope,
     401           4 :                              directives, options.extraWarningsOption);
     402           4 :     return compileScript(environment, &evalsc);
     403             : }
     404             : 
     405             : ModuleObject*
     406           0 : BytecodeCompiler::compileModule()
     407             : {
     408           0 :     if (!createSourceAndParser())
     409           0 :         return nullptr;
     410             : 
     411           0 :     Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
     412           0 :     if (!module)
     413           0 :         return nullptr;
     414             : 
     415           0 :     if (!createScript())
     416           0 :         return nullptr;
     417             : 
     418           0 :     module->init(script);
     419             : 
     420           0 :     ModuleBuilder builder(cx, module);
     421           0 :     ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
     422           0 :     ParseNode* pn = parser->moduleBody(&modulesc);
     423           0 :     if (!pn)
     424           0 :         return nullptr;
     425             : 
     426           0 :     Maybe<BytecodeEmitter> emitter;
     427           0 :     if (!emplaceEmitter(emitter, &modulesc))
     428           0 :         return nullptr;
     429           0 :     if (!emitter->emitScript(pn->pn_body))
     430           0 :         return nullptr;
     431             : 
     432           0 :     if (!NameFunctions(cx, pn))
     433           0 :         return nullptr;
     434             : 
     435           0 :     parser->handler.freeTree(pn);
     436             : 
     437           0 :     if (!builder.initModule())
     438           0 :         return nullptr;
     439             : 
     440           0 :     RootedModuleEnvironmentObject env(cx, ModuleEnvironmentObject::create(cx, module));
     441           0 :     if (!env)
     442           0 :         return nullptr;
     443             : 
     444           0 :     module->setInitialEnvironment(env);
     445             : 
     446             :     // Enqueue an off-thread source compression task after finishing parsing.
     447           0 :     if (!scriptSource->tryCompressOffThread(cx))
     448           0 :         return nullptr;
     449             : 
     450           0 :     MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
     451           0 :     return module;
     452             : }
     453             : 
     454             : // Compile a standalone JS function, which might appear as the value of an
     455             : // event handler attribute in an HTML <INPUT> tag, or in a Function()
     456             : // constructor.
     457             : bool
     458          15 : BytecodeCompiler::compileStandaloneFunction(MutableHandleFunction fun,
     459             :                                             GeneratorKind generatorKind,
     460             :                                             FunctionAsyncKind asyncKind,
     461             :                                             const Maybe<uint32_t>& parameterListEnd)
     462             : {
     463          15 :     MOZ_ASSERT(fun);
     464          15 :     MOZ_ASSERT(fun->isTenured());
     465             : 
     466          15 :     if (!createSourceAndParser(parameterListEnd))
     467           0 :         return false;
     468             : 
     469             :     // Speculatively parse using the default directives implied by the context.
     470             :     // If a directive is encountered (e.g., "use strict") that changes how the
     471             :     // function should have been parsed, we backup and reparse with the new set
     472             :     // of directives.
     473             : 
     474             :     ParseNode* fn;
     475           0 :     do {
     476          15 :         Directives newDirectives = directives;
     477          30 :         fn = parser->standaloneFunction(fun, enclosingScope, parameterListEnd, generatorKind,
     478          15 :                                         asyncKind, directives, &newDirectives);
     479          15 :         if (!fn && !handleParseFailure(newDirectives))
     480           0 :             return false;
     481          15 :     } while (!fn);
     482             : 
     483          15 :     if (fn->pn_funbox->function()->isInterpreted()) {
     484          15 :         MOZ_ASSERT(fun == fn->pn_funbox->function());
     485             : 
     486          15 :         if (!createScript(fn->pn_funbox->toStringStart, fn->pn_funbox->toStringEnd))
     487           0 :             return false;
     488             : 
     489          30 :         Maybe<BytecodeEmitter> emitter;
     490          15 :         if (!emplaceEmitter(emitter, fn->pn_funbox))
     491           0 :             return false;
     492          15 :         if (!emitter->emitFunctionScript(fn->pn_body))
     493           0 :             return false;
     494             :     } else {
     495           0 :         fun.set(fn->pn_funbox->function());
     496           0 :         MOZ_ASSERT(IsAsmJSModule(fun));
     497             :     }
     498             : 
     499          15 :     if (!NameFunctions(cx, fn))
     500           0 :         return false;
     501             : 
     502             :     // Enqueue an off-thread source compression task after finishing parsing.
     503          15 :     if (!scriptSource->tryCompressOffThread(cx))
     504           0 :         return false;
     505             : 
     506          15 :     return true;
     507             : }
     508             : 
     509             : ScriptSourceObject*
     510           0 : BytecodeCompiler::sourceObjectPtr() const
     511             : {
     512           0 :     return sourceObject.get();
     513             : }
     514             : 
     515             : ScriptSourceObject*
     516         425 : frontend::CreateScriptSourceObject(JSContext* cx, const ReadOnlyCompileOptions& options,
     517             :                                    const Maybe<uint32_t>& parameterListEnd /* = Nothing() */)
     518             : {
     519         425 :     ScriptSource* ss = cx->new_<ScriptSource>();
     520         425 :     if (!ss)
     521           0 :         return nullptr;
     522         850 :     ScriptSourceHolder ssHolder(ss);
     523             : 
     524         425 :     if (!ss->initFromOptions(cx, options, parameterListEnd))
     525           0 :         return nullptr;
     526             : 
     527         850 :     RootedScriptSource sso(cx, ScriptSourceObject::create(cx, ss));
     528         425 :     if (!sso)
     529           0 :         return nullptr;
     530             : 
     531             :     // Off-thread compilations do all their GC heap allocation, including the
     532             :     // SSO, in a temporary compartment. Hence, for the SSO to refer to the
     533             :     // gc-heap-allocated values in |options|, it would need cross-compartment
     534             :     // wrappers from the temporary compartment to the real compartment --- which
     535             :     // would then be inappropriate once we merged the temporary and real
     536             :     // compartments.
     537             :     //
     538             :     // Instead, we put off populating those SSO slots in off-thread compilations
     539             :     // until after we've merged compartments.
     540         425 :     if (!cx->helperThread()) {
     541         425 :         if (!ScriptSourceObject::initFromOptions(cx, sso, options))
     542           0 :             return nullptr;
     543             :     }
     544             : 
     545         425 :     return sso;
     546             : }
     547             : 
     548             : // CompileScript independently returns the ScriptSourceObject (SSO) for the
     549             : // compile.  This is used by off-thread script compilation (OT-SC).
     550             : //
     551             : // OT-SC cannot initialize the SSO when it is first constructed because the
     552             : // SSO is allocated initially in a separate compartment.
     553             : //
     554             : // After OT-SC, the separate compartment is merged with the main compartment,
     555             : // at which point the JSScripts created become observable by the debugger via
     556             : // memory-space scanning.
     557             : //
     558             : // Whatever happens to the top-level script compilation (even if it fails and
     559             : // returns null), we must finish initializing the SSO.  This is because there
     560             : // may be valid inner scripts observable by the debugger which reference the
     561             : // partially-initialized SSO.
     562             : class MOZ_STACK_CLASS AutoInitializeSourceObject
     563             : {
     564             :     BytecodeCompiler& compiler_;
     565             :     ScriptSourceObject** sourceObjectOut_;
     566             : 
     567             :   public:
     568         269 :     AutoInitializeSourceObject(BytecodeCompiler& compiler,
     569             :                                ScriptSourceObject** sourceObjectOut)
     570         269 :       : compiler_(compiler),
     571         269 :         sourceObjectOut_(sourceObjectOut)
     572         269 :     { }
     573             : 
     574         538 :     ~AutoInitializeSourceObject() {
     575         269 :         if (sourceObjectOut_)
     576           0 :             *sourceObjectOut_ = compiler_.sourceObjectPtr();
     577         269 :     }
     578             : };
     579             : 
     580             : JSScript*
     581         267 : frontend::CompileGlobalScript(JSContext* cx, LifoAlloc& alloc, ScopeKind scopeKind,
     582             :                               const ReadOnlyCompileOptions& options,
     583             :                               SourceBufferHolder& srcBuf,
     584             :                               ScriptSourceObject** sourceObjectOut)
     585             : {
     586         267 :     MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic);
     587         534 :     BytecodeCompiler compiler(cx, alloc, options, srcBuf, /* enclosingScope = */ nullptr);
     588         534 :     AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
     589         534 :     return compiler.compileGlobalScript(scopeKind);
     590             : }
     591             : 
     592             : JSScript*
     593           2 : frontend::CompileEvalScript(JSContext* cx, LifoAlloc& alloc,
     594             :                             HandleObject environment, HandleScope enclosingScope,
     595             :                             const ReadOnlyCompileOptions& options,
     596             :                             SourceBufferHolder& srcBuf,
     597             :                             ScriptSourceObject** sourceObjectOut)
     598             : {
     599           4 :     BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingScope);
     600           4 :     AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
     601           4 :     return compiler.compileEvalScript(environment, enclosingScope);
     602             : }
     603             : 
     604             : ModuleObject*
     605           0 : frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
     606             :                         SourceBufferHolder& srcBuf, LifoAlloc& alloc,
     607             :                         ScriptSourceObject** sourceObjectOut)
     608             : {
     609           0 :     MOZ_ASSERT(srcBuf.get());
     610           0 :     MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
     611             : 
     612           0 :     CompileOptions options(cx, optionsInput);
     613           0 :     options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
     614           0 :     options.setIsRunOnce(true);
     615           0 :     options.allowHTMLComments = false;
     616             : 
     617           0 :     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
     618           0 :     BytecodeCompiler compiler(cx, alloc, options, srcBuf, emptyGlobalScope);
     619           0 :     AutoInitializeSourceObject autoSSO(compiler, sourceObjectOut);
     620           0 :     return compiler.compileModule();
     621             : }
     622             : 
     623             : ModuleObject*
     624           0 : frontend::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
     625             :                         SourceBufferHolder& srcBuf)
     626             : {
     627           0 :     if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global()))
     628           0 :         return nullptr;
     629             : 
     630           0 :     LifoAlloc& alloc = cx->tempLifoAlloc();
     631           0 :     RootedModuleObject module(cx, CompileModule(cx, options, srcBuf, alloc));
     632           0 :     if (!module)
     633           0 :         return nullptr;
     634             : 
     635             :     // This happens in GlobalHelperThreadState::finishModuleParseTask() when a
     636             :     // module is compiled off thread.
     637           0 :     if (!ModuleObject::Freeze(cx, module))
     638           0 :         return nullptr;
     639             : 
     640           0 :     return module;
     641             : }
     642             : 
     643             : bool
     644        1407 : frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
     645             : {
     646        1407 :     MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
     647             : 
     648        1407 :     uint32_t sourceStartColumn = lazy->scriptSource()->startColumn();
     649        2814 :     CompileOptions options(cx, lazy->version());
     650        1407 :     options.setMutedErrors(lazy->mutedErrors())
     651        2814 :            .setFileAndLine(lazy->filename(), lazy->lineno())
     652        2814 :            .setColumn(lazy->column(), sourceStartColumn)
     653        1407 :            .setNoScriptRval(false)
     654        1407 :            .setSelfHostingMode(false);
     655             : 
     656             :     // Update statistics to find out if we are delazifying just after having
     657             :     // lazified. Note that we are interested in the delta between end of
     658             :     // syntax parsing and start of full parsing, so we do this now rather than
     659             :     // after parsing below.
     660        1407 :     if (!lazy->scriptSource()->parseEnded().IsNull()) {
     661        1278 :         const mozilla::TimeDuration delta = mozilla::TimeStamp::Now() -
     662        1917 :             lazy->scriptSource()->parseEnded();
     663             : 
     664             :         // Differentiate between web-facing and privileged code, to aid
     665             :         // with optimization. Due to the number of calls to this function,
     666             :         // we use `cx->runningWithTrustedPrincipals`, which is fast but
     667             :         // will classify addons alongside with web-facing code.
     668         639 :         const int HISTOGRAM = cx->runningWithTrustedPrincipals()
     669         639 :             ? JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS
     670         639 :             : JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS;
     671         639 :         cx->runtime()->addTelemetry(HISTOGRAM, delta.ToMilliseconds());
     672             :     }
     673             : 
     674        2814 :     UsedNameTracker usedNames(cx);
     675        1407 :     if (!usedNames.init())
     676           0 :         return false;
     677             :     Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
     678             :                                               /* foldConstants = */ true, usedNames, nullptr,
     679        2814 :                                               lazy);
     680        1407 :     if (!parser.checkOptions())
     681           0 :         return false;
     682             : 
     683        2814 :     Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
     684        1407 :     MOZ_ASSERT(!lazy->isLegacyGenerator());
     685        5628 :     ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->toStringStart() + sourceStartColumn,
     686        5628 :                                                   lazy->strict(), lazy->generatorKind(), lazy->asyncKind());
     687        1407 :     if (!pn)
     688           0 :         return false;
     689             : 
     690        2814 :     RootedScriptSource sourceObject(cx, lazy->sourceObject());
     691        1407 :     MOZ_ASSERT(sourceObject);
     692             : 
     693        7035 :     Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
     694        2814 :                                                   lazy->begin(), lazy->end(),
     695        5628 :                                                   lazy->toStringStart(), lazy->toStringEnd()));
     696        1407 :     if (!script)
     697           0 :         return false;
     698             : 
     699        1407 :     if (lazy->isLikelyConstructorWrapper())
     700           0 :         script->setLikelyConstructorWrapper();
     701        1407 :     if (lazy->hasBeenCloned())
     702          22 :         script->setHasBeenCloned();
     703             : 
     704        2814 :     BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
     705        5628 :                         pn->pn_pos, BytecodeEmitter::LazyFunction);
     706        1407 :     if (!bce.init())
     707           0 :         return false;
     708             : 
     709        1407 :     if (!bce.emitFunctionScript(pn->pn_body))
     710           0 :         return false;
     711             : 
     712        1407 :     if (!NameFunctions(cx, pn))
     713           0 :         return false;
     714             : 
     715        1407 :     return true;
     716             : }
     717             : 
     718             : bool
     719          15 : frontend::CompileStandaloneFunction(JSContext* cx, MutableHandleFunction fun,
     720             :                                     const ReadOnlyCompileOptions& options,
     721             :                                     JS::SourceBufferHolder& srcBuf,
     722             :                                     const Maybe<uint32_t>& parameterListEnd,
     723             :                                     HandleScope enclosingScope /* = nullptr */)
     724             : {
     725          30 :     RootedScope scope(cx, enclosingScope);
     726          15 :     if (!scope)
     727           4 :         scope = &cx->global()->emptyGlobalScope();
     728             : 
     729          30 :     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, scope);
     730          30 :     return compiler.compileStandaloneFunction(fun, NotGenerator, SyncFunction, parameterListEnd);
     731             : }
     732             : 
     733             : bool
     734           0 : frontend::CompileStandaloneGenerator(JSContext* cx, MutableHandleFunction fun,
     735             :                                      const ReadOnlyCompileOptions& options,
     736             :                                      JS::SourceBufferHolder& srcBuf,
     737             :                                      const Maybe<uint32_t>& parameterListEnd)
     738             : {
     739           0 :     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
     740             : 
     741           0 :     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
     742           0 :     return compiler.compileStandaloneFunction(fun, StarGenerator, SyncFunction, parameterListEnd);
     743             : }
     744             : 
     745             : bool
     746           0 : frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
     747             :                                          const ReadOnlyCompileOptions& options,
     748             :                                          JS::SourceBufferHolder& srcBuf,
     749             :                                          const Maybe<uint32_t>& parameterListEnd)
     750             : {
     751           0 :     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
     752             : 
     753           0 :     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
     754           0 :     return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd);
     755             : }
     756             : 
     757             : bool
     758           0 : frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
     759             :                                           const ReadOnlyCompileOptions& options,
     760             :                                           JS::SourceBufferHolder& srcBuf,
     761             :                                           const Maybe<uint32_t>& parameterListEnd)
     762             : {
     763           0 :     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
     764             : 
     765           0 :     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
     766           0 :     return compiler.compileStandaloneFunction(fun, StarGenerator, AsyncFunction, parameterListEnd);
     767             : }

Generated by: LCOV version 1.13