summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/Stencil.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/Stencil.h')
-rw-r--r--js/src/frontend/Stencil.h1141
1 files changed, 1141 insertions, 0 deletions
diff --git a/js/src/frontend/Stencil.h b/js/src/frontend/Stencil.h
new file mode 100644
index 0000000000..a5aa84e787
--- /dev/null
+++ b/js/src/frontend/Stencil.h
@@ -0,0 +1,1141 @@
+/* -*- 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/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;`.
+ 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<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 */