summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/Stencil.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/frontend/Stencil.h886
1 files changed, 886 insertions, 0 deletions
diff --git a/js/src/frontend/Stencil.h b/js/src/frontend/Stencil.h
new file mode 100644
index 0000000000..7eb4f5bc0b
--- /dev/null
+++ b/js/src/frontend/Stencil.h
@@ -0,0 +1,886 @@
+/* -*- 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 <stddef.h> // size_t
+#include <stdint.h> // 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<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 ParserFunctionScopeSlotInfo = ParserScopeSlotInfo<FunctionScope>;
+using ParserModuleScopeSlotInfo = ParserScopeSlotInfo<ModuleScope>;
+using ParserVarScopeSlotInfo = ParserScopeSlotInfo<VarScope>;
+
+using ParserBindingIter = AbstractBindingIter<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
+// ------------------
+// 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<ScriptStencil> scriptData;
+// Span<ScopeStencil> scopeData;
+// ...
+// }
+//
+// struct CompilationStencil : BaseCompilationStencil {
+// LifoAlloc alloc;
+// CompilationInput input;
+// Span<ScriptStencilExtra> scriptExtra;
+// ...
+// }
+//
+// struct CompilationStencilSet : CompilationStencil {
+// Span<BaseCompilationStencil> 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<RegExpStencil>;
+using BigIntIndex = TypedIndex<BigIntStencil>;
+using ObjLiteralIndex = TypedIndex<ObjLiteralStencil>;
+
+// Index into {CompilationState,BaseCompilationStencil}.gcThingData.
+class CompilationGCThingType {};
+using CompilationGCThingIndex = TypedIndex<CompilationGCThingType>;
+
+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<char16_t, 32>& 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<const char16_t> source(buf_.get(), length_);
+
+ return js::ParseBigIntLiteral(cx, source);
+ }
+
+ bool isZero() const {
+ mozilla::Range<const char16_t> 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<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.
+ mozilla::Unused << 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(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<ScopeIndex> enclosing, ScopeIndex* index);
+
+ static bool createForLexicalScope(JSContext* cx, CompilationStencil& stencil,
+ CompilationState& compilationState,
+ ScopeKind kind,
+ LexicalScope::ParserData* dataArg,
+ uint32_t firstFrameSlot,
+ mozilla::Maybe<ScopeIndex> enclosing,
+ ScopeIndex* index);
+
+ static bool createForVarScope(JSContext* cx, CompilationStencil& stencil,
+ CompilationState& compilationState,
+ ScopeKind kind, VarScope::ParserData* dataArg,
+ uint32_t firstFrameSlot, bool needsEnvironment,
+ mozilla::Maybe<ScopeIndex> 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<ScopeIndex> enclosing,
+ ScopeIndex* index);
+
+ static bool createForModuleScope(JSContext* cx, CompilationStencil& stencil,
+ CompilationState& compilationState,
+ ModuleScope::ParserData* dataArg,
+ mozilla::Maybe<ScopeIndex> enclosing,
+ ScopeIndex* index);
+
+ static bool createForWithScope(JSContext* cx, CompilationStencil& stencil,
+ 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;
+
+#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 <typename SpecificScopeType>
+ UniquePtr<typename SpecificScopeType::RuntimeData> createSpecificScopeData(
+ JSContext* cx, CompilationAtomCache& atomCache,
+ CompilationGCOutput& gcOutput, BaseParserScopeData* baseData) const;
+
+ template <typename SpecificEnvironmentType>
+ MOZ_MUST_USE bool createSpecificShape(JSContext* cx, ScopeKind kind,
+ BaseScopeData* scopeData,
+ MutableHandleShape shape) const;
+
+ template <typename SpecificScopeType, typename SpecificEnvironmentType>
+ Scope* createSpecificScope(JSContext* cx, CompilationInput& input,
+ CompilationGCOutput& gcOutput,
+ 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:
+ case ScopeKind::ClassBody: {
+ return std::is_same_v<ScopeT, LexicalScope>;
+ }
+ 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;
+ }
+};
+
+// See JSOp::Lambda for interepretation of this index.
+using FunctionDeclaration = GCThingIndex;
+using FunctionDeclarationVector =
+ Vector<FunctionDeclaration, 0, js::SystemAllocPolicy>;
+
+// 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<StencilModuleEntry, 0, js::SystemAllocPolicy>;
+
+ 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<ModuleObject*> 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<TaggedScriptThingIndex> 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 */