summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/Stencil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/Stencil.cpp')
-rw-r--r--js/src/frontend/Stencil.cpp2261
1 files changed, 2261 insertions, 0 deletions
diff --git a/js/src/frontend/Stencil.cpp b/js/src/frontend/Stencil.cpp
new file mode 100644
index 0000000000..f1562d132b
--- /dev/null
+++ b/js/src/frontend/Stencil.cpp
@@ -0,0 +1,2261 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "frontend/Stencil.h"
+
+#include "mozilla/OperatorNewExtensions.h" // mozilla::KnownNotNull
+#include "mozilla/RefPtr.h" // RefPtr
+#include "mozilla/Sprintf.h" // SprintfLiteral
+
+#include "frontend/AbstractScopePtr.h" // ScopeIndex
+#include "frontend/BytecodeCompilation.h" // CanLazilyParse
+#include "frontend/BytecodeSection.h" // EmitScriptThingsVector
+#include "frontend/CompilationInfo.h" // CompilationStencil, CompilationStencilSet, CompilationGCOutput
+#include "frontend/SharedContext.h"
+#include "gc/AllocKind.h" // gc::AllocKind
+#include "js/CallArgs.h" // JSNative
+#include "js/RootingAPI.h" // Rooted
+#include "js/Transcoding.h" // JS::TranscodeBuffer
+#include "js/Value.h" // ObjectValue
+#include "js/WasmModule.h" // JS::WasmModule
+#include "vm/EnvironmentObject.h"
+#include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind
+#include "vm/JSContext.h" // JSContext
+#include "vm/JSFunction.h" // JSFunction, GetFunctionPrototype, NewFunctionWithProto
+#include "vm/JSObject.h" // JSObject
+#include "vm/JSONPrinter.h" // js::JSONPrinter
+#include "vm/JSScript.h" // BaseScript, JSScript
+#include "vm/ObjectGroup.h" // TenuredObject
+#include "vm/Printer.h" // js::Fprinter
+#include "vm/Scope.h" // Scope, ScopeKindString
+#include "vm/StencilEnums.h" // ImmutableScriptFlagsEnum
+#include "vm/StringType.h" // JSAtom, js::CopyChars
+#include "vm/Xdr.h" // XDRMode, XDRResult, XDREncoder
+#include "wasm/AsmJS.h" // InstantiateAsmJS
+#include "wasm/WasmModule.h" // wasm::Module
+
+#include "vm/JSFunction-inl.h" // JSFunction::create
+
+using namespace js;
+using namespace js::frontend;
+
+AbstractScopePtr ScopeStencil::enclosing(
+ CompilationState& compilationState) const {
+ if (hasEnclosing()) {
+ return AbstractScopePtr(compilationState, enclosing());
+ }
+
+ return AbstractScopePtr(compilationState.input.enclosingScope);
+}
+
+Scope* ScopeStencil::enclosingExistingScope(
+ const CompilationInput& input, const CompilationGCOutput& gcOutput) const {
+ if (hasEnclosing()) {
+ Scope* result = gcOutput.scopes[enclosing()];
+ MOZ_ASSERT(result, "Scope must already exist to use this method");
+ return result;
+ }
+
+ return input.enclosingScope;
+}
+
+Scope* ScopeStencil::createScope(JSContext* cx, CompilationInput& input,
+ CompilationGCOutput& gcOutput,
+ BaseParserScopeData* baseScopeData) const {
+ Scope* scope = nullptr;
+ switch (kind()) {
+ case ScopeKind::Function: {
+ using ScopeType = FunctionScope;
+ MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
+ scope = createSpecificScope<ScopeType, CallObject>(cx, input, gcOutput,
+ baseScopeData);
+ break;
+ }
+ case ScopeKind::Lexical:
+ case ScopeKind::SimpleCatch:
+ case ScopeKind::Catch:
+ case ScopeKind::NamedLambda:
+ case ScopeKind::StrictNamedLambda:
+ case ScopeKind::FunctionLexical:
+ case ScopeKind::ClassBody: {
+ using ScopeType = LexicalScope;
+ MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
+ scope = createSpecificScope<ScopeType, LexicalEnvironmentObject>(
+ cx, input, gcOutput, baseScopeData);
+ break;
+ }
+ case ScopeKind::FunctionBodyVar: {
+ using ScopeType = VarScope;
+ MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
+ scope = createSpecificScope<ScopeType, VarEnvironmentObject>(
+ cx, input, gcOutput, baseScopeData);
+ break;
+ }
+ case ScopeKind::Global:
+ case ScopeKind::NonSyntactic: {
+ using ScopeType = GlobalScope;
+ MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
+ scope = createSpecificScope<ScopeType, std::nullptr_t>(
+ cx, input, gcOutput, baseScopeData);
+ break;
+ }
+ case ScopeKind::Eval:
+ case ScopeKind::StrictEval: {
+ using ScopeType = EvalScope;
+ MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
+ scope = createSpecificScope<ScopeType, VarEnvironmentObject>(
+ cx, input, gcOutput, baseScopeData);
+ break;
+ }
+ case ScopeKind::Module: {
+ using ScopeType = ModuleScope;
+ MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
+ scope = createSpecificScope<ScopeType, ModuleEnvironmentObject>(
+ cx, input, gcOutput, baseScopeData);
+ break;
+ }
+ case ScopeKind::With: {
+ using ScopeType = WithScope;
+ MOZ_ASSERT(matchScopeKind<ScopeType>(kind()));
+ scope = createSpecificScope<ScopeType, std::nullptr_t>(
+ cx, input, gcOutput, baseScopeData);
+ break;
+ }
+ case ScopeKind::WasmFunction:
+ case ScopeKind::WasmInstance: {
+ MOZ_CRASH("Unexpected deferred type");
+ }
+ }
+ return scope;
+}
+
+static bool CreateLazyScript(JSContext* cx, CompilationInput& input,
+ BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput,
+ const ScriptStencil& script,
+ const ScriptStencilExtra& scriptExtra,
+ ScriptIndex scriptIndex, HandleFunction function) {
+ Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject);
+
+ size_t ngcthings = script.gcThingsLength;
+
+ Rooted<BaseScript*> lazy(
+ cx, BaseScript::CreateRawLazy(cx, ngcthings, function, sourceObject,
+ scriptExtra.extent,
+ scriptExtra.immutableFlags));
+ if (!lazy) {
+ return false;
+ }
+
+ if (ngcthings) {
+ if (!EmitScriptThingsVector(cx, input, stencil, gcOutput,
+ script.gcthings(stencil),
+ lazy->gcthingsForInit())) {
+ return false;
+ }
+ }
+
+ function->initScript(lazy);
+
+ return true;
+}
+
+// Parser-generated functions with the same prototype will share the same shape
+// and group. By computing the correct values up front, we can save a lot of
+// time in the Object creation code. For simplicity, we focus only on plain
+// synchronous functions which are by far the most common.
+//
+// This bypasses the `NewObjectCache`, but callers are expected to retrieve a
+// valid group and shape from the appropriate de-duplication tables.
+//
+// NOTE: Keep this in sync with `js::NewFunctionWithProto`.
+static JSFunction* CreateFunctionFast(JSContext* cx, CompilationInput& input,
+ HandleObjectGroup group,
+ HandleShape shape,
+ const ScriptStencil& script,
+ const ScriptStencilExtra& scriptExtra) {
+ MOZ_ASSERT(
+ !scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsAsync));
+ MOZ_ASSERT(!scriptExtra.immutableFlags.hasFlag(
+ ImmutableScriptFlagsEnum::IsGenerator));
+ MOZ_ASSERT(!script.functionFlags.isAsmJSNative());
+
+ FunctionFlags flags = script.functionFlags;
+ gc::AllocKind allocKind = flags.isExtended()
+ ? gc::AllocKind::FUNCTION_EXTENDED
+ : gc::AllocKind::FUNCTION;
+
+ JSFunction* fun;
+ JS_TRY_VAR_OR_RETURN_NULL(
+ cx, fun,
+ JSFunction::create(cx, allocKind, gc::TenuredHeap, shape, group));
+
+ fun->setArgCount(scriptExtra.nargs);
+ fun->setFlags(flags);
+
+ fun->initScript(nullptr);
+ fun->initEnvironment(nullptr);
+
+ if (script.functionAtom) {
+ JSAtom* atom = input.atomCache.getExistingAtomAt(cx, script.functionAtom);
+ MOZ_ASSERT(atom);
+ fun->initAtom(atom);
+ }
+
+ if (flags.isExtended()) {
+ fun->initializeExtended();
+ }
+
+ return fun;
+}
+
+static JSFunction* CreateFunction(JSContext* cx, CompilationInput& input,
+ BaseCompilationStencil& stencil,
+ const ScriptStencil& script,
+ const ScriptStencilExtra& scriptExtra,
+ ScriptIndex functionIndex) {
+ GeneratorKind generatorKind =
+ scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsGenerator)
+ ? GeneratorKind::Generator
+ : GeneratorKind::NotGenerator;
+ FunctionAsyncKind asyncKind =
+ scriptExtra.immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsAsync)
+ ? FunctionAsyncKind::AsyncFunction
+ : FunctionAsyncKind::SyncFunction;
+
+ // Determine the new function's proto. This must be done for singleton
+ // functions.
+ RootedObject proto(cx);
+ if (!GetFunctionPrototype(cx, generatorKind, asyncKind, &proto)) {
+ return nullptr;
+ }
+
+ gc::AllocKind allocKind = script.functionFlags.isExtended()
+ ? gc::AllocKind::FUNCTION_EXTENDED
+ : gc::AllocKind::FUNCTION;
+ bool isAsmJS = script.functionFlags.isAsmJSNative();
+
+ JSNative maybeNative = isAsmJS ? InstantiateAsmJS : nullptr;
+
+ RootedAtom displayAtom(cx);
+ if (script.functionAtom) {
+ displayAtom.set(input.atomCache.getExistingAtomAt(cx, script.functionAtom));
+ MOZ_ASSERT(displayAtom);
+ }
+ RootedFunction fun(
+ cx, NewFunctionWithProto(cx, maybeNative, scriptExtra.nargs,
+ script.functionFlags, nullptr, displayAtom,
+ proto, allocKind, TenuredObject));
+ if (!fun) {
+ return nullptr;
+ }
+
+ if (isAsmJS) {
+ RefPtr<const JS::WasmModule> asmJS =
+ stencil.asCompilationStencil().asmJS.lookup(functionIndex)->value();
+
+ JSObject* moduleObj = asmJS->createObjectForAsmJS(cx);
+ if (!moduleObj) {
+ return nullptr;
+ }
+
+ fun->setExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT,
+ ObjectValue(*moduleObj));
+ }
+
+ return fun;
+}
+
+static bool InstantiateAtoms(JSContext* cx, CompilationInput& input,
+ BaseCompilationStencil& stencil) {
+ return InstantiateMarkedAtoms(cx, stencil.parserAtomData, input.atomCache);
+}
+
+static bool InstantiateScriptSourceObject(JSContext* cx,
+ CompilationInput& input,
+ CompilationGCOutput& gcOutput) {
+ MOZ_ASSERT(input.source());
+
+ gcOutput.sourceObject = ScriptSourceObject::create(cx, input.source());
+ if (!gcOutput.sourceObject) {
+ return false;
+ }
+
+ // Off-thread compilations do all their GC heap allocation, including the
+ // SSO, in a temporary compartment. Hence, for the SSO to refer to the
+ // gc-heap-allocated values in |options|, it would need cross-compartment
+ // wrappers from the temporary compartment to the real compartment --- which
+ // would then be inappropriate once we merged the temporary and real
+ // compartments.
+ //
+ // Instead, we put off populating those SSO slots in off-thread compilations
+ // until after we've merged compartments.
+ if (!cx->isHelperThreadContext()) {
+ Rooted<ScriptSourceObject*> sourceObject(cx, gcOutput.sourceObject);
+ if (!ScriptSourceObject::initFromOptions(cx, sourceObject, input.options)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Instantiate ModuleObject. Further initialization is done after the associated
+// BaseScript is instantiated in InstantiateTopLevel.
+static bool InstantiateModuleObject(JSContext* cx, CompilationInput& input,
+ CompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ MOZ_ASSERT(stencil.scriptExtra[CompilationStencil::TopLevelIndex].isModule());
+
+ gcOutput.module = ModuleObject::create(cx);
+ if (!gcOutput.module) {
+ return false;
+ }
+
+ Rooted<ModuleObject*> module(cx, gcOutput.module);
+ return stencil.moduleMetadata->initModule(cx, input.atomCache, module);
+}
+
+// Instantiate JSFunctions for each FunctionBox.
+static bool InstantiateFunctions(JSContext* cx, CompilationInput& input,
+ BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ using ImmutableFlags = ImmutableScriptFlagsEnum;
+
+ if (!gcOutput.functions.resize(stencil.scriptData.size())) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ // Most JSFunctions will be have the same Shape / Group so we can compute it
+ // now to allow fast object creation. Generators / Async will use the slow
+ // path instead.
+ RootedObject proto(cx,
+ GlobalObject::getOrCreatePrototype(cx, JSProto_Function));
+ if (!proto) {
+ return false;
+ }
+ RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(
+ cx, &JSFunction::class_, TaggedProto(proto)));
+ if (!group) {
+ return false;
+ }
+ RootedShape shape(
+ cx, EmptyShape::getInitialShape(cx, &JSFunction::class_,
+ TaggedProto(proto), /* nfixed = */ 0,
+ /* objectFlags = */ 0));
+ if (!shape) {
+ return false;
+ }
+
+ for (auto item :
+ CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
+ const auto& scriptStencil = item.script;
+ const auto& scriptExtra = (*item.scriptExtra);
+ auto index = item.index;
+
+ MOZ_ASSERT(!item.function);
+
+ // Plain functions can use a fast path.
+ bool useFastPath =
+ !scriptExtra.immutableFlags.hasFlag(ImmutableFlags::IsAsync) &&
+ !scriptExtra.immutableFlags.hasFlag(ImmutableFlags::IsGenerator) &&
+ !scriptStencil.functionFlags.isAsmJSNative();
+
+ JSFunction* fun = useFastPath
+ ? CreateFunctionFast(cx, input, group, shape,
+ scriptStencil, scriptExtra)
+ : CreateFunction(cx, input, stencil, scriptStencil,
+ scriptExtra, index);
+ if (!fun) {
+ return false;
+ }
+
+ gcOutput.functions[index] = fun;
+ }
+
+ return true;
+}
+
+// Instantiate Scope for each ScopeStencil.
+//
+// This should be called after InstantiateFunctions, given FunctionScope needs
+// associated JSFunction pointer, and also should be called before
+// InstantiateScriptStencils, given JSScript needs Scope pointer in gc things.
+static bool InstantiateScopes(JSContext* cx, CompilationInput& input,
+ BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ // While allocating Scope object from ScopeStencil, Scope object for the
+ // enclosing Scope should already be allocated.
+ //
+ // Enclosing scope of ScopeStencil can be either ScopeStencil or Scope*
+ // pointer.
+ //
+ // If the enclosing scope is ScopeStencil, it's guaranteed to be earlier
+ // element in stencil.scopeData, because enclosing_ field holds
+ // index into it, and newly created ScopeStencil is pushed back to the vector.
+ //
+ // If the enclosing scope is Scope*, it's CompilationInput.enclosingScope.
+
+ MOZ_ASSERT(stencil.scopeData.size() == stencil.scopeNames.size());
+ size_t scopeCount = stencil.scopeData.size();
+ for (size_t i = 0; i < scopeCount; i++) {
+ Scope* scope = stencil.scopeData[i].createScope(cx, input, gcOutput,
+ stencil.scopeNames[i]);
+ if (!scope) {
+ return false;
+ }
+ gcOutput.scopes.infallibleAppend(scope);
+ }
+
+ return true;
+}
+
+// Instantiate js::BaseScripts from ScriptStencils for inner functions of the
+// compilation. Note that standalone functions and functions being delazified
+// are handled below with other top-levels.
+static bool InstantiateScriptStencils(JSContext* cx, CompilationInput& input,
+ CompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ MOZ_ASSERT(input.lazy == nullptr);
+
+ Rooted<JSFunction*> fun(cx);
+ for (auto item :
+ CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
+ auto& scriptStencil = item.script;
+ auto* scriptExtra = item.scriptExtra;
+ fun = item.function;
+ auto index = item.index;
+ if (scriptStencil.hasSharedData()) {
+ // If the function was not referenced by enclosing script's bytecode, we
+ // do not generate a BaseScript for it. For example, `(function(){});`.
+ //
+ // `wasFunctionEmitted` is false also for standalone functions. They are
+ // handled in InstantiateTopLevel.
+ if (!scriptStencil.wasFunctionEmitted()) {
+ continue;
+ }
+
+ RootedScript script(
+ cx, JSScript::fromStencil(cx, input, stencil, gcOutput, index));
+ if (!script) {
+ return false;
+ }
+
+ // NOTE: Inner functions can be marked `allowRelazify` after merging
+ // a stencil for delazification into the top-level stencil.
+ if (scriptStencil.allowRelazify()) {
+ MOZ_ASSERT(script->isRelazifiable());
+ script->setAllowRelazify();
+ }
+ } else if (scriptStencil.functionFlags.isAsmJSNative()) {
+ MOZ_ASSERT(fun->isAsmJSNative());
+ } else {
+ MOZ_ASSERT(fun->isIncomplete());
+ if (!CreateLazyScript(cx, input, stencil, gcOutput, scriptStencil,
+ *scriptExtra, index, fun)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+// Instantiate the Stencil for the top-level script of the compilation. This
+// includes standalone functions and functions being delazified.
+static bool InstantiateTopLevel(JSContext* cx, CompilationInput& input,
+ BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ const ScriptStencil& scriptStencil =
+ stencil.scriptData[CompilationStencil::TopLevelIndex];
+
+ // Top-level asm.js does not generate a JSScript.
+ if (scriptStencil.functionFlags.isAsmJSNative()) {
+ return true;
+ }
+
+ MOZ_ASSERT(scriptStencil.hasSharedData());
+ MOZ_ASSERT(stencil.sharedData.get(CompilationStencil::TopLevelIndex));
+
+ if (input.lazy) {
+ RootedScript script(cx, JSScript::CastFromLazy(input.lazy));
+ if (!JSScript::fullyInitFromStencil(cx, input, stencil, gcOutput, script,
+ CompilationStencil::TopLevelIndex)) {
+ return false;
+ }
+
+ if (scriptStencil.allowRelazify()) {
+ MOZ_ASSERT(script->isRelazifiable());
+ script->setAllowRelazify();
+ }
+
+ gcOutput.script = script;
+ return true;
+ }
+
+ gcOutput.script =
+ JSScript::fromStencil(cx, input, stencil.asCompilationStencil(), gcOutput,
+ CompilationStencil::TopLevelIndex);
+ if (!gcOutput.script) {
+ return false;
+ }
+
+ if (scriptStencil.allowRelazify()) {
+ MOZ_ASSERT(gcOutput.script->isRelazifiable());
+ gcOutput.script->setAllowRelazify();
+ }
+
+ const ScriptStencilExtra& scriptExtra =
+ stencil.asCompilationStencil()
+ .scriptExtra[CompilationStencil::TopLevelIndex];
+
+ // Finish initializing the ModuleObject if needed.
+ if (scriptExtra.isModule()) {
+ RootedScript script(cx, gcOutput.script);
+
+ gcOutput.module->initScriptSlots(script);
+ gcOutput.module->initStatusSlot();
+
+ RootedModuleObject module(cx, gcOutput.module);
+ if (!ModuleObject::createEnvironment(cx, module)) {
+ return false;
+ }
+
+ // Off-thread compilation with parseGlobal will freeze the module object
+ // in GlobalHelperThreadState::finishSingleParseTask instead.
+ if (!cx->isHelperThreadContext()) {
+ if (!ModuleObject::Freeze(cx, module)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+// When a function is first referenced by enclosing script's bytecode, we need
+// to update it with information determined by the BytecodeEmitter. This applies
+// to both initial and delazification parses. The functions being update may or
+// may not have bytecode at this point.
+static void UpdateEmittedInnerFunctions(JSContext* cx, CompilationInput& input,
+ BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ for (auto item :
+ CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
+ auto& scriptStencil = item.script;
+ auto& fun = item.function;
+ if (!scriptStencil.wasFunctionEmitted()) {
+ continue;
+ }
+
+ if (scriptStencil.functionFlags.isAsmJSNative() ||
+ fun->baseScript()->hasBytecode()) {
+ // Non-lazy inner functions don't use the enclosingScope_ field.
+ MOZ_ASSERT(!scriptStencil.hasLazyFunctionEnclosingScopeIndex());
+ } else {
+ // Apply updates from FunctionEmitter::emitLazy().
+ BaseScript* script = fun->baseScript();
+
+ ScopeIndex index = scriptStencil.lazyFunctionEnclosingScopeIndex();
+ Scope* scope = gcOutput.scopes[index];
+ script->setEnclosingScope(scope);
+
+ if (scriptStencil.hasMemberInitializers()) {
+ script->setMemberInitializers(scriptStencil.memberInitializers());
+ }
+
+ // Inferred and Guessed names are computed by BytecodeEmitter and so may
+ // need to be applied to existing JSFunctions during delazification.
+ if (fun->displayAtom() == nullptr) {
+ JSAtom* funcAtom = nullptr;
+ if (scriptStencil.functionFlags.hasInferredName() ||
+ scriptStencil.functionFlags.hasGuessedAtom()) {
+ funcAtom =
+ input.atomCache.getExistingAtomAt(cx, scriptStencil.functionAtom);
+ MOZ_ASSERT(funcAtom);
+ }
+ if (scriptStencil.functionFlags.hasInferredName()) {
+ fun->setInferredName(funcAtom);
+ }
+ if (scriptStencil.functionFlags.hasGuessedAtom()) {
+ fun->setGuessedAtom(funcAtom);
+ }
+ }
+ }
+ }
+}
+
+// During initial parse we must link lazy-functions-inside-lazy-functions to
+// their enclosing script.
+static void LinkEnclosingLazyScript(BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ for (auto item :
+ CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
+ auto& scriptStencil = item.script;
+ auto& fun = item.function;
+ if (!scriptStencil.functionFlags.hasBaseScript()) {
+ continue;
+ }
+
+ if (fun->baseScript()->hasBytecode()) {
+ continue;
+ }
+
+ BaseScript* script = fun->baseScript();
+ MOZ_ASSERT(!script->hasBytecode());
+
+ for (auto inner : script->gcthings()) {
+ if (!inner.is<JSObject>()) {
+ continue;
+ }
+ inner.as<JSObject>().as<JSFunction>().setEnclosingLazyScript(script);
+ }
+ }
+}
+
+#ifdef DEBUG
+// Some fields aren't used in delazification, given the target functions and
+// scripts are already instantiated, but they still should match.
+static void AssertDelazificationFieldsMatch(BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ for (auto item :
+ CompilationStencil::functionScriptStencils(stencil, gcOutput)) {
+ auto& scriptStencil = item.script;
+ auto* scriptExtra = item.scriptExtra;
+ auto& fun = item.function;
+
+ MOZ_ASSERT(scriptExtra == nullptr);
+
+ // Names are updated by UpdateInnerFunctions.
+ constexpr uint16_t HAS_INFERRED_NAME =
+ uint16_t(FunctionFlags::Flags::HAS_INFERRED_NAME);
+ constexpr uint16_t HAS_GUESSED_ATOM =
+ uint16_t(FunctionFlags::Flags::HAS_GUESSED_ATOM);
+ constexpr uint16_t MUTABLE_FLAGS =
+ uint16_t(FunctionFlags::Flags::MUTABLE_FLAGS);
+ constexpr uint16_t acceptableDifferenceForFunction =
+ HAS_INFERRED_NAME | HAS_GUESSED_ATOM | MUTABLE_FLAGS;
+
+ MOZ_ASSERT((fun->flags().toRaw() | acceptableDifferenceForFunction) ==
+ (scriptStencil.functionFlags.toRaw() |
+ acceptableDifferenceForFunction));
+
+ // Delazification shouldn't delazify inner scripts.
+ MOZ_ASSERT_IF(item.index == CompilationStencil::TopLevelIndex,
+ scriptStencil.hasSharedData());
+ MOZ_ASSERT_IF(item.index > CompilationStencil::TopLevelIndex,
+ !scriptStencil.hasSharedData());
+ }
+}
+#endif // DEBUG
+
+// When delazifying, use the existing JSFunctions. The initial and delazifying
+// parse are required to generate the same sequence of functions for lazy
+// parsing to work at all.
+static void FunctionsFromExistingLazy(CompilationInput& input,
+ CompilationGCOutput& gcOutput) {
+ MOZ_ASSERT(gcOutput.functions.empty());
+ gcOutput.functions.infallibleAppend(input.lazy->function());
+
+ for (JS::GCCellPtr elem : input.lazy->gcthings()) {
+ if (!elem.is<JSObject>()) {
+ continue;
+ }
+ JSFunction* fun = &elem.as<JSObject>().as<JSFunction>();
+ gcOutput.functions.infallibleAppend(fun);
+ }
+}
+
+/* static */
+bool CompilationStencil::instantiateStencils(JSContext* cx,
+ CompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ if (!stencil.preparationIsPerformed) {
+ if (!prepareForInstantiate(cx, stencil, gcOutput)) {
+ return false;
+ }
+ }
+
+ return instantiateStencilsAfterPreparation(cx, stencil.input, stencil,
+ gcOutput);
+}
+
+/* static */
+bool CompilationStencil::instantiateStencilsAfterPreparation(
+ JSContext* cx, CompilationInput& input, BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ // Distinguish between the initial (possibly lazy) compile and any subsequent
+ // delazification compiles. Delazification will update existing GC things.
+ bool isInitialParse = (input.lazy == nullptr);
+
+ // Phase 1: Instantate JSAtoms.
+ if (!InstantiateAtoms(cx, input, stencil)) {
+ return false;
+ }
+
+ // Phase 2: Instantiate ScriptSourceObject, ModuleObject, JSFunctions.
+ if (isInitialParse) {
+ CompilationStencil& initialStencil = stencil.asCompilationStencil();
+
+ if (!InstantiateScriptSourceObject(cx, input, gcOutput)) {
+ return false;
+ }
+
+ if (initialStencil.moduleMetadata) {
+ // The enclosing script of a module is always the global scope. Fetch the
+ // scope of the current global and update input data.
+ MOZ_ASSERT(input.enclosingScope == nullptr);
+ input.enclosingScope = &cx->global()->emptyGlobalScope();
+ MOZ_ASSERT(input.enclosingScope->environmentChainLength() ==
+ ModuleScope::EnclosingEnvironmentChainLength);
+
+ if (!InstantiateModuleObject(cx, input, initialStencil, gcOutput)) {
+ return false;
+ }
+ }
+
+ if (!InstantiateFunctions(cx, input, stencil, gcOutput)) {
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(
+ stencil.scriptData[CompilationStencil::TopLevelIndex].isFunction());
+
+ // FunctionKey is used when caching to map a delazification stencil to a
+ // specific lazy script. It is not used by instantiation, but we should
+ // ensure it is correctly defined.
+ MOZ_ASSERT(stencil.functionKey ==
+ BaseCompilationStencil::toFunctionKey(input.lazy->extent()));
+
+ FunctionsFromExistingLazy(input, gcOutput);
+ MOZ_ASSERT(gcOutput.functions.length() == stencil.scriptData.size());
+
+#ifdef DEBUG
+ AssertDelazificationFieldsMatch(stencil, gcOutput);
+#endif
+ }
+
+ // Phase 3: Instantiate js::Scopes.
+ if (!InstantiateScopes(cx, input, stencil, gcOutput)) {
+ return false;
+ }
+
+ // Phase 4: Instantiate (inner) BaseScripts.
+ if (isInitialParse) {
+ if (!InstantiateScriptStencils(cx, input, stencil.asCompilationStencil(),
+ gcOutput)) {
+ return false;
+ }
+ }
+
+ // Phase 5: Finish top-level handling
+ if (!InstantiateTopLevel(cx, input, stencil, gcOutput)) {
+ return false;
+ }
+
+ // !! Must be infallible from here forward !!
+
+ // Phase 6: Update lazy scripts.
+ if (CanLazilyParse(input.options)) {
+ UpdateEmittedInnerFunctions(cx, input, stencil, gcOutput);
+
+ if (isInitialParse) {
+ LinkEnclosingLazyScript(stencil, gcOutput);
+ }
+ }
+
+ return true;
+}
+
+bool CompilationStencilSet::buildDelazificationIndices(JSContext* cx) {
+ // Standalone-functions are not supported by XDR.
+ MOZ_ASSERT(!scriptData[0].isFunction());
+
+ // If no delazifications, we are done.
+ if (delazifications.empty()) {
+ return true;
+ }
+
+ if (!delazificationIndices.resize(delazifications.length())) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ HashMap<BaseCompilationStencil::FunctionKey, size_t> keyToIndex(cx);
+ if (!keyToIndex.reserve(delazifications.length())) {
+ return false;
+ }
+
+ for (size_t i = 0; i < delazifications.length(); i++) {
+ const auto& delazification = delazifications[i];
+ auto key = delazification.functionKey;
+ keyToIndex.putNewInfallible(key, i);
+ }
+
+ MOZ_ASSERT(keyToIndex.count() == delazifications.length());
+
+ for (size_t i = 1; i < scriptData.size(); i++) {
+ auto key = BaseCompilationStencil::toFunctionKey(scriptExtra[i].extent);
+ auto ptr = keyToIndex.lookup(key);
+ if (!ptr) {
+ continue;
+ }
+ delazificationIndices[ptr->value()] = ScriptIndex(i);
+ }
+
+ return true;
+}
+
+bool CompilationStencilSet::instantiateStencils(
+ JSContext* cx, CompilationGCOutput& gcOutput,
+ CompilationGCOutput& gcOutputForDelazification) {
+ if (!prepareForInstantiate(cx, gcOutput, gcOutputForDelazification)) {
+ return false;
+ }
+
+ return instantiateStencilsAfterPreparation(cx, gcOutput,
+ gcOutputForDelazification);
+}
+
+bool CompilationStencilSet::instantiateStencilsAfterPreparation(
+ JSContext* cx, CompilationGCOutput& gcOutput,
+ CompilationGCOutput& gcOutputForDelazification) {
+ if (!CompilationStencil::instantiateStencilsAfterPreparation(cx, input, *this,
+ gcOutput)) {
+ return false;
+ }
+
+ CompilationAtomCache::AtomCacheVector reusableAtomCache;
+ input.atomCache.releaseBuffer(reusableAtomCache);
+
+ for (size_t i = 0; i < delazifications.length(); i++) {
+ auto& delazification = delazifications[i];
+ auto index = delazificationIndices[i];
+
+ JSFunction* fun = gcOutput.functions[index];
+ MOZ_ASSERT(fun);
+
+ BaseScript* lazy = fun->baseScript();
+ MOZ_ASSERT(!lazy->hasBytecode());
+
+ if (!lazy->isReadyForDelazification()) {
+ MOZ_ASSERT(false, "Delazification target is not ready. Bad XDR?");
+ continue;
+ }
+
+ Rooted<CompilationInput> delazificationInput(
+ cx, CompilationInput(input.options));
+ delazificationInput.get().initFromLazy(lazy);
+
+ delazificationInput.get().atomCache.stealBuffer(reusableAtomCache);
+
+ if (!CompilationStencil::prepareGCOutputForInstantiate(
+ cx, delazification, gcOutputForDelazification)) {
+ return false;
+ }
+ if (!CompilationStencil::instantiateStencilsAfterPreparation(
+ cx, delazificationInput.get(), delazification,
+ gcOutputForDelazification)) {
+ return false;
+ }
+
+ // Destroy elements, without unreserving.
+ gcOutputForDelazification.functions.clear();
+ gcOutputForDelazification.scopes.clear();
+
+ delazificationInput.get().atomCache.releaseBuffer(reusableAtomCache);
+ }
+
+ input.atomCache.stealBuffer(reusableAtomCache);
+
+ return true;
+}
+
+/* static */
+bool CompilationStencil::prepareInputAndStencilForInstantiate(
+ JSContext* cx, CompilationInput& input, BaseCompilationStencil& stencil) {
+ if (!input.atomCache.allocate(cx, stencil.parserAtomData.size())) {
+ return false;
+ }
+
+ return true;
+}
+
+/* static */
+bool CompilationStencil::prepareGCOutputForInstantiate(
+ JSContext* cx, BaseCompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ if (!gcOutput.functions.reserve(stencil.scriptData.size())) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ if (!gcOutput.scopes.reserve(stencil.scopeData.size())) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
+/* static */
+bool CompilationStencil::prepareForInstantiate(JSContext* cx,
+ CompilationStencil& stencil,
+ CompilationGCOutput& gcOutput) {
+ auto& input = stencil.input;
+
+ if (!prepareInputAndStencilForInstantiate(cx, input, stencil)) {
+ return false;
+ }
+ if (!prepareGCOutputForInstantiate(cx, stencil, gcOutput)) {
+ return false;
+ }
+
+ stencil.preparationIsPerformed = true;
+ return true;
+}
+
+bool CompilationStencilSet::prepareForInstantiate(
+ JSContext* cx, CompilationGCOutput& gcOutput,
+ CompilationGCOutput& gcOutputForDelazification) {
+ if (!CompilationStencil::prepareForInstantiate(cx, *this, gcOutput)) {
+ return false;
+ }
+
+ size_t maxScriptDataLength = 0;
+ size_t maxScopeDataLength = 0;
+ size_t maxParserAtomDataLength = 0;
+ for (auto& delazification : delazifications) {
+ if (maxParserAtomDataLength < delazification.parserAtomData.size()) {
+ maxParserAtomDataLength = delazification.parserAtomData.size();
+ }
+ if (maxScriptDataLength < delazification.scriptData.size()) {
+ maxScriptDataLength = delazification.scriptData.size();
+ }
+ if (maxScopeDataLength < delazification.scopeData.size()) {
+ maxScopeDataLength = delazification.scopeData.size();
+ }
+ }
+
+ if (!input.atomCache.extendIfNecessary(cx, maxParserAtomDataLength)) {
+ return false;
+ }
+ if (!gcOutput.functions.reserve(maxScriptDataLength)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ if (!gcOutput.scopes.reserve(maxScopeDataLength)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ if (!buildDelazificationIndices(cx)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool CompilationStencil::serializeStencils(JSContext* cx,
+ JS::TranscodeBuffer& buf,
+ bool* succeededOut) {
+ if (succeededOut) {
+ *succeededOut = false;
+ }
+ XDRIncrementalStencilEncoder encoder(cx);
+
+ XDRResult res = encoder.codeStencil(*this);
+ if (res.isErr()) {
+ if (res.unwrapErr() & JS::TranscodeResult_Failure) {
+ buf.clear();
+ return true;
+ }
+ MOZ_ASSERT(res.unwrapErr() == JS::TranscodeResult_Throw);
+
+ return false;
+ }
+
+ // Linearize the encoder, return empty buffer on failure.
+ res = encoder.linearize(buf, input.source());
+ if (res.isErr()) {
+ MOZ_ASSERT(cx->isThrowingOutOfMemory());
+ buf.clear();
+ return false;
+ }
+
+ if (succeededOut) {
+ *succeededOut = true;
+ }
+ return true;
+}
+
+bool CompilationStencilSet::deserializeStencils(JSContext* cx,
+ const JS::TranscodeRange& range,
+ bool* succeededOut) {
+ if (succeededOut) {
+ *succeededOut = false;
+ }
+ MOZ_ASSERT(parserAtomData.empty());
+ XDRStencilDecoder decoder(cx, &input.options, range);
+
+ XDRResult res = decoder.codeStencils(*this);
+ if (res.isErr()) {
+ if (res.unwrapErr() & JS::TranscodeResult_Failure) {
+ return true;
+ }
+ MOZ_ASSERT(res.unwrapErr() == JS::TranscodeResult_Throw);
+
+ return false;
+ }
+
+ if (succeededOut) {
+ *succeededOut = true;
+ }
+ return true;
+}
+
+CompilationState::CompilationState(
+ JSContext* cx, LifoAllocScope& frontendAllocScope,
+ const JS::ReadOnlyCompileOptions& options, CompilationStencil& stencil,
+ InheritThis inheritThis /* = InheritThis::No */,
+ Scope* enclosingScope /* = nullptr */,
+ JSObject* enclosingEnv /* = nullptr */)
+ : directives(options.forceStrictMode()),
+ scopeContext(cx, inheritThis, enclosingScope, enclosingEnv),
+ usedNames(cx),
+ allocScope(frontendAllocScope),
+ input(stencil.input),
+ parserAtoms(cx->runtime(), stencil.alloc) {}
+
+SharedDataContainer::~SharedDataContainer() {
+ if (isEmpty()) {
+ // Nothing to do.
+ } else if (isSingle()) {
+ asSingle()->Release();
+ } else if (isVector()) {
+ js_delete(asVector());
+ } else {
+ MOZ_ASSERT(isMap());
+ js_delete(asMap());
+ }
+}
+
+bool SharedDataContainer::initVector(JSContext* cx) {
+ auto* vec = js_new<SharedDataVector>();
+ if (!vec) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ data_ = uintptr_t(vec) | VectorTag;
+ return true;
+}
+
+bool SharedDataContainer::initMap(JSContext* cx) {
+ auto* map = js_new<SharedDataMap>();
+ if (!map) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ data_ = uintptr_t(map) | MapTag;
+ return true;
+}
+
+bool SharedDataContainer::prepareStorageFor(JSContext* cx,
+ size_t nonLazyScriptCount,
+ size_t allScriptCount) {
+ if (nonLazyScriptCount <= 1) {
+ MOZ_ASSERT(isSingle());
+ return true;
+ }
+
+ // If the ratio of scripts with bytecode is small, allocating the Vector
+ // storage with the number of all scripts isn't space-efficient.
+ // In that case use HashMap instead.
+ //
+ // In general, we expect either all scripts to contain bytecode (priviledge
+ // and self-hosted), or almost none to (eg standard lazy parsing output).
+ constexpr size_t thresholdRatio = 8;
+ bool useHashMap = nonLazyScriptCount < allScriptCount / thresholdRatio;
+ if (useHashMap) {
+ if (!initMap(cx)) {
+ return false;
+ }
+ if (!asMap()->reserve(nonLazyScriptCount)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ } else {
+ if (!initVector(cx)) {
+ return false;
+ }
+ if (!asVector()->resize(allScriptCount)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+js::SharedImmutableScriptData* SharedDataContainer::get(ScriptIndex index) {
+ if (isSingle()) {
+ if (index == CompilationStencil::TopLevelIndex) {
+ return asSingle();
+ }
+ return nullptr;
+ }
+
+ if (isVector()) {
+ auto& vec = *asVector();
+ if (index.index < vec.length()) {
+ return vec[index];
+ }
+ return nullptr;
+ }
+
+ MOZ_ASSERT(isMap());
+ auto& map = *asMap();
+ auto p = map.lookup(index);
+ if (p) {
+ return p->value();
+ }
+ return nullptr;
+}
+
+bool SharedDataContainer::addAndShare(JSContext* cx, ScriptIndex index,
+ js::SharedImmutableScriptData* data) {
+ if (isSingle()) {
+ MOZ_ASSERT(index == CompilationStencil::TopLevelIndex);
+ RefPtr<SharedImmutableScriptData> ref(data);
+ if (!SharedImmutableScriptData::shareScriptData(cx, ref)) {
+ return false;
+ }
+ setSingle(ref.forget());
+ return true;
+ }
+
+ if (isVector()) {
+ auto& vec = *asVector();
+ // Resized by SharedDataContainer::prepareStorageFor.
+ vec[index] = data;
+ return SharedImmutableScriptData::shareScriptData(cx, vec[index]);
+ }
+
+ MOZ_ASSERT(isMap());
+ auto& map = *asMap();
+ // Reserved by SharedDataContainer::prepareStorageFor.
+ map.putNewInfallible(index, data);
+ auto p = map.lookup(index);
+ MOZ_ASSERT(p);
+ return SharedImmutableScriptData::shareScriptData(cx, p->value());
+}
+
+template <typename T, typename VectorT>
+bool CopyVectorToSpan(JSContext* cx, LifoAlloc& alloc, mozilla::Span<T>& span,
+ VectorT& vec) {
+ auto len = vec.length();
+ if (len == 0) {
+ return true;
+ }
+
+ auto* p = alloc.newArrayUninitialized<T>(len);
+ if (!p) {
+ js::ReportOutOfMemory(cx);
+ return false;
+ }
+ span = mozilla::Span(p, len);
+ memcpy(span.data(), vec.begin(), sizeof(T) * len);
+ return true;
+}
+
+bool CompilationState::finish(JSContext* cx, CompilationStencil& stencil) {
+ if (!CopyVectorToSpan(cx, stencil.alloc, stencil.regExpData, regExpData)) {
+ return false;
+ }
+
+ if (!CopyVectorToSpan(cx, stencil.alloc, stencil.scriptData, scriptData)) {
+ return false;
+ }
+
+ if (!CopyVectorToSpan(cx, stencil.alloc, stencil.scriptExtra, scriptExtra)) {
+ return false;
+ }
+
+ if (!CopyVectorToSpan(cx, stencil.alloc, stencil.scopeData, scopeData)) {
+ return false;
+ }
+
+ if (!CopyVectorToSpan(cx, stencil.alloc, stencil.scopeNames, scopeNames)) {
+ return false;
+ }
+
+ if (!CopyVectorToSpan(cx, stencil.alloc, stencil.parserAtomData,
+ parserAtoms.entries())) {
+ return false;
+ }
+
+ if (!CopyVectorToSpan(cx, stencil.alloc, stencil.gcThingData, gcThingData)) {
+ return false;
+ }
+
+ return true;
+}
+
+mozilla::Span<TaggedScriptThingIndex> ScriptStencil::gcthings(
+ BaseCompilationStencil& stencil) const {
+ return stencil.gcThingData.Subspan(gcThingsOffset, gcThingsLength);
+}
+
+#if defined(DEBUG) || defined(JS_JITSPEW)
+
+void frontend::DumpTaggedParserAtomIndex(js::JSONPrinter& json,
+ TaggedParserAtomIndex taggedIndex,
+ BaseCompilationStencil* stencil) {
+ if (taggedIndex.isParserAtomIndex()) {
+ json.property("tag", "AtomIndex");
+ auto index = taggedIndex.toParserAtomIndex();
+ if (stencil && stencil->parserAtomData[index]) {
+ GenericPrinter& out = json.beginStringProperty("atom");
+ stencil->parserAtomData[index]->dumpCharsNoQuote(out);
+ json.endString();
+ } else {
+ json.property("index", size_t(index));
+ }
+ return;
+ }
+
+ if (taggedIndex.isWellKnownAtomId()) {
+ json.property("tag", "WellKnown");
+ auto index = taggedIndex.toWellKnownAtomId();
+ switch (index) {
+ case WellKnownAtomId::empty:
+ json.property("atom", "");
+ break;
+
+# define CASE_(_, name, _2) \
+ case WellKnownAtomId::name: { \
+ GenericPrinter& out = json.beginStringProperty("atom"); \
+ WellKnownParserAtoms::rom_.name.dumpCharsNoQuote(out); \
+ json.endString(); \
+ break; \
+ }
+ FOR_EACH_NONTINY_COMMON_PROPERTYNAME(CASE_)
+# undef CASE_
+
+# define CASE_(name, _) \
+ case WellKnownAtomId::name: { \
+ GenericPrinter& out = json.beginStringProperty("atom"); \
+ WellKnownParserAtoms::rom_.name.dumpCharsNoQuote(out); \
+ json.endString(); \
+ break; \
+ }
+ JS_FOR_EACH_PROTOTYPE(CASE_)
+# undef CASE_
+
+ default:
+ // This includes tiny WellKnownAtomId atoms, which is invalid.
+ json.property("index", size_t(index));
+ break;
+ }
+ return;
+ }
+
+ if (taggedIndex.isStaticParserString1()) {
+ json.property("tag", "Static1");
+ auto index = taggedIndex.toStaticParserString1();
+ GenericPrinter& out = json.beginStringProperty("atom");
+ WellKnownParserAtoms::getStatic1(index)->dumpCharsNoQuote(out);
+ json.endString();
+ return;
+ }
+
+ if (taggedIndex.isStaticParserString2()) {
+ json.property("tag", "Static2");
+ auto index = taggedIndex.toStaticParserString2();
+ GenericPrinter& out = json.beginStringProperty("atom");
+ WellKnownParserAtoms::getStatic2(index)->dumpCharsNoQuote(out);
+ json.endString();
+ return;
+ }
+
+ MOZ_ASSERT(taggedIndex.isNull());
+ json.property("tag", "null");
+}
+
+void RegExpStencil::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json, nullptr);
+}
+
+void RegExpStencil::dump(js::JSONPrinter& json,
+ BaseCompilationStencil* stencil) {
+ json.beginObject();
+ dumpFields(json, stencil);
+ json.endObject();
+}
+
+void RegExpStencil::dumpFields(js::JSONPrinter& json,
+ BaseCompilationStencil* stencil) {
+ json.beginObjectProperty("pattern");
+ DumpTaggedParserAtomIndex(json, atom_, stencil);
+ json.endObject();
+
+ GenericPrinter& out = json.beginStringProperty("flags");
+
+ if (flags().global()) {
+ out.put("g");
+ }
+ if (flags().ignoreCase()) {
+ out.put("i");
+ }
+ if (flags().multiline()) {
+ out.put("m");
+ }
+ if (flags().dotAll()) {
+ out.put("s");
+ }
+ if (flags().unicode()) {
+ out.put("u");
+ }
+ if (flags().sticky()) {
+ out.put("y");
+ }
+
+ json.endStringProperty();
+}
+
+void BigIntStencil::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json);
+}
+
+void BigIntStencil::dump(js::JSONPrinter& json) {
+ GenericPrinter& out = json.beginString();
+ dumpCharsNoQuote(out);
+ json.endString();
+}
+
+void BigIntStencil::dumpCharsNoQuote(GenericPrinter& out) {
+ for (size_t i = 0; i < length_; i++) {
+ out.putChar(char(buf_[i]));
+ }
+}
+
+void ScopeStencil::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json, nullptr, nullptr);
+}
+
+void ScopeStencil::dump(js::JSONPrinter& json,
+ BaseParserScopeData* baseScopeData,
+ BaseCompilationStencil* stencil) {
+ json.beginObject();
+ dumpFields(json, baseScopeData, stencil);
+ json.endObject();
+}
+
+void ScopeStencil::dumpFields(js::JSONPrinter& json,
+ BaseParserScopeData* baseScopeData,
+ BaseCompilationStencil* stencil) {
+ json.property("kind", ScopeKindString(kind_));
+
+ if (hasEnclosing()) {
+ json.formatProperty("enclosing", "ScopeIndex(%zu)", size_t(enclosing()));
+ }
+
+ json.property("firstFrameSlot", firstFrameSlot_);
+
+ if (hasEnvironmentShape()) {
+ json.formatProperty("numEnvironmentSlots", "%zu",
+ size_t(numEnvironmentSlots_));
+ }
+
+ if (isFunction()) {
+ json.formatProperty("functionIndex", "ScriptIndex(%zu)",
+ size_t(functionIndex_));
+ }
+
+ json.beginListProperty("flags");
+ if (flags_ & HasEnclosing) {
+ json.value("HasEnclosing");
+ }
+ if (flags_ & HasEnvironmentShape) {
+ json.value("HasEnvironmentShape");
+ }
+ if (flags_ & IsArrow) {
+ json.value("IsArrow");
+ }
+ json.endList();
+
+ if (!baseScopeData) {
+ return;
+ }
+
+ json.beginObjectProperty("data");
+
+ AbstractTrailingNamesArray<TaggedParserAtomIndex>* trailingNames = nullptr;
+ uint32_t length = 0;
+
+ switch (kind_) {
+ case ScopeKind::Function: {
+ auto* data = static_cast<FunctionScope::ParserData*>(baseScopeData);
+ json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
+ json.property("hasParameterExprs", data->slotInfo.hasParameterExprs());
+ json.property("nonPositionalFormalStart",
+ data->slotInfo.nonPositionalFormalStart);
+ json.property("varStart", data->slotInfo.varStart);
+
+ trailingNames = &data->trailingNames;
+ length = data->slotInfo.length;
+ break;
+ }
+
+ case ScopeKind::FunctionBodyVar: {
+ auto* data = static_cast<VarScope::ParserData*>(baseScopeData);
+ json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
+
+ trailingNames = &data->trailingNames;
+ length = data->slotInfo.length;
+ break;
+ }
+
+ case ScopeKind::Lexical:
+ case ScopeKind::SimpleCatch:
+ case ScopeKind::Catch:
+ case ScopeKind::NamedLambda:
+ case ScopeKind::StrictNamedLambda:
+ case ScopeKind::FunctionLexical:
+ case ScopeKind::ClassBody: {
+ auto* data = static_cast<LexicalScope::ParserData*>(baseScopeData);
+ json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
+ json.property("constStart", data->slotInfo.constStart);
+
+ trailingNames = &data->trailingNames;
+ length = data->slotInfo.length;
+ break;
+ }
+
+ case ScopeKind::With: {
+ break;
+ }
+
+ case ScopeKind::Eval:
+ case ScopeKind::StrictEval: {
+ auto* data = static_cast<EvalScope::ParserData*>(baseScopeData);
+ json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
+
+ trailingNames = &data->trailingNames;
+ length = data->slotInfo.length;
+ break;
+ }
+
+ case ScopeKind::Global:
+ case ScopeKind::NonSyntactic: {
+ auto* data = static_cast<GlobalScope::ParserData*>(baseScopeData);
+ json.property("letStart", data->slotInfo.letStart);
+ json.property("constStart", data->slotInfo.constStart);
+
+ trailingNames = &data->trailingNames;
+ length = data->slotInfo.length;
+ break;
+ }
+
+ case ScopeKind::Module: {
+ auto* data = static_cast<ModuleScope::ParserData*>(baseScopeData);
+ json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
+ json.property("varStart", data->slotInfo.varStart);
+ json.property("letStart", data->slotInfo.letStart);
+ json.property("constStart", data->slotInfo.constStart);
+
+ trailingNames = &data->trailingNames;
+ length = data->slotInfo.length;
+ break;
+ }
+
+ case ScopeKind::WasmInstance: {
+ auto* data = static_cast<WasmInstanceScope::ParserData*>(baseScopeData);
+ json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
+ json.property("globalsStart", data->slotInfo.globalsStart);
+
+ trailingNames = &data->trailingNames;
+ length = data->slotInfo.length;
+ break;
+ }
+
+ case ScopeKind::WasmFunction: {
+ auto* data = static_cast<WasmFunctionScope::ParserData*>(baseScopeData);
+ json.property("nextFrameSlot", data->slotInfo.nextFrameSlot);
+
+ trailingNames = &data->trailingNames;
+ length = data->slotInfo.length;
+ break;
+ }
+
+ default: {
+ MOZ_CRASH("Unexpected ScopeKind");
+ break;
+ }
+ }
+
+ if (trailingNames) {
+ char index[64];
+ json.beginObjectProperty("trailingNames");
+ for (size_t i = 0; i < length; i++) {
+ auto& name = (*trailingNames)[i];
+ SprintfLiteral(index, "%zu", i);
+ json.beginObjectProperty(index);
+
+ json.boolProperty("closedOver", name.closedOver());
+
+ json.boolProperty("isTopLevelFunction", name.isTopLevelFunction());
+
+ json.beginObjectProperty("name");
+ DumpTaggedParserAtomIndex(json, name.name(), stencil);
+ json.endObject();
+
+ json.endObject();
+ }
+ json.endObject();
+ }
+
+ json.endObject();
+}
+
+static void DumpModuleEntryVectorItems(
+ js::JSONPrinter& json, const StencilModuleMetadata::EntryVector& entries,
+ BaseCompilationStencil* stencil) {
+ for (const auto& entry : entries) {
+ json.beginObject();
+ if (entry.specifier) {
+ json.beginObjectProperty("specifier");
+ DumpTaggedParserAtomIndex(json, entry.specifier, stencil);
+ json.endObject();
+ }
+ if (entry.localName) {
+ json.beginObjectProperty("localName");
+ DumpTaggedParserAtomIndex(json, entry.localName, stencil);
+ json.endObject();
+ }
+ if (entry.importName) {
+ json.beginObjectProperty("importName");
+ DumpTaggedParserAtomIndex(json, entry.importName, stencil);
+ json.endObject();
+ }
+ if (entry.exportName) {
+ json.beginObjectProperty("exportName");
+ DumpTaggedParserAtomIndex(json, entry.exportName, stencil);
+ json.endObject();
+ }
+ json.endObject();
+ }
+}
+
+void StencilModuleMetadata::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json, nullptr);
+}
+
+void StencilModuleMetadata::dump(js::JSONPrinter& json,
+ BaseCompilationStencil* stencil) {
+ json.beginObject();
+ dumpFields(json, stencil);
+ json.endObject();
+}
+
+void StencilModuleMetadata::dumpFields(js::JSONPrinter& json,
+ BaseCompilationStencil* stencil) {
+ json.beginListProperty("requestedModules");
+ DumpModuleEntryVectorItems(json, requestedModules, stencil);
+ json.endList();
+
+ json.beginListProperty("importEntries");
+ DumpModuleEntryVectorItems(json, importEntries, stencil);
+ json.endList();
+
+ json.beginListProperty("localExportEntries");
+ DumpModuleEntryVectorItems(json, localExportEntries, stencil);
+ json.endList();
+
+ json.beginListProperty("indirectExportEntries");
+ DumpModuleEntryVectorItems(json, indirectExportEntries, stencil);
+ json.endList();
+
+ json.beginListProperty("starExportEntries");
+ DumpModuleEntryVectorItems(json, starExportEntries, stencil);
+ json.endList();
+
+ json.beginListProperty("functionDecls");
+ for (auto& index : functionDecls) {
+ json.value("ScriptIndex(%zu)", size_t(index));
+ }
+ json.endList();
+
+ json.boolProperty("isAsync", isAsync);
+}
+
+static void DumpImmutableScriptFlags(js::JSONPrinter& json,
+ ImmutableScriptFlags immutableFlags) {
+ for (uint32_t i = 1; i; i = i << 1) {
+ if (uint32_t(immutableFlags) & i) {
+ switch (ImmutableScriptFlagsEnum(i)) {
+ case ImmutableScriptFlagsEnum::IsForEval:
+ json.value("IsForEval");
+ break;
+ case ImmutableScriptFlagsEnum::IsModule:
+ json.value("IsModule");
+ break;
+ case ImmutableScriptFlagsEnum::IsFunction:
+ json.value("IsFunction");
+ break;
+ case ImmutableScriptFlagsEnum::SelfHosted:
+ json.value("SelfHosted");
+ break;
+ case ImmutableScriptFlagsEnum::ForceStrict:
+ json.value("ForceStrict");
+ break;
+ case ImmutableScriptFlagsEnum::HasNonSyntacticScope:
+ json.value("HasNonSyntacticScope");
+ break;
+ case ImmutableScriptFlagsEnum::NoScriptRval:
+ json.value("NoScriptRval");
+ break;
+ case ImmutableScriptFlagsEnum::TreatAsRunOnce:
+ json.value("TreatAsRunOnce");
+ break;
+ case ImmutableScriptFlagsEnum::Strict:
+ json.value("Strict");
+ break;
+ case ImmutableScriptFlagsEnum::HasModuleGoal:
+ json.value("HasModuleGoal");
+ break;
+ case ImmutableScriptFlagsEnum::HasInnerFunctions:
+ json.value("HasInnerFunctions");
+ break;
+ case ImmutableScriptFlagsEnum::HasDirectEval:
+ json.value("HasDirectEval");
+ break;
+ case ImmutableScriptFlagsEnum::BindingsAccessedDynamically:
+ json.value("BindingsAccessedDynamically");
+ break;
+ case ImmutableScriptFlagsEnum::HasCallSiteObj:
+ json.value("HasCallSiteObj");
+ break;
+ case ImmutableScriptFlagsEnum::IsAsync:
+ json.value("IsAsync");
+ break;
+ case ImmutableScriptFlagsEnum::IsGenerator:
+ json.value("IsGenerator");
+ break;
+ case ImmutableScriptFlagsEnum::FunHasExtensibleScope:
+ json.value("FunHasExtensibleScope");
+ break;
+ case ImmutableScriptFlagsEnum::FunctionHasThisBinding:
+ json.value("FunctionHasThisBinding");
+ break;
+ case ImmutableScriptFlagsEnum::NeedsHomeObject:
+ json.value("NeedsHomeObject");
+ break;
+ case ImmutableScriptFlagsEnum::IsDerivedClassConstructor:
+ json.value("IsDerivedClassConstructor");
+ break;
+ case ImmutableScriptFlagsEnum::IsFieldInitializer:
+ json.value("IsFieldInitializer");
+ break;
+ case ImmutableScriptFlagsEnum::HasRest:
+ json.value("HasRest");
+ break;
+ case ImmutableScriptFlagsEnum::NeedsFunctionEnvironmentObjects:
+ json.value("NeedsFunctionEnvironmentObjects");
+ break;
+ case ImmutableScriptFlagsEnum::FunctionHasExtraBodyVarScope:
+ json.value("FunctionHasExtraBodyVarScope");
+ break;
+ case ImmutableScriptFlagsEnum::ShouldDeclareArguments:
+ json.value("ShouldDeclareArguments");
+ break;
+ case ImmutableScriptFlagsEnum::ArgumentsHasVarBinding:
+ json.value("ArgumentsHasVarBinding");
+ break;
+ case ImmutableScriptFlagsEnum::AlwaysNeedsArgsObj:
+ json.value("AlwaysNeedsArgsObj");
+ break;
+ case ImmutableScriptFlagsEnum::HasMappedArgsObj:
+ json.value("HasMappedArgsObj");
+ break;
+ default:
+ json.value("Unknown(%x)", i);
+ break;
+ }
+ }
+ }
+}
+
+static void DumpFunctionFlagsItems(js::JSONPrinter& json,
+ FunctionFlags functionFlags) {
+ switch (functionFlags.kind()) {
+ case FunctionFlags::FunctionKind::NormalFunction:
+ json.value("NORMAL_KIND");
+ break;
+ case FunctionFlags::FunctionKind::AsmJS:
+ json.value("ASMJS_KIND");
+ break;
+ case FunctionFlags::FunctionKind::Wasm:
+ json.value("WASM_KIND");
+ break;
+ case FunctionFlags::FunctionKind::Arrow:
+ json.value("ARROW_KIND");
+ break;
+ case FunctionFlags::FunctionKind::Method:
+ json.value("METHOD_KIND");
+ break;
+ case FunctionFlags::FunctionKind::ClassConstructor:
+ json.value("CLASSCONSTRUCTOR_KIND");
+ break;
+ case FunctionFlags::FunctionKind::Getter:
+ json.value("GETTER_KIND");
+ break;
+ case FunctionFlags::FunctionKind::Setter:
+ json.value("SETTER_KIND");
+ break;
+ default:
+ json.value("Unknown(%x)", uint8_t(functionFlags.kind()));
+ break;
+ }
+
+ static_assert(FunctionFlags::FUNCTION_KIND_MASK == 0x0007,
+ "FunctionKind should use the lowest 3 bits");
+ for (uint16_t i = 1 << 3; i; i = i << 1) {
+ if (functionFlags.toRaw() & i) {
+ switch (FunctionFlags::Flags(i)) {
+ case FunctionFlags::Flags::EXTENDED:
+ json.value("EXTENDED");
+ break;
+ case FunctionFlags::Flags::SELF_HOSTED:
+ json.value("SELF_HOSTED");
+ break;
+ case FunctionFlags::Flags::BASESCRIPT:
+ json.value("BASESCRIPT");
+ break;
+ case FunctionFlags::Flags::SELFHOSTLAZY:
+ json.value("SELFHOSTLAZY");
+ break;
+ case FunctionFlags::Flags::CONSTRUCTOR:
+ json.value("CONSTRUCTOR");
+ break;
+ case FunctionFlags::Flags::BOUND_FUN:
+ json.value("BOUND_FUN");
+ break;
+ case FunctionFlags::Flags::LAMBDA:
+ json.value("LAMBDA");
+ break;
+ case FunctionFlags::Flags::WASM_JIT_ENTRY:
+ json.value("WASM_JIT_ENTRY");
+ break;
+ case FunctionFlags::Flags::HAS_INFERRED_NAME:
+ json.value("HAS_INFERRED_NAME");
+ break;
+ case FunctionFlags::Flags::ATOM_EXTRA_FLAG:
+ json.value("ATOM_EXTRA_FLAG");
+ break;
+ case FunctionFlags::Flags::RESOLVED_NAME:
+ json.value("RESOLVED_NAME");
+ break;
+ case FunctionFlags::Flags::RESOLVED_LENGTH:
+ json.value("RESOLVED_LENGTH");
+ break;
+ default:
+ json.value("Unknown(%x)", i);
+ break;
+ }
+ }
+ }
+}
+
+static void DumpScriptThing(js::JSONPrinter& json,
+ BaseCompilationStencil* stencil,
+ TaggedScriptThingIndex& thing) {
+ switch (thing.tag()) {
+ case TaggedScriptThingIndex::Kind::ParserAtomIndex:
+ case TaggedScriptThingIndex::Kind::WellKnown:
+ json.beginObject();
+ json.property("type", "Atom");
+ DumpTaggedParserAtomIndex(json, thing.toAtom(), stencil);
+ json.endObject();
+ break;
+ case TaggedScriptThingIndex::Kind::Null:
+ json.nullValue();
+ break;
+ case TaggedScriptThingIndex::Kind::BigInt:
+ json.value("BigIntIndex(%zu)", size_t(thing.toBigInt()));
+ break;
+ case TaggedScriptThingIndex::Kind::ObjLiteral:
+ json.value("ObjLiteralIndex(%zu)", size_t(thing.toObjLiteral()));
+ break;
+ case TaggedScriptThingIndex::Kind::RegExp:
+ json.value("RegExpIndex(%zu)", size_t(thing.toRegExp()));
+ break;
+ case TaggedScriptThingIndex::Kind::Scope:
+ json.value("ScopeIndex(%zu)", size_t(thing.toScope()));
+ break;
+ case TaggedScriptThingIndex::Kind::Function:
+ json.value("ScriptIndex(%zu)", size_t(thing.toFunction()));
+ break;
+ case TaggedScriptThingIndex::Kind::EmptyGlobalScope:
+ json.value("EmptyGlobalScope");
+ break;
+ }
+}
+
+void ScriptStencil::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json, nullptr);
+}
+
+void ScriptStencil::dump(js::JSONPrinter& json,
+ BaseCompilationStencil* stencil) {
+ json.beginObject();
+ dumpFields(json, stencil);
+ json.endObject();
+}
+
+void ScriptStencil::dumpFields(js::JSONPrinter& json,
+ BaseCompilationStencil* stencil) {
+ if (hasMemberInitializers()) {
+ json.property("memberInitializers", memberInitializers_);
+ }
+
+ json.formatProperty("gcThingsOffset", "CompilationGCThingIndex(%u)",
+ gcThingsOffset.index);
+ json.property("gcThingsLength", gcThingsLength);
+
+ if (stencil) {
+ json.beginListProperty("gcThings");
+ for (auto& thing : gcthings(*stencil)) {
+ DumpScriptThing(json, stencil, thing);
+ }
+ json.endList();
+ }
+
+ json.beginListProperty("flags");
+ if (flags_ & WasFunctionEmittedFlag) {
+ json.value("WasFunctionEmittedFlag");
+ }
+ if (flags_ & AllowRelazifyFlag) {
+ json.value("AllowRelazifyFlag");
+ }
+ if (flags_ & HasSharedDataFlag) {
+ json.value("HasSharedDataFlag");
+ }
+ if (flags_ & HasMemberInitializersFlag) {
+ json.value("HasMemberInitializersFlag");
+ }
+ if (flags_ & HasLazyFunctionEnclosingScopeIndexFlag) {
+ json.value("HasLazyFunctionEnclosingScopeIndexFlag");
+ }
+ json.endList();
+
+ if (isFunction()) {
+ json.beginObjectProperty("functionAtom");
+ DumpTaggedParserAtomIndex(json, functionAtom, stencil);
+ json.endObject();
+
+ json.beginListProperty("functionFlags");
+ DumpFunctionFlagsItems(json, functionFlags);
+ json.endList();
+
+ if (hasLazyFunctionEnclosingScopeIndex()) {
+ json.formatProperty("lazyFunctionEnclosingScopeIndex", "ScopeIndex(%zu)",
+ size_t(lazyFunctionEnclosingScopeIndex_));
+ }
+ }
+}
+
+void ScriptStencilExtra::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json);
+}
+
+void ScriptStencilExtra::dump(js::JSONPrinter& json) {
+ json.beginObject();
+ dumpFields(json);
+ json.endObject();
+}
+
+void ScriptStencilExtra::dumpFields(js::JSONPrinter& json) {
+ json.beginListProperty("immutableFlags");
+ DumpImmutableScriptFlags(json, immutableFlags);
+ json.endList();
+
+ json.beginObjectProperty("extent");
+ json.property("sourceStart", extent.sourceStart);
+ json.property("sourceEnd", extent.sourceEnd);
+ json.property("toStringStart", extent.toStringStart);
+ json.property("toStringEnd", extent.toStringEnd);
+ json.property("lineno", extent.lineno);
+ json.property("column", extent.column);
+ json.endObject();
+
+ json.property("nargs", nargs);
+}
+
+void SharedDataContainer::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json);
+}
+
+void SharedDataContainer::dump(js::JSONPrinter& json) {
+ json.beginObject();
+ dumpFields(json);
+ json.endObject();
+}
+
+void SharedDataContainer::dumpFields(js::JSONPrinter& json) {
+ if (isEmpty()) {
+ json.nullProperty("ScriptIndex(0)");
+ return;
+ }
+
+ if (isSingle()) {
+ json.formatProperty("ScriptIndex(0)", "u8[%zu]",
+ asSingle()->immutableDataLength());
+ return;
+ }
+
+ if (isVector()) {
+ auto& vec = *asVector();
+
+ char index[64];
+ for (size_t i = 0; i < vec.length(); i++) {
+ SprintfLiteral(index, "ScriptIndex(%zu)", i);
+ if (vec[i]) {
+ json.formatProperty(index, "u8[%zu]", vec[i]->immutableDataLength());
+ } else {
+ json.nullProperty(index);
+ }
+ }
+ return;
+ }
+
+ MOZ_ASSERT(isMap());
+ auto& map = *asMap();
+
+ char index[64];
+ for (auto iter = map.iter(); !iter.done(); iter.next()) {
+ SprintfLiteral(index, "ScriptIndex(%u)", iter.get().key().index);
+ json.formatProperty(index, "u8[%zu]",
+ iter.get().value()->immutableDataLength());
+ }
+}
+
+void BaseCompilationStencil::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json);
+ out.put("\n");
+}
+
+void BaseCompilationStencil::dump(js::JSONPrinter& json) {
+ json.beginObject();
+ dumpFields(json);
+ json.endObject();
+}
+
+void BaseCompilationStencil::dumpFields(js::JSONPrinter& json) {
+ char index[64];
+
+ json.beginObjectProperty("scriptData");
+ for (size_t i = 0; i < scriptData.size(); i++) {
+ SprintfLiteral(index, "ScriptIndex(%zu)", i);
+ json.beginObjectProperty(index);
+ scriptData[i].dumpFields(json, this);
+ json.endObject();
+ }
+ json.endObject();
+
+ json.beginObjectProperty("regExpData");
+ for (size_t i = 0; i < regExpData.size(); i++) {
+ SprintfLiteral(index, "RegExpIndex(%zu)", i);
+ json.beginObjectProperty(index);
+ regExpData[i].dumpFields(json, this);
+ json.endObject();
+ }
+ json.endObject();
+
+ json.beginObjectProperty("bigIntData");
+ for (size_t i = 0; i < bigIntData.length(); i++) {
+ SprintfLiteral(index, "BigIntIndex(%zu)", i);
+ GenericPrinter& out = json.beginStringProperty(index);
+ bigIntData[i].dumpCharsNoQuote(out);
+ json.endStringProperty();
+ }
+ json.endObject();
+
+ json.beginObjectProperty("objLiteralData");
+ for (size_t i = 0; i < objLiteralData.length(); i++) {
+ SprintfLiteral(index, "ObjLiteralIndex(%zu)", i);
+ json.beginObjectProperty(index);
+ objLiteralData[i].dumpFields(json, this);
+ json.endObject();
+ }
+ json.endObject();
+
+ json.beginObjectProperty("scopeData");
+ MOZ_ASSERT(scopeData.size() == scopeNames.size());
+ for (size_t i = 0; i < scopeData.size(); i++) {
+ SprintfLiteral(index, "ScopeIndex(%zu)", i);
+ json.beginObjectProperty(index);
+ scopeData[i].dumpFields(json, scopeNames[i], this);
+ json.endObject();
+ }
+ json.endObject();
+
+ json.beginObjectProperty("sharedData");
+ sharedData.dumpFields(json);
+ json.endObject();
+}
+
+void CompilationStencil::dump() {
+ js::Fprinter out(stderr);
+ js::JSONPrinter json(out);
+ dump(json);
+ out.put("\n");
+}
+
+void CompilationStencil::dump(js::JSONPrinter& json) {
+ json.beginObject();
+ dumpFields(json);
+ json.endObject();
+}
+
+void CompilationStencil::dumpFields(js::JSONPrinter& json) {
+ BaseCompilationStencil::dumpFields(json);
+
+ char index[64];
+ json.beginObjectProperty("scriptExtra");
+ for (size_t i = 0; i < scriptExtra.size(); i++) {
+ SprintfLiteral(index, "ScriptIndex(%zu)", i);
+ json.beginObjectProperty(index);
+ scriptExtra[i].dumpFields(json);
+ json.endObject();
+ }
+ json.endObject();
+
+ if (moduleMetadata) {
+ json.beginObjectProperty("moduleMetadata");
+ moduleMetadata->dumpFields(json, this);
+ json.endObject();
+ }
+
+ json.beginObjectProperty("asmJS");
+ for (auto iter = asmJS.iter(); !iter.done(); iter.next()) {
+ SprintfLiteral(index, "ScriptIndex(%u)", iter.get().key().index);
+ json.formatProperty(index, "asm.js");
+ }
+ json.endObject();
+}
+
+#endif // defined(DEBUG) || defined(JS_JITSPEW)
+
+JSAtom* CompilationAtomCache::getExistingAtomAt(ParserAtomIndex index) const {
+ return atoms_[index];
+}
+
+JSAtom* CompilationAtomCache::getExistingAtomAt(
+ JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
+ if (taggedIndex.isParserAtomIndex()) {
+ auto index = taggedIndex.toParserAtomIndex();
+ return getExistingAtomAt(index);
+ }
+
+ if (taggedIndex.isWellKnownAtomId()) {
+ auto index = taggedIndex.toWellKnownAtomId();
+ return GetWellKnownAtom(cx, index);
+ }
+
+ if (taggedIndex.isStaticParserString1()) {
+ auto index = taggedIndex.toStaticParserString1();
+ return cx->staticStrings().getUnit(char16_t(index));
+ }
+
+ MOZ_ASSERT(taggedIndex.isStaticParserString2());
+ auto index = taggedIndex.toStaticParserString2();
+ return cx->staticStrings().getLength2FromIndex(size_t(index));
+}
+
+JSAtom* CompilationAtomCache::getAtomAt(ParserAtomIndex index) const {
+ if (size_t(index) >= atoms_.length()) {
+ return nullptr;
+ }
+ return atoms_[index];
+}
+
+bool CompilationAtomCache::hasAtomAt(ParserAtomIndex index) const {
+ if (size_t(index) >= atoms_.length()) {
+ return false;
+ }
+ return !!atoms_[index];
+}
+
+bool CompilationAtomCache::setAtomAt(JSContext* cx, ParserAtomIndex index,
+ JSAtom* atom) {
+ if (size_t(index) < atoms_.length()) {
+ atoms_[index] = atom;
+ return true;
+ }
+
+ if (!atoms_.resize(size_t(index) + 1)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ atoms_[index] = atom;
+ return true;
+}
+
+bool CompilationAtomCache::allocate(JSContext* cx, size_t length) {
+ MOZ_ASSERT(length >= atoms_.length());
+ if (length == atoms_.length()) {
+ return true;
+ }
+
+ if (!atoms_.resize(length)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
+bool CompilationAtomCache::extendIfNecessary(JSContext* cx, size_t length) {
+ if (length <= atoms_.length()) {
+ return true;
+ }
+
+ if (!atoms_.resize(length)) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
+void CompilationAtomCache::stealBuffer(AtomCacheVector& atoms) {
+ atoms_ = std::move(atoms);
+ // Destroy elements, without unreserving.
+ atoms_.clear();
+}
+
+void CompilationAtomCache::releaseBuffer(AtomCacheVector& atoms) {
+ atoms = std::move(atoms_);
+}
+
+const ParserAtom* GetWellKnownParserAtomAt(JSContext* cx,
+ TaggedParserAtomIndex taggedIndex) {
+ MOZ_ASSERT(!taggedIndex.isParserAtomIndex());
+
+ if (taggedIndex.isWellKnownAtomId()) {
+ auto index = taggedIndex.toWellKnownAtomId();
+ return cx->runtime()->commonParserNames->getWellKnown(index);
+ }
+
+ if (taggedIndex.isStaticParserString1()) {
+ auto index = taggedIndex.toStaticParserString1();
+ return WellKnownParserAtoms::getStatic1(index);
+ }
+
+ MOZ_ASSERT(taggedIndex.isStaticParserString2());
+ auto index = taggedIndex.toStaticParserString2();
+ return WellKnownParserAtoms::getStatic2(index);
+}
+
+const ParserAtom* CompilationState::getParserAtomAt(
+ JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
+ if (taggedIndex.isParserAtomIndex()) {
+ auto index = taggedIndex.toParserAtomIndex();
+ MOZ_ASSERT(index < parserAtoms.entries().length());
+ return parserAtoms.entries()[index]->asAtom();
+ }
+
+ return GetWellKnownParserAtomAt(cx, taggedIndex);
+}
+
+bool CompilationState::allocateGCThingsUninitialized(
+ JSContext* cx, ScriptIndex scriptIndex, size_t length,
+ TaggedScriptThingIndex** cursor) {
+ MOZ_ASSERT(gcThingData.length() <= UINT32_MAX);
+
+ auto gcThingsOffset = CompilationGCThingIndex(gcThingData.length());
+
+ if (length > INDEX_LIMIT) {
+ ReportAllocationOverflow(cx);
+ return false;
+ }
+ uint32_t gcThingsLength = length;
+
+ if (!gcThingData.growByUninitialized(length)) {
+ js::ReportOutOfMemory(cx);
+ return false;
+ }
+
+ if (gcThingData.length() > UINT32_MAX) {
+ ReportAllocationOverflow(cx);
+ return false;
+ }
+
+ ScriptStencil& script = scriptData[scriptIndex];
+ script.gcThingsOffset = gcThingsOffset;
+ script.gcThingsLength = gcThingsLength;
+
+ *cursor = gcThingData.begin() + gcThingsOffset;
+ return true;
+}
+
+bool CompilationState::appendGCThings(
+ JSContext* cx, ScriptIndex scriptIndex,
+ mozilla::Span<const TaggedScriptThingIndex> things) {
+ MOZ_ASSERT(gcThingData.length() <= UINT32_MAX);
+
+ auto gcThingsOffset = CompilationGCThingIndex(gcThingData.length());
+
+ if (things.size() > INDEX_LIMIT) {
+ ReportAllocationOverflow(cx);
+ return false;
+ }
+ uint32_t gcThingsLength = uint32_t(things.size());
+
+ if (!gcThingData.append(things.data(), things.size())) {
+ js::ReportOutOfMemory(cx);
+ return false;
+ }
+
+ if (gcThingData.length() > UINT32_MAX) {
+ ReportAllocationOverflow(cx);
+ return false;
+ }
+
+ ScriptStencil& script = scriptData[scriptIndex];
+ script.gcThingsOffset = gcThingsOffset;
+ script.gcThingsLength = gcThingsLength;
+ return true;
+}
+
+const ParserAtom* BaseCompilationStencil::getParserAtomAt(
+ JSContext* cx, TaggedParserAtomIndex taggedIndex) const {
+ if (taggedIndex.isParserAtomIndex()) {
+ auto index = taggedIndex.toParserAtomIndex();
+ MOZ_ASSERT(index < parserAtomData.size());
+ return parserAtomData[index]->asAtom();
+ }
+
+ return GetWellKnownParserAtomAt(cx, taggedIndex);
+}