/* -*- 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/. */ #ifndef frontend_Stencil_h #define frontend_Stencil_h #include "mozilla/Assertions.h" // MOZ_ASSERT #include "mozilla/Maybe.h" // mozilla::{Maybe, Nothing} #include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf #include "mozilla/Span.h" // mozilla::Span #include // size_t #include // char16_t, uint8_t, uint16_t, uint32_t #include "frontend/AbstractScopePtr.h" // AbstractScopePtr, ScopeIndex #include "frontend/ObjLiteral.h" // ObjLiteralStencil #include "frontend/ParserAtom.h" // TaggedParserAtomIndex #include "frontend/ScriptIndex.h" // ScriptIndex #include "frontend/TypedIndex.h" // TypedIndex #include "js/AllocPolicy.h" // SystemAllocPolicy #include "js/RefCounted.h" // AtomicRefCounted #include "js/RegExpFlags.h" // JS::RegExpFlags #include "js/RootingAPI.h" // Handle #include "js/TypeDecls.h" // JSContext #include "js/UniquePtr.h" // js::UniquePtr #include "js/Utility.h" // UniqueTwoByteChars #include "js/Vector.h" // js::Vector #include "vm/FunctionFlags.h" // FunctionFlags #include "vm/Scope.h" // Scope, BaseScopeData, FunctionScope, LexicalScope, VarScope, GlobalScope, EvalScope, ModuleScope #include "vm/ScopeKind.h" // ScopeKind #include "vm/SharedStencil.h" // ImmutableScriptFlags, GCThingIndex, js::SharedImmutableScriptData, MemberInitializers, SourceExtent namespace js { class LifoAlloc; class JSONPrinter; class RegExpObject; namespace frontend { struct CompilationInput; struct CompilationStencil; struct CompilationAtomCache; struct CompilationGCOutput; struct CompilationStencilMerger; class RegExpStencil; class BigIntStencil; class StencilXDR; using BaseParserScopeData = AbstractBaseScopeData; using ParserBindingName = AbstractBindingName; template using ParserScopeSlotInfo = typename Scope::SlotInfo; using ParserGlobalScopeSlotInfo = ParserScopeSlotInfo; using ParserEvalScopeSlotInfo = ParserScopeSlotInfo; using ParserLexicalScopeSlotInfo = ParserScopeSlotInfo; using ParserClassBodyScopeSlotInfo = ParserScopeSlotInfo; using ParserFunctionScopeSlotInfo = ParserScopeSlotInfo; using ParserModuleScopeSlotInfo = ParserScopeSlotInfo; using ParserVarScopeSlotInfo = ParserScopeSlotInfo; using ParserBindingIter = AbstractBindingIter; using ParserPositionalFormalParameterIter = AbstractPositionalFormalParameterIter; // [SMDOC] Script Stencil (Frontend Representation) // // Stencils are the set of data structures capturing the result of parsing and // bytecode emission. The Stencil format is a precursor format that is then used // to allocate the corresponding scripts on the GC heap that will be used for // execution. By decoupling from the GC and other runtime systems, robust // caching and speculation systems can be built that are more thread-agnostic // and flexible. // // See: https://bugzil.la/stencil // // There are numerous data structures that make up the Stencil format. The // structures are designed for fast serialization to and from disk by preferring // indices over pointers and vectors instead of graphs to allow bulk operations. // // // ParserAtom // ---------- // Our parser relies on atomized strings as part of its normal operations and so // a `ParserAtom` type exists that mirrors the `JSAtom` type but does not // involve the garbage collector. This is possible because the lifetime of these // ParserAtoms is the lifetime of the Stencil that makes use of them and we do // not need finer grained collection. // // // ScriptStencil // ------------- // The key structures generated by parsing are instances of `ScriptStencil`. // There is a `ScriptStencil` for the top-level script and for each inner // function. It contains information needed to create the `JSFunction` (if it is // a function) and the `BaseScript` (if not asm.js) and may or may not have // bytecode. Each `ScriptStencil` may also reference the following Stencil types // (similar to the `BaseScript::gcthings()` list): // // * ParserAtom // * ScopeStencil // * RegExpStencil // * BigIntStencil // * ObjLiteralStencil // * StencilModuleMetadata // // // CompilationStencil / ExtensibleCompilationStencil // ------------------------------------------------- // Parsing a single JavaScript file may generate a tree of `ScriptStencil` that // we then package up into the `ExtensibleCompilationStencil` type or // `CompilationStencil`. They contain a series of vectors/spans segregated by // data type for fast processing (a.k.a Data Oriented Design). // // `ExtensibleCompilationStencil` is mutable type used during parsing, and // can be allocated either on stack or heap. // `ExtensibleCompilationStencil` holds vectors of stencils. // // `CompilationStencil` is immutable type used for caching the compilation // result, and is allocated on heap with refcount. // `CompilationStencil` holds spans of stencils, and it can point either // owned data or borrowed data. // The borrowed data can be from other `ExtensibleCompilationStencil` or // from serialized stencil (XDR) on memory or mmap. // // Delazifying a function will generate its bytecode but some fields remain // unchanged from the initial lazy parse. // When we delazify a function that was lazily parsed, we generate a new // Stencil at the point too. These delazifications can be merged into the // Stencil of the initial parse by using `CompilationStencilMerger`. // // Conversion from ExtensibleCompilationStencil to CompilationStencil // ------------------------------------------------------------------ // There are multiple ways to convert from `ExtensibleCompilationStencil` to // `CompilationStencil`: // // 1. Temporarily borrow `ExtensibleCompilationStencil` content and call // function that takes `CompilationStencil` reference, and keep using the // `ExtensibleCompilationStencil` after that: // // ExtensibleCompilationStencil extensible = ...; // { // BorrowingCompilationStencil stencil(extensible); // // Use `stencil reference. // } // // 2. Take the ownership of an on-heap ExtensibleCompilationStencil. This makes // the `CompilationStencil` self-contained and it's useful for caching: // // UniquePtr extensible = ...; // CompilationStencil stencil(std::move(extensible)); // // Conversion from CompilationStencil to ExtensibleCompilationStencil // ------------------------------------------------------------------ // In the same way, there are multiple ways to convert from // `CompilationStencil` to `ExtensibleCompilationStencil`: // // 1. Take the ownership of `CompilationStencil`'s underlying data, Only when // stencil owns the data and the refcount is 1: // // RefPtr stencil = ...; // ExtensibleCompilationStencil extensible(...); // // NOTE: This is equivalent to cloneFrom below if `stencil` has refcount // // more than 2, or it doesn't own the data. // extensible.steal(fc, std::move(stencil)); // // 2. Clone the underlying data. This is slow but safe operation: // // CompilationStencil stencil = ...; // ExtensibleCompilationStencil extensible(...); // extensible.cloneFrom(fc, stencil); // // 3. Take the ownership back from the `CompilationStencil` which is created by // taking the ownership of an on-heap `ExtensibleCompilationStencil`: // // CompilationStencil stencil = ...; // ExtensibleCompilationStencil* extensible = stencil.takeOwnedBorrow(); // // CompilationGCOutput // ------------------- // When a Stencil is instantiated the equivalent script objects are allocated on // the GC heap and their pointers are collected into the `CompilationGCOutput` // structure. This is only used temporarily during instantiation. // // // CompilationState // ---------------- // This is another temporary structure used by the parser while the Stencil is // being generated. Once the `CompilationStencil` is complete, this can be // released. // Typed indices for the different stencil elements in the compilation result. using RegExpIndex = TypedIndex; using BigIntIndex = TypedIndex; using ObjLiteralIndex = TypedIndex; // Index into {ExtensibleCompilationStencil,CompilationStencil}.gcThingData. class CompilationGCThingType {}; using CompilationGCThingIndex = TypedIndex; // A syntax-checked regular expression string. class RegExpStencil { friend class StencilXDR; TaggedParserAtomIndex atom_; // Use uint32_t to make this struct fully-packed. uint32_t flags_; friend struct CompilationStencilMerger; public: RegExpStencil() = default; RegExpStencil(TaggedParserAtomIndex atom, JS::RegExpFlags flags) : atom_(atom), flags_(flags.value()) {} JS::RegExpFlags flags() const { return JS::RegExpFlags(flags_); } RegExpObject* createRegExp(JSContext* cx, const CompilationAtomCache& atomCache) const; // This is used by `Reflect.parse` when we need the RegExpObject but are not // doing a complete instantiation of the CompilationStencil. RegExpObject* createRegExpAndEnsureAtom( JSContext* cx, FrontendContext* fc, ParserAtomsTable& parserAtoms, CompilationAtomCache& atomCache) const; #if defined(DEBUG) || defined(JS_JITSPEW) void dump() const; void dump(JSONPrinter& json, const CompilationStencil* stencil) const; void dumpFields(JSONPrinter& json, const CompilationStencil* stencil) const; #endif }; // This owns a set of characters guaranteed to parse into a BigInt via // ParseBigIntLiteral. Used to avoid allocating the BigInt on the // GC heap during parsing. class BigIntStencil { friend class StencilXDR; // Source of the BigInt literal. // It's not null-terminated, and also trailing 'n' suffix is not included. mozilla::Span source_; public: BigIntStencil() = default; [[nodiscard]] bool init(FrontendContext* fc, LifoAlloc& alloc, const mozilla::Span buf); BigInt* createBigInt(JSContext* cx) const; bool isZero() const; mozilla::Span source() const { return source_; } #ifdef DEBUG bool isContainedIn(const LifoAlloc& alloc) const; #endif #if defined(DEBUG) || defined(JS_JITSPEW) void dump() const; void dump(JSONPrinter& json) const; void dumpCharsNoQuote(GenericPrinter& out) const; #endif }; class ScopeStencil { friend class StencilXDR; friend class InputScope; friend class AbstractBindingIter; friend struct CompilationStencil; friend struct CompilationStencilMerger; // The enclosing scope. Valid only if HasEnclosing flag is set. // compilation applies. ScopeIndex enclosing_; // First frame slot to use, or LOCALNO_LIMIT if none are allowed. uint32_t firstFrameSlot_ = UINT32_MAX; // The number of environment shape's slots. Valid only if // HasEnvironmentShape flag is set. uint32_t numEnvironmentSlots_; // Canonical function if this is a FunctionScope. Valid only if // kind_ is ScopeKind::Function. ScriptIndex functionIndex_; // The kind determines the corresponding BaseParserScopeData. ScopeKind kind_{UINT8_MAX}; // True if this scope has enclosing scope stencil. Otherwise, the enclosing // scope will be read from CompilationInput while instantiating. Self-hosting // is a special case and will use `emptyGlobalScope` when there is no // enclosing scope stencil. static constexpr uint8_t HasEnclosing = 1 << 0; // If true, an environment Shape must be created. The shape itself may // have no slots if the environment may be extensible later. static constexpr uint8_t HasEnvironmentShape = 1 << 1; // True if this is a FunctionScope for an arrow function. static constexpr uint8_t IsArrow = 1 << 2; uint8_t flags_ = 0; // To make this struct packed, add explicit field for padding. uint16_t padding_ = 0; public: // For XDR only. ScopeStencil() = default; ScopeStencil(ScopeKind kind, mozilla::Maybe enclosing, uint32_t firstFrameSlot, mozilla::Maybe numEnvironmentSlots, mozilla::Maybe functionIndex = mozilla::Nothing(), bool isArrow = false) : enclosing_(enclosing.valueOr(ScopeIndex(0))), firstFrameSlot_(firstFrameSlot), numEnvironmentSlots_(numEnvironmentSlots.valueOr(0)), functionIndex_(functionIndex.valueOr(ScriptIndex(0))), kind_(kind), flags_((enclosing.isSome() ? HasEnclosing : 0) | (numEnvironmentSlots.isSome() ? HasEnvironmentShape : 0) | (isArrow ? IsArrow : 0)) { MOZ_ASSERT((kind == ScopeKind::Function) == functionIndex.isSome()); // Silence -Wunused-private-field warnings. (void)padding_; } private: // Create ScopeStencil with `args`, and append ScopeStencil and `data` to // `compilationState`, and return the index of them as `indexOut`. template static bool appendScopeStencilAndData(FrontendContext* fc, CompilationState& compilationState, BaseParserScopeData* data, ScopeIndex* indexOut, Args&&... args); public: static bool createForFunctionScope( FrontendContext* fc, CompilationState& compilationState, FunctionScope::ParserData* dataArg, bool hasParameterExprs, bool needsEnvironment, ScriptIndex functionIndex, bool isArrow, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForLexicalScope( FrontendContext* fc, CompilationState& compilationState, ScopeKind kind, LexicalScope::ParserData* dataArg, uint32_t firstFrameSlot, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForClassBodyScope( FrontendContext* fc, CompilationState& compilationState, ScopeKind kind, ClassBodyScope::ParserData* dataArg, uint32_t firstFrameSlot, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForVarScope(FrontendContext* fc, CompilationState& compilationState, ScopeKind kind, VarScope::ParserData* dataArg, uint32_t firstFrameSlot, bool needsEnvironment, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForGlobalScope(FrontendContext* fc, CompilationState& compilationState, ScopeKind kind, GlobalScope::ParserData* dataArg, ScopeIndex* index); static bool createForEvalScope(FrontendContext* fc, CompilationState& compilationState, ScopeKind kind, EvalScope::ParserData* dataArg, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForModuleScope(FrontendContext* fc, CompilationState& compilationState, ModuleScope::ParserData* dataArg, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForWithScope(FrontendContext* fc, CompilationState& compilationState, mozilla::Maybe enclosing, ScopeIndex* index); AbstractScopePtr enclosing(CompilationState& compilationState) const; js::Scope* enclosingExistingScope(const CompilationInput& input, const CompilationGCOutput& gcOutput) const; private: bool hasEnclosing() const { return flags_ & HasEnclosing; } ScopeIndex enclosing() const { MOZ_ASSERT(hasEnclosing()); return enclosing_; } uint32_t firstFrameSlot() const { return firstFrameSlot_; } bool hasEnvironmentShape() const { return flags_ & HasEnvironmentShape; } uint32_t numEnvironmentSlots() const { MOZ_ASSERT(hasEnvironmentShape()); return numEnvironmentSlots_; } bool isFunction() const { return kind_ == ScopeKind::Function; } ScriptIndex functionIndex() const { return functionIndex_; } public: ScopeKind kind() const { return kind_; } bool hasEnvironment() const { // Check if scope kind alone means we have an env shape, and // otherwise check if we have one created. return Scope::hasEnvironment(kind(), hasEnvironmentShape()); } bool isArrow() const { return flags_ & IsArrow; } Scope* createScope(JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, BaseParserScopeData* baseScopeData) const; Scope* createScope(JSContext* cx, CompilationAtomCache& atomCache, Handle enclosingScope, BaseParserScopeData* baseScopeData) const; #if defined(DEBUG) || defined(JS_JITSPEW) void dump() const; void dump(JSONPrinter& json, const BaseParserScopeData* baseScopeData, const CompilationStencil* stencil) const; void dumpFields(JSONPrinter& json, const BaseParserScopeData* baseScopeData, const CompilationStencil* stencil) const; #endif private: // Transfer ownership into a new UniquePtr. template UniquePtr createSpecificScopeData( JSContext* cx, CompilationAtomCache& atomCache, BaseParserScopeData* baseData) const; template [[nodiscard]] bool createSpecificShape( JSContext* cx, ScopeKind kind, BaseScopeData* scopeData, MutableHandle shape) const; template Scope* createSpecificScope(JSContext* cx, CompilationAtomCache& atomCache, Handle enclosingScope, BaseParserScopeData* baseData) const; template static constexpr bool matchScopeKind(ScopeKind kind) { switch (kind) { case ScopeKind::Function: { return std::is_same_v; } case ScopeKind::Lexical: case ScopeKind::SimpleCatch: case ScopeKind::Catch: case ScopeKind::NamedLambda: case ScopeKind::StrictNamedLambda: case ScopeKind::FunctionLexical: { return std::is_same_v; } case ScopeKind::ClassBody: { return std::is_same_v; } case ScopeKind::FunctionBodyVar: { return std::is_same_v; } case ScopeKind::Global: case ScopeKind::NonSyntactic: { return std::is_same_v; } case ScopeKind::Eval: case ScopeKind::StrictEval: { return std::is_same_v; } case ScopeKind::Module: { return std::is_same_v; } case ScopeKind::With: { return std::is_same_v; } case ScopeKind::WasmFunction: case ScopeKind::WasmInstance: { return false; } } return false; } }; class StencilModuleAssertion { public: TaggedParserAtomIndex key; TaggedParserAtomIndex value; StencilModuleAssertion() = default; StencilModuleAssertion(TaggedParserAtomIndex key, TaggedParserAtomIndex value) : key(key), value(value) {} }; class StencilModuleRequest { public: TaggedParserAtomIndex specifier; using AssertionVector = Vector; AssertionVector assertions; // For XDR only. StencilModuleRequest() = default; explicit StencilModuleRequest(TaggedParserAtomIndex specifier) : specifier(specifier) { MOZ_ASSERT(specifier); } StencilModuleRequest(const StencilModuleRequest& other) : specifier(other.specifier) { AutoEnterOOMUnsafeRegion oomUnsafe; if (!assertions.appendAll(other.assertions)) { oomUnsafe.crash("StencilModuleRequest::StencilModuleRequest"); } } StencilModuleRequest(StencilModuleRequest&& other) noexcept : specifier(other.specifier), assertions(std::move(other.assertions)) {} StencilModuleRequest& operator=(StencilModuleRequest& other) { specifier = other.specifier; assertions = std::move(other.assertions); return *this; } StencilModuleRequest& operator=(StencilModuleRequest&& other) noexcept { specifier = other.specifier; assertions = std::move(other.assertions); return *this; } }; class MaybeModuleRequestIndex { static constexpr uint32_t NOTHING = UINT32_MAX; uint32_t bits = NOTHING; public: MaybeModuleRequestIndex() = default; explicit MaybeModuleRequestIndex(uint32_t index) : bits(index) { MOZ_ASSERT(isSome()); } MaybeModuleRequestIndex(const MaybeModuleRequestIndex& other) = default; MaybeModuleRequestIndex& operator=(const MaybeModuleRequestIndex& other) = default; bool isNothing() const { return bits == NOTHING; } bool isSome() const { return !isNothing(); } explicit operator bool() const { return isSome(); } uint32_t value() const { MOZ_ASSERT(isSome()); return bits; } uint32_t* operator&() { return &bits; } }; // Common type for ImportEntry / ExportEntry / ModuleRequest within frontend. We // use a shared stencil class type to simplify serialization. // // https://tc39.es/ecma262/#importentry-record // https://tc39.es/ecma262/#exportentry-record // // Note: We subdivide the spec's ExportEntry into ExportAs / ExportFrom forms // for readability. class StencilModuleEntry { public: // clang-format off // // | RequestedModule | ImportEntry | ImportNamespaceEntry | ExportAs | ExportFrom | ExportNamespaceFrom | ExportBatchFrom | // |----------------------------------------------------------------------------------------------------------------------| // moduleRequest | required | required | required | null | required | required | required | // localName | null | required | required | required | null | null | null | // importName | null | required | null | null | required | null | null | // exportName | null | null | null | required | required | required | null | // // clang-format on MaybeModuleRequestIndex moduleRequest; TaggedParserAtomIndex localName; TaggedParserAtomIndex importName; TaggedParserAtomIndex exportName; // Location used for error messages. If this is for a module request entry // then it is the module specifier string, otherwise the import/export spec // that failed. Exports may not fill these fields if an error cannot be // generated such as `export let x;`. uint32_t lineno = 0; uint32_t column = 0; private: StencilModuleEntry(uint32_t lineno, uint32_t column) : lineno(lineno), column(column) {} public: // For XDR only. StencilModuleEntry() = default; StencilModuleEntry(const StencilModuleEntry& other) : moduleRequest(other.moduleRequest), localName(other.localName), importName(other.importName), exportName(other.exportName), lineno(other.lineno), column(other.column) {} StencilModuleEntry(StencilModuleEntry&& other) noexcept : moduleRequest(other.moduleRequest), localName(other.localName), importName(other.importName), exportName(other.exportName), lineno(other.lineno), column(other.column) {} StencilModuleEntry& operator=(StencilModuleEntry& other) { moduleRequest = other.moduleRequest; localName = other.localName; importName = other.importName; exportName = other.exportName; lineno = other.lineno; column = other.column; return *this; } StencilModuleEntry& operator=(StencilModuleEntry&& other) noexcept { moduleRequest = other.moduleRequest; localName = other.localName; importName = other.importName; exportName = other.exportName; lineno = other.lineno; column = other.column; return *this; } static StencilModuleEntry requestedModule( MaybeModuleRequestIndex moduleRequest, uint32_t lineno, uint32_t column) { MOZ_ASSERT(moduleRequest.isSome()); StencilModuleEntry entry(lineno, column); entry.moduleRequest = moduleRequest; return entry; } static StencilModuleEntry importEntry(MaybeModuleRequestIndex moduleRequest, TaggedParserAtomIndex localName, TaggedParserAtomIndex importName, uint32_t lineno, uint32_t column) { MOZ_ASSERT(moduleRequest.isSome()); MOZ_ASSERT(localName && importName); StencilModuleEntry entry(lineno, column); entry.moduleRequest = moduleRequest; entry.localName = localName; entry.importName = importName; return entry; } static StencilModuleEntry importNamespaceEntry( MaybeModuleRequestIndex moduleRequest, TaggedParserAtomIndex localName, uint32_t lineno, uint32_t column) { MOZ_ASSERT(moduleRequest.isSome()); MOZ_ASSERT(localName); StencilModuleEntry entry(lineno, column); entry.moduleRequest = moduleRequest; entry.localName = localName; return entry; } static StencilModuleEntry exportAsEntry(TaggedParserAtomIndex localName, TaggedParserAtomIndex exportName, uint32_t lineno, uint32_t column) { MOZ_ASSERT(localName && exportName); StencilModuleEntry entry(lineno, column); entry.localName = localName; entry.exportName = exportName; return entry; } static StencilModuleEntry exportFromEntry( MaybeModuleRequestIndex moduleRequest, TaggedParserAtomIndex importName, TaggedParserAtomIndex exportName, uint32_t lineno, uint32_t column) { MOZ_ASSERT(moduleRequest.isSome()); MOZ_ASSERT(importName && exportName); StencilModuleEntry entry(lineno, column); entry.moduleRequest = moduleRequest; entry.importName = importName; entry.exportName = exportName; return entry; } static StencilModuleEntry exportNamespaceFromEntry( MaybeModuleRequestIndex moduleRequest, TaggedParserAtomIndex exportName, uint32_t lineno, uint32_t column) { MOZ_ASSERT(moduleRequest.isSome()); MOZ_ASSERT(exportName); StencilModuleEntry entry(lineno, column); entry.moduleRequest = MaybeModuleRequestIndex(moduleRequest); entry.exportName = exportName; return entry; } static StencilModuleEntry exportBatchFromEntry( MaybeModuleRequestIndex moduleRequest, uint32_t lineno, uint32_t column) { MOZ_ASSERT(moduleRequest.isSome()); StencilModuleEntry entry(lineno, column); entry.moduleRequest = MaybeModuleRequestIndex(moduleRequest); return entry; } }; // Metadata generated by parsing module scripts, including import/export tables. class StencilModuleMetadata : public js::AtomicRefCounted { public: using RequestVector = Vector; using EntryVector = Vector; RequestVector moduleRequests; EntryVector requestedModules; EntryVector importEntries; EntryVector localExportEntries; EntryVector indirectExportEntries; EntryVector starExportEntries; FunctionDeclarationVector functionDecls; // Set to true if the module has a top-level await keyword. bool isAsync = false; StencilModuleMetadata() = default; bool initModule(JSContext* cx, FrontendContext* fc, CompilationAtomCache& atomCache, JS::Handle module) const; size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { return mallocSizeOf(this) + requestedModules.sizeOfExcludingThis(mallocSizeOf) + importEntries.sizeOfExcludingThis(mallocSizeOf) + localExportEntries.sizeOfExcludingThis(mallocSizeOf) + indirectExportEntries.sizeOfExcludingThis(mallocSizeOf) + starExportEntries.sizeOfExcludingThis(mallocSizeOf) + functionDecls.sizeOfExcludingThis(mallocSizeOf); } #if defined(DEBUG) || defined(JS_JITSPEW) void dump() const; void dump(JSONPrinter& json, const CompilationStencil* stencil) const; void dumpFields(JSONPrinter& json, const CompilationStencil* stencil) const; #endif private: bool createModuleRequestObjects( JSContext* cx, CompilationAtomCache& atomCache, MutableHandle output) const; bool createRequestedModules( JSContext* cx, CompilationAtomCache& atomCache, Handle moduleRequests, MutableHandle output) const; bool createImportEntries(JSContext* cx, CompilationAtomCache& atomCache, Handle moduleRequests, MutableHandle output) const; bool createExportEntries(JSContext* cx, CompilationAtomCache& atomCache, Handle moduleRequests, const EntryVector& input, MutableHandle output) const; ModuleRequestObject* createModuleRequestObject( JSContext* cx, CompilationAtomCache& atomCache, const StencilModuleRequest& request) const; }; // As an alternative to a ScopeIndex (which references a ScopeStencil), we may // instead refer to an existing scope from GlobalObject::emptyGlobalScope(). // // NOTE: This is only used for the self-hosting global. class EmptyGlobalScopeType {}; // Things pointed by this index all end up being baked into GC things as part // of stencil instantiation. // // 0x0000_0000 Null // 0x1YYY_YYYY 28-bit ParserAtom // 0x2YYY_YYYY Well-known/static atom (See TaggedParserAtomIndex) // 0x3YYY_YYYY 28-bit BigInt // 0x4YYY_YYYY 28-bit ObjLiteral // 0x5YYY_YYYY 28-bit RegExp // 0x6YYY_YYYY 28-bit Scope // 0x7YYY_YYYY 28-bit Function // 0x8000_0000 EmptyGlobalScope class TaggedScriptThingIndex { uint32_t data_; static constexpr size_t IndexBit = TaggedParserAtomIndex::IndexBit; static constexpr size_t IndexMask = TaggedParserAtomIndex::IndexMask; static constexpr size_t TagShift = TaggedParserAtomIndex::TagShift; static constexpr size_t TagBit = TaggedParserAtomIndex::TagBit; static constexpr size_t TagMask = TaggedParserAtomIndex::TagMask; public: enum class Kind : uint32_t { Null = uint32_t(TaggedParserAtomIndex::Kind::Null), ParserAtomIndex = uint32_t(TaggedParserAtomIndex::Kind::ParserAtomIndex), WellKnown = uint32_t(TaggedParserAtomIndex::Kind::WellKnown), BigInt, ObjLiteral, RegExp, Scope, Function, EmptyGlobalScope, }; private: static constexpr uint32_t NullTag = uint32_t(Kind::Null) << TagShift; static_assert(NullTag == TaggedParserAtomIndex::NullTag); static constexpr uint32_t ParserAtomIndexTag = uint32_t(Kind::ParserAtomIndex) << TagShift; static_assert(ParserAtomIndexTag == TaggedParserAtomIndex::ParserAtomIndexTag); static constexpr uint32_t WellKnownTag = uint32_t(Kind::WellKnown) << TagShift; static_assert(WellKnownTag == TaggedParserAtomIndex::WellKnownTag); static constexpr uint32_t BigIntTag = uint32_t(Kind::BigInt) << TagShift; static constexpr uint32_t ObjLiteralTag = uint32_t(Kind::ObjLiteral) << TagShift; static constexpr uint32_t RegExpTag = uint32_t(Kind::RegExp) << TagShift; static constexpr uint32_t ScopeTag = uint32_t(Kind::Scope) << TagShift; static constexpr uint32_t FunctionTag = uint32_t(Kind::Function) << TagShift; static constexpr uint32_t EmptyGlobalScopeTag = uint32_t(Kind::EmptyGlobalScope) << TagShift; public: static constexpr uint32_t IndexLimit = Bit(IndexBit); TaggedScriptThingIndex() : data_(NullTag) {} explicit TaggedScriptThingIndex(TaggedParserAtomIndex index) : data_(index.rawData()) {} explicit TaggedScriptThingIndex(BigIntIndex index) : data_(uint32_t(index) | BigIntTag) { MOZ_ASSERT(uint32_t(index) < IndexLimit); } explicit TaggedScriptThingIndex(ObjLiteralIndex index) : data_(uint32_t(index) | ObjLiteralTag) { MOZ_ASSERT(uint32_t(index) < IndexLimit); } explicit TaggedScriptThingIndex(RegExpIndex index) : data_(uint32_t(index) | RegExpTag) { MOZ_ASSERT(uint32_t(index) < IndexLimit); } explicit TaggedScriptThingIndex(ScopeIndex index) : data_(uint32_t(index) | ScopeTag) { MOZ_ASSERT(uint32_t(index) < IndexLimit); } explicit TaggedScriptThingIndex(ScriptIndex index) : data_(uint32_t(index) | FunctionTag) { MOZ_ASSERT(uint32_t(index) < IndexLimit); } explicit TaggedScriptThingIndex(EmptyGlobalScopeType t) : data_(EmptyGlobalScopeTag) {} bool isAtom() const { return (data_ & TagMask) == ParserAtomIndexTag || (data_ & TagMask) == WellKnownTag; } bool isNull() const { bool result = !data_; MOZ_ASSERT_IF(result, (data_ & TagMask) == NullTag); return result; } bool isBigInt() const { return (data_ & TagMask) == BigIntTag; } bool isObjLiteral() const { return (data_ & TagMask) == ObjLiteralTag; } bool isRegExp() const { return (data_ & TagMask) == RegExpTag; } bool isScope() const { return (data_ & TagMask) == ScopeTag; } bool isFunction() const { return (data_ & TagMask) == FunctionTag; } bool isEmptyGlobalScope() const { return (data_ & TagMask) == EmptyGlobalScopeTag; } TaggedParserAtomIndex toAtom() const { MOZ_ASSERT(isAtom()); return TaggedParserAtomIndex::fromRaw(data_); } BigIntIndex toBigInt() const { return BigIntIndex(data_ & IndexMask); } ObjLiteralIndex toObjLiteral() const { return ObjLiteralIndex(data_ & IndexMask); } RegExpIndex toRegExp() const { return RegExpIndex(data_ & IndexMask); } ScopeIndex toScope() const { return ScopeIndex(data_ & IndexMask); } ScriptIndex toFunction() const { return ScriptIndex(data_ & IndexMask); } TaggedParserAtomIndex toAtomOrNull() const { MOZ_ASSERT(isAtom() || isNull()); return TaggedParserAtomIndex::fromRaw(data_); } uint32_t* rawDataRef() { return &data_; } uint32_t rawData() const { return data_; } Kind tag() const { return Kind((data_ & TagMask) >> TagShift); } bool operator==(const TaggedScriptThingIndex& rhs) const { return data_ == rhs.data_; } }; // Data generated by frontend that will be used to create a js::BaseScript. class ScriptStencil { friend struct CompilationStencilMerger; public: // Fields for BaseScript. // Used by: // * Global script // * Eval // * Module // * non-lazy Function (except asm.js module) // * lazy Function (cannot be asm.js module) // GCThings are stored into // {ExtensibleCompilationStencil,CompilationStencil}.gcThingData, // in [gcThingsOffset, gcThingsOffset + gcThingsLength) range. CompilationGCThingIndex gcThingsOffset; uint32_t gcThingsLength = 0; // Fields for JSFunction. // Used by: // * non-lazy Function // * lazy Function // * asm.js module // The explicit or implicit name of the function. The FunctionFlags indicate // the kind of name. TaggedParserAtomIndex functionAtom; // If this ScriptStencil refers to a lazy child of the function being // compiled, this field holds the child's immediately enclosing scope's index. // Once compilation succeeds, we will store the scope pointed by this in the // child's BaseScript. (Debugger may become confused if lazy scripts refer to // partially initialized enclosing scopes, so we must avoid storing the // scope in the BaseScript until compilation has completed // successfully.) // // OR // // This may be used for self-hosting canonical name (TaggedParserAtomIndex). TaggedScriptThingIndex enclosingScopeOrCanonicalName; // See: `FunctionFlags`. FunctionFlags functionFlags = {}; // This is set by the BytecodeEmitter of the enclosing script when a reference // to this function is generated. static constexpr uint16_t WasEmittedByEnclosingScriptFlag = 1 << 0; // If this is for the root of delazification, this represents // MutableScriptFlagsEnum::AllowRelazify value of the script *after* // delazification. // False otherwise. static constexpr uint16_t AllowRelazifyFlag = 1 << 1; // Set if this is non-lazy script and shared data is created. // The shared data is stored into CompilationStencil.sharedData. static constexpr uint16_t HasSharedDataFlag = 1 << 2; // True if this script is lazy function and has enclosing scope. In that // case, `enclosingScopeOrCanonicalName` will hold the ScopeIndex. static constexpr uint16_t HasLazyFunctionEnclosingScopeIndexFlag = 1 << 3; // True if this script is a self-hosted function with a canonical name // explicitly set. In that case, `enclosingScopeOrCanonicalName` will hold the // TaggedParserAtomIndex. static constexpr uint16_t HasSelfHostedCanonicalName = 1 << 4; uint16_t flags_ = 0; // End of fields. ScriptStencil() = default; bool isFunction() const { bool result = functionFlags.toRaw() != 0x0000; MOZ_ASSERT_IF( result, functionFlags.isAsmJSNative() || functionFlags.hasBaseScript()); return result; } bool hasGCThings() const { return gcThingsLength; } mozilla::Span gcthings( const CompilationStencil& stencil) const; bool wasEmittedByEnclosingScript() const { return flags_ & WasEmittedByEnclosingScriptFlag; } void setWasEmittedByEnclosingScript() { flags_ |= WasEmittedByEnclosingScriptFlag; } bool allowRelazify() const { return flags_ & AllowRelazifyFlag; } void setAllowRelazify() { flags_ |= AllowRelazifyFlag; } bool isGhost() const { return functionFlags.isGhost(); } void setIsGhost() { functionFlags.setIsGhost(); } bool hasSharedData() const { return flags_ & HasSharedDataFlag; } void setHasSharedData() { flags_ |= HasSharedDataFlag; } bool hasLazyFunctionEnclosingScopeIndex() const { return flags_ & HasLazyFunctionEnclosingScopeIndexFlag; } bool hasSelfHostedCanonicalName() const { return flags_ & HasSelfHostedCanonicalName; } private: void setHasLazyFunctionEnclosingScopeIndex() { flags_ |= HasLazyFunctionEnclosingScopeIndexFlag; } void setHasSelfHostedCanonicalName() { flags_ |= HasSelfHostedCanonicalName; } public: void setLazyFunctionEnclosingScopeIndex(ScopeIndex index) { MOZ_ASSERT(enclosingScopeOrCanonicalName.isNull()); enclosingScopeOrCanonicalName = TaggedScriptThingIndex(index); setHasLazyFunctionEnclosingScopeIndex(); } void resetHasLazyFunctionEnclosingScopeIndexAfterStencilMerge() { flags_ &= ~HasLazyFunctionEnclosingScopeIndexFlag; enclosingScopeOrCanonicalName = TaggedScriptThingIndex(); } ScopeIndex lazyFunctionEnclosingScopeIndex() const { MOZ_ASSERT(hasLazyFunctionEnclosingScopeIndex()); return enclosingScopeOrCanonicalName.toScope(); } void setSelfHostedCanonicalName(TaggedParserAtomIndex name) { MOZ_ASSERT(enclosingScopeOrCanonicalName.isNull()); enclosingScopeOrCanonicalName = TaggedScriptThingIndex(name); setHasSelfHostedCanonicalName(); } TaggedParserAtomIndex selfHostedCanonicalName() const { MOZ_ASSERT(hasSelfHostedCanonicalName()); return enclosingScopeOrCanonicalName.toAtom(); } #if defined(DEBUG) || defined(JS_JITSPEW) void dump() const; void dump(JSONPrinter& json, const CompilationStencil* stencil) const; void dumpFields(JSONPrinter& json, const CompilationStencil* stencil) const; #endif }; // In addition to ScriptStencil, data generated only while initial-parsing. class ScriptStencilExtra { public: // See `BaseScript::immutableFlags_`. ImmutableScriptFlags immutableFlags; // The location of this script in the source. SourceExtent extent; // See `PrivateScriptData::memberInitializers_`. // This data only valid when `UseMemberInitializers` script flag is true. uint32_t memberInitializers_ = 0; // See `JSFunction::nargs_`. uint16_t nargs = 0; // To make this struct packed, add explicit field for padding. uint16_t padding_ = 0; ScriptStencilExtra() = default; RO_IMMUTABLE_SCRIPT_FLAGS(immutableFlags) void setMemberInitializers(MemberInitializers member) { MOZ_ASSERT(useMemberInitializers()); memberInitializers_ = member.serialize(); } MemberInitializers memberInitializers() const { MOZ_ASSERT(useMemberInitializers()); return MemberInitializers::deserialize(memberInitializers_); } #if defined(DEBUG) || defined(JS_JITSPEW) void dump() const; void dump(JSONPrinter& json) const; void dumpFields(JSONPrinter& json) const; #endif }; #if defined(DEBUG) || defined(JS_JITSPEW) void DumpTaggedParserAtomIndex(js::JSONPrinter& json, TaggedParserAtomIndex taggedIndex, const CompilationStencil* stencil); void DumpTaggedParserAtomIndexNoQuote(GenericPrinter& out, TaggedParserAtomIndex taggedIndex, const CompilationStencil* stencil); #endif } /* namespace frontend */ #if defined(DEBUG) || defined(JS_JITSPEW) void DumpImmutableScriptFlags(js::JSONPrinter& json, ImmutableScriptFlags immutableFlags); void DumpFunctionFlagsItems(js::JSONPrinter& json, FunctionFlags functionFlags); #endif } /* namespace js */ #endif /* frontend_Stencil_h */