diff options
Diffstat (limited to 'js/src/frontend/Stencil.h')
-rw-r--r-- | js/src/frontend/Stencil.h | 1151 |
1 files changed, 1151 insertions, 0 deletions
diff --git a/js/src/frontend/Stencil.h b/js/src/frontend/Stencil.h new file mode 100644 index 0000000000..91308ffcc9 --- /dev/null +++ b/js/src/frontend/Stencil.h @@ -0,0 +1,1151 @@ +/* -*- 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 <stddef.h> // size_t +#include <stdint.h> // 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/ColumnNumber.h" // JS::ColumnNumberOneOrigin +#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<TaggedParserAtomIndex>; +using ParserBindingName = AbstractBindingName<TaggedParserAtomIndex>; + +template <typename Scope> +using ParserScopeSlotInfo = typename Scope::SlotInfo; +using ParserGlobalScopeSlotInfo = ParserScopeSlotInfo<GlobalScope>; +using ParserEvalScopeSlotInfo = ParserScopeSlotInfo<EvalScope>; +using ParserLexicalScopeSlotInfo = ParserScopeSlotInfo<LexicalScope>; +using ParserClassBodyScopeSlotInfo = ParserScopeSlotInfo<ClassBodyScope>; +using ParserFunctionScopeSlotInfo = ParserScopeSlotInfo<FunctionScope>; +using ParserModuleScopeSlotInfo = ParserScopeSlotInfo<ModuleScope>; +using ParserVarScopeSlotInfo = ParserScopeSlotInfo<VarScope>; + +using ParserBindingIter = AbstractBindingIter<TaggedParserAtomIndex>; +using ParserPositionalFormalParameterIter = + AbstractPositionalFormalParameterIter<TaggedParserAtomIndex>; + +// [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<ExtensibleCompilationStencil> 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<CompilationStencil> 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<RegExpStencil>; +using BigIntIndex = TypedIndex<BigIntStencil>; +using ObjLiteralIndex = TypedIndex<ObjLiteralStencil>; + +// Index into {ExtensibleCompilationStencil,CompilationStencil}.gcThingData. +class CompilationGCThingType {}; +using CompilationGCThingIndex = TypedIndex<CompilationGCThingType>; + +// 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<char16_t> source_; + + public: + BigIntStencil() = default; + + [[nodiscard]] bool init(FrontendContext* fc, LifoAlloc& alloc, + const mozilla::Span<const char16_t> buf); + + BigInt* createBigInt(JSContext* cx) const; + + bool isZero() const; + + mozilla::Span<const char16_t> 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<frontend::TaggedParserAtomIndex>; + 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<ScopeIndex> enclosing, + uint32_t firstFrameSlot, + mozilla::Maybe<uint32_t> numEnvironmentSlots, + mozilla::Maybe<ScriptIndex> 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 <typename... Args> + 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<ScopeIndex> enclosing, ScopeIndex* index); + + static bool createForLexicalScope( + FrontendContext* fc, CompilationState& compilationState, ScopeKind kind, + LexicalScope::ParserData* dataArg, uint32_t firstFrameSlot, + mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index); + + static bool createForClassBodyScope( + FrontendContext* fc, CompilationState& compilationState, ScopeKind kind, + ClassBodyScope::ParserData* dataArg, uint32_t firstFrameSlot, + mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index); + + static bool createForVarScope(FrontendContext* fc, + CompilationState& compilationState, + ScopeKind kind, VarScope::ParserData* dataArg, + uint32_t firstFrameSlot, bool needsEnvironment, + mozilla::Maybe<ScopeIndex> 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<ScopeIndex> enclosing, + ScopeIndex* index); + + static bool createForModuleScope(FrontendContext* fc, + CompilationState& compilationState, + ModuleScope::ParserData* dataArg, + mozilla::Maybe<ScopeIndex> enclosing, + ScopeIndex* index); + + static bool createForWithScope(FrontendContext* fc, + CompilationState& compilationState, + mozilla::Maybe<ScopeIndex> 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<Scope*> 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 <typename SpecificScopeType> + UniquePtr<typename SpecificScopeType::RuntimeData> createSpecificScopeData( + JSContext* cx, CompilationAtomCache& atomCache, + BaseParserScopeData* baseData) const; + + template <typename SpecificEnvironmentType> + [[nodiscard]] bool createSpecificShape( + JSContext* cx, ScopeKind kind, BaseScopeData* scopeData, + MutableHandle<SharedShape*> shape) const; + + template <typename SpecificScopeType, typename SpecificEnvironmentType> + Scope* createSpecificScope(JSContext* cx, CompilationAtomCache& atomCache, + Handle<Scope*> enclosingScope, + BaseParserScopeData* baseData) const; + + template <typename ScopeT> + static constexpr bool matchScopeKind(ScopeKind kind) { + switch (kind) { + case ScopeKind::Function: { + return std::is_same_v<ScopeT, FunctionScope>; + } + case ScopeKind::Lexical: + case ScopeKind::SimpleCatch: + case ScopeKind::Catch: + case ScopeKind::NamedLambda: + case ScopeKind::StrictNamedLambda: + case ScopeKind::FunctionLexical: { + return std::is_same_v<ScopeT, LexicalScope>; + } + case ScopeKind::ClassBody: { + return std::is_same_v<ScopeT, ClassBodyScope>; + } + case ScopeKind::FunctionBodyVar: { + return std::is_same_v<ScopeT, VarScope>; + } + case ScopeKind::Global: + case ScopeKind::NonSyntactic: { + return std::is_same_v<ScopeT, GlobalScope>; + } + case ScopeKind::Eval: + case ScopeKind::StrictEval: { + return std::is_same_v<ScopeT, EvalScope>; + } + case ScopeKind::Module: { + return std::is_same_v<ScopeT, ModuleScope>; + } + case ScopeKind::With: { + return std::is_same_v<ScopeT, WithScope>; + } + 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<StencilModuleAssertion, 0, js::SystemAllocPolicy>; + 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;`. + + // Line number (1-origin). + uint32_t lineno = 0; + + // Column number in UTF-16 code units. + JS::ColumnNumberOneOrigin column; + + private: + StencilModuleEntry(uint32_t lineno, JS::ColumnNumberOneOrigin 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, + JS::ColumnNumberOneOrigin 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, + JS::ColumnNumberOneOrigin 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, JS::ColumnNumberOneOrigin 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, + JS::ColumnNumberOneOrigin 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, + JS::ColumnNumberOneOrigin 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, JS::ColumnNumberOneOrigin 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, + JS::ColumnNumberOneOrigin 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<StencilModuleMetadata> { + public: + using RequestVector = Vector<StencilModuleRequest, 0, js::SystemAllocPolicy>; + using EntryVector = Vector<StencilModuleEntry, 0, js::SystemAllocPolicy>; + + 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<ModuleObject*> 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<ModuleRequestVector> output) const; + bool createRequestedModules( + JSContext* cx, CompilationAtomCache& atomCache, + Handle<ModuleRequestVector> moduleRequests, + MutableHandle<RequestedModuleVector> output) const; + bool createImportEntries(JSContext* cx, CompilationAtomCache& atomCache, + Handle<ModuleRequestVector> moduleRequests, + MutableHandle<ImportEntryVector> output) const; + bool createExportEntries(JSContext* cx, CompilationAtomCache& atomCache, + Handle<ModuleRequestVector> moduleRequests, + const EntryVector& input, + MutableHandle<ExportEntryVector> 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<TaggedScriptThingIndex> 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 */ |