/* -*- 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/Attributes.h" // MOZ_MUST_USE #include "mozilla/Maybe.h" // mozilla::{Maybe, Nothing} #include "mozilla/Range.h" // mozilla::Range #include "mozilla/Span.h" // mozilla::Span #include "mozilla/Variant.h" // mozilla::Variant #include // size_t #include // char16_t, uint8_t, uint16_t, uint32_t #include "frontend/AbstractScopePtr.h" // AbstractScopePtr, ScopeIndex #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind #include "frontend/ObjLiteral.h" // ObjLiteralStencil #include "frontend/ParserAtom.h" // ParserAtom, TaggedParserAtomIndex #include "frontend/ScriptIndex.h" // ScriptIndex #include "frontend/TypedIndex.h" // TypedIndex #include "js/AllocPolicy.h" // SystemAllocPolicy #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 "util/Text.h" // DuplicateString #include "vm/BigIntType.h" // ParseBigIntLiteral #include "vm/FunctionFlags.h" // FunctionFlags #include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind #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 #include "vm/StencilEnums.h" // ImmutableScriptFlagsEnum namespace js { class JSONPrinter; class RegExpObject; namespace frontend { struct CompilationStencil; struct CompilationAtomCache; struct BaseCompilationStencil; struct CompilationGCOutput; 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 ParserFunctionScopeSlotInfo = ParserScopeSlotInfo; using ParserModuleScopeSlotInfo = ParserScopeSlotInfo; using ParserVarScopeSlotInfo = ParserScopeSlotInfo; using ParserBindingIter = AbstractBindingIter; // [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 // ------------------ // Parsing a single JavaScript file may generate a tree of `ScriptStencil` that // we then package up into the `CompilationStencil` type. This contains a series // of vectors segregated by stencil type for fast processing. Delazifying a // function will generate its bytecode but some fields remain unchanged from the // initial lazy parse. We use a base class to capture fields that are meaningful // for both the initial lazy and delazification parse. // // struct BaseCompilationStencil { // FunctionKey functionKey; // Span scriptData; // Span scopeData; // ... // } // // struct CompilationStencil : BaseCompilationStencil { // LifoAlloc alloc; // CompilationInput input; // Span scriptExtra; // ... // } // // struct CompilationStencilSet : CompilationStencil { // Span delazifications; // ... // } // // When we delazify a function that was lazily parsed, we generate a new Stencil // at the point too. These delazifications can be cached as well. When loading // back from a cache we group these together in a `CompilationStencilSet`. Note // that the base class we inherit from provides storage for the initial lazy // parse and the `delazifications` field is the collection of delazified // function data that are available. // // // 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 {CompilationState,BaseCompilationStencil}.gcThingData. class CompilationGCThingType {}; using CompilationGCThingIndex = TypedIndex; FunctionFlags InitialFunctionFlags(FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool isSelfHosting = false, bool hasUnclonedName = false); // 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_; 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, CompilationAtomCache& atomCache) const; // This is used by `Reflect.parse` when we need the RegExpObject but are not // doing a complete instantiation of the BaseCompilationStencil. RegExpObject* createRegExpAndEnsureAtom( JSContext* cx, CompilationAtomCache& atomCache, BaseCompilationStencil& stencil) const; #if defined(DEBUG) || defined(JS_JITSPEW) void dump(); void dump(JSONPrinter& json, BaseCompilationStencil* stencil); void dumpFields(JSONPrinter& json, BaseCompilationStencil* stencil); #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; UniqueTwoByteChars buf_; size_t length_ = 0; public: BigIntStencil() = default; MOZ_MUST_USE bool init(JSContext* cx, const Vector& buf) { #ifdef DEBUG // Assert we have no separators; if we have a separator then the algorithm // used in BigInt::literalIsZero will be incorrect. for (char16_t c : buf) { MOZ_ASSERT(c != '_'); } #endif length_ = buf.length(); buf_ = js::DuplicateString(cx, buf.begin(), buf.length()); return buf_ != nullptr; } BigInt* createBigInt(JSContext* cx) const { mozilla::Range source(buf_.get(), length_); return js::ParseBigIntLiteral(cx, source); } bool isZero() const { mozilla::Range source(buf_.get(), length_); return js::BigIntLiteralIsZero(source); } #if defined(DEBUG) || defined(JS_JITSPEW) void dump(); void dump(JSONPrinter& json); void dumpCharsNoQuote(GenericPrinter& out); #endif }; class ScopeStencil { friend class StencilXDR; // 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. 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. mozilla::Unused << 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(JSContext* cx, CompilationState& compilationState, BaseParserScopeData* data, ScopeIndex* indexOut, Args&&... args); public: static bool createForFunctionScope( JSContext* cx, CompilationStencil& stencil, CompilationState& compilationState, FunctionScope::ParserData* dataArg, bool hasParameterExprs, bool needsEnvironment, ScriptIndex functionIndex, bool isArrow, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForLexicalScope(JSContext* cx, CompilationStencil& stencil, CompilationState& compilationState, ScopeKind kind, LexicalScope::ParserData* dataArg, uint32_t firstFrameSlot, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForVarScope(JSContext* cx, CompilationStencil& stencil, CompilationState& compilationState, ScopeKind kind, VarScope::ParserData* dataArg, uint32_t firstFrameSlot, bool needsEnvironment, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForGlobalScope(JSContext* cx, CompilationStencil& stencil, CompilationState& compilationState, ScopeKind kind, GlobalScope::ParserData* dataArg, ScopeIndex* index); static bool createForEvalScope(JSContext* cx, CompilationStencil& stencil, CompilationState& compilationState, ScopeKind kind, EvalScope::ParserData* dataArg, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForModuleScope(JSContext* cx, CompilationStencil& stencil, CompilationState& compilationState, ModuleScope::ParserData* dataArg, mozilla::Maybe enclosing, ScopeIndex* index); static bool createForWithScope(JSContext* cx, CompilationStencil& stencil, 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; #if defined(DEBUG) || defined(JS_JITSPEW) void dump(); void dump(JSONPrinter& json, BaseParserScopeData* baseScopeData, BaseCompilationStencil* stencil); void dumpFields(JSONPrinter& json, BaseParserScopeData* baseScopeData, BaseCompilationStencil* stencil); #endif private: // Transfer ownership into a new UniquePtr. template UniquePtr createSpecificScopeData( JSContext* cx, CompilationAtomCache& atomCache, CompilationGCOutput& gcOutput, BaseParserScopeData* baseData) const; template MOZ_MUST_USE bool createSpecificShape(JSContext* cx, ScopeKind kind, BaseScopeData* scopeData, MutableHandleShape shape) const; template Scope* createSpecificScope(JSContext* cx, CompilationInput& input, CompilationGCOutput& gcOutput, 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: 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; } }; // See JSOp::Lambda for interepretation of this index. using FunctionDeclaration = GCThingIndex; using FunctionDeclarationVector = Vector; // 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: // | ModuleRequest | ImportEntry | ExportAs | ExportFrom | // |-----------------------------------------------------| // specifier | required | required | nullptr | required | // localName | null | required | required | nullptr | // importName | null | required | nullptr | required | // exportName | null | null | required | optional | TaggedParserAtomIndex specifier; 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; static StencilModuleEntry moduleRequest(TaggedParserAtomIndex specifier, uint32_t lineno, uint32_t column) { MOZ_ASSERT(!!specifier); StencilModuleEntry entry(lineno, column); entry.specifier = specifier; return entry; } static StencilModuleEntry importEntry(TaggedParserAtomIndex specifier, TaggedParserAtomIndex localName, TaggedParserAtomIndex importName, uint32_t lineno, uint32_t column) { MOZ_ASSERT(specifier && localName && importName); StencilModuleEntry entry(lineno, column); entry.specifier = specifier; entry.localName = localName; entry.importName = importName; 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(TaggedParserAtomIndex specifier, TaggedParserAtomIndex importName, TaggedParserAtomIndex exportName, uint32_t lineno, uint32_t column) { // NOTE: The `export * from "mod";` syntax generates nullptr exportName. MOZ_ASSERT(specifier && importName); StencilModuleEntry entry(lineno, column); entry.specifier = specifier; entry.importName = importName; entry.exportName = exportName; return entry; } }; // Metadata generated by parsing module scripts, including import/export tables. class StencilModuleMetadata { public: using EntryVector = Vector; 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, CompilationAtomCache& atomCache, JS::Handle module) const; #if defined(DEBUG) || defined(JS_JITSPEW) void dump(); void dump(JSONPrinter& json, BaseCompilationStencil* stencil); void dumpFields(JSONPrinter& json, BaseCompilationStencil* stencil); #endif }; // 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); } uint32_t* rawData() { 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 { public: // Fields for BaseScript. // Used by: // * Global script // * Eval // * Module // * non-lazy Function (except asm.js module) // * lazy Function (cannot be asm.js module) uint32_t memberInitializers_ = 0; // GCThings are stored into // {CompilationState,BaseCompilationStencil}.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.) ScopeIndex lazyFunctionEnclosingScopeIndex_; // 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 WasFunctionEmittedFlag = 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 BaseCompilationStencil.sharedData. static constexpr uint16_t HasSharedDataFlag = 1 << 2; // Set if this script has member initializer. // `memberInitializers_` is valid only if this flag is set. static constexpr uint16_t HasMemberInitializersFlag = 1 << 3; // True if this script is lazy function and has enclosing scope. // `lazyFunctionEnclosingScopeIndex_` is valid only if this flag is set. static constexpr uint16_t HasLazyFunctionEnclosingScopeIndexFlag = 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( BaseCompilationStencil& stencil) const; bool wasFunctionEmitted() const { return flags_ & WasFunctionEmittedFlag; } void setWasFunctionEmitted() { flags_ |= WasFunctionEmittedFlag; } bool allowRelazify() const { return flags_ & AllowRelazifyFlag; } void setAllowRelazify() { flags_ |= AllowRelazifyFlag; } bool hasSharedData() const { return flags_ & HasSharedDataFlag; } void setHasSharedData() { flags_ |= HasSharedDataFlag; } bool hasMemberInitializers() const { return flags_ & HasMemberInitializersFlag; } private: void setHasMemberInitializers() { flags_ |= HasMemberInitializersFlag; } public: void setMemberInitializers(MemberInitializers member) { memberInitializers_ = member.serialize(); setHasMemberInitializers(); } MemberInitializers memberInitializers() const { MOZ_ASSERT(hasMemberInitializers()); return MemberInitializers(memberInitializers_); } bool hasLazyFunctionEnclosingScopeIndex() const { return flags_ & HasLazyFunctionEnclosingScopeIndexFlag; } private: void setHasLazyFunctionEnclosingScopeIndex() { flags_ |= HasLazyFunctionEnclosingScopeIndexFlag; } public: void setLazyFunctionEnclosingScopeIndex(ScopeIndex index) { lazyFunctionEnclosingScopeIndex_ = index; setHasLazyFunctionEnclosingScopeIndex(); } ScopeIndex lazyFunctionEnclosingScopeIndex() const { MOZ_ASSERT(hasLazyFunctionEnclosingScopeIndex()); return lazyFunctionEnclosingScopeIndex_; } #if defined(DEBUG) || defined(JS_JITSPEW) void dump(); void dump(JSONPrinter& json, BaseCompilationStencil* stencil); void dumpFields(JSONPrinter& json, BaseCompilationStencil* stencil); #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 `JSFunction::nargs_`. uint16_t nargs = 0; // To make this struct packed, add explicit field for padding. uint16_t padding_ = 0; ScriptStencilExtra() = default; bool isModule() const { return immutableFlags.hasFlag(ImmutableScriptFlagsEnum::IsModule); } #if defined(DEBUG) || defined(JS_JITSPEW) void dump(); void dump(JSONPrinter& json); void dumpFields(JSONPrinter& json); #endif }; #if defined(DEBUG) || defined(JS_JITSPEW) void DumpTaggedParserAtomIndex(js::JSONPrinter& json, TaggedParserAtomIndex taggedIndex, BaseCompilationStencil* stencil); #endif } /* namespace frontend */ } /* namespace js */ #endif /* frontend_Stencil_h */