Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "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/ObjLiteral.h" // ObjLiteralStencil
#include "frontend/ParserAtom.h" // TaggedParserAtomIndex
#include "frontend/ScriptIndex.h" // ScriptIndex
#include "frontend/TaggedParserAtomIndexHasher.h" // frontend::TaggedParserAtomIndexHasher
#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.
//
//
// 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.
//
// Int64-sized BigInt values are directly stored inline as int64_t.
mozilla::Variant<mozilla::Span<char16_t>, int64_t> bigInt_{int64_t{}};
// Methods used by XDR.
mozilla::Span<char16_t>& source() {
if (bigInt_.is<int64_t>()) {
bigInt_ = mozilla::AsVariant(mozilla::Span<char16_t>{});
}
return bigInt_.as<mozilla::Span<char16_t>>();
}
const mozilla::Span<char16_t>& source() const {
return bigInt_.as<mozilla::Span<char16_t>>();
}
[[nodiscard]] bool initFromChars(FrontendContext* fc, LifoAlloc& alloc,
mozilla::Span<const char16_t> buf);
public:
BigIntStencil() = default;
[[nodiscard]] bool init(FrontendContext* fc, LifoAlloc& alloc,
mozilla::Span<const char16_t> buf);
[[nodiscard]] bool init(FrontendContext* fc, LifoAlloc& alloc,
const BigIntStencil& other);
BigInt* createBigInt(JSContext* cx) const;
// Methods used by constant-folding.
bool isZero() const;
bool inplaceNegate();
bool inplaceBitNot();
#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
};
using BigIntStencilVector = Vector<BigIntStencil, 0, js::SystemAllocPolicy>;
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 StencilModuleImportAttribute {
public:
TaggedParserAtomIndex key;
TaggedParserAtomIndex value;
StencilModuleImportAttribute() = default;
StencilModuleImportAttribute(TaggedParserAtomIndex key,
TaggedParserAtomIndex value)
: key(key), value(value) {}
bool operator!=(const StencilModuleImportAttribute& rhs) const {
return key != rhs.key || value != rhs.value;
}
bool operator==(const StencilModuleImportAttribute& rhs) const {
return !(*this != rhs);
}
};
class StencilModuleRequest {
public:
TaggedParserAtomIndex specifier;
TaggedParserAtomIndex firstUnsupportedAttributeKey;
using ImportAttributeVector =
Vector<StencilModuleImportAttribute, 0, js::SystemAllocPolicy>;
ImportAttributeVector attributes;
// For XDR only.
StencilModuleRequest() = default;
explicit StencilModuleRequest(TaggedParserAtomIndex specifier)
: specifier(specifier) {
MOZ_ASSERT(specifier);
}
StencilModuleRequest(const StencilModuleRequest& other)
: specifier(other.specifier),
firstUnsupportedAttributeKey(other.firstUnsupportedAttributeKey) {
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!attributes.appendAll(other.attributes)) {
oomUnsafe.crash("StencilModuleRequest::StencilModuleRequest");
}
}
StencilModuleRequest(StencilModuleRequest&& other) noexcept
: specifier(other.specifier),
firstUnsupportedAttributeKey(other.firstUnsupportedAttributeKey),
attributes(std::move(other.attributes)) {}
StencilModuleRequest& operator=(StencilModuleRequest& other) {
specifier = other.specifier;
firstUnsupportedAttributeKey = other.firstUnsupportedAttributeKey;
attributes = std::move(other.attributes);
return *this;
}
StencilModuleRequest& operator=(StencilModuleRequest&& other) noexcept {
specifier = other.specifier;
firstUnsupportedAttributeKey = other.firstUnsupportedAttributeKey;
attributes = std::move(other.attributes);
return *this;
}
bool operator==(const StencilModuleRequest& other) const {
size_t attrLen = attributes.length();
if (specifier != other.specifier ||
firstUnsupportedAttributeKey != other.firstUnsupportedAttributeKey ||
attrLen != other.attributes.length()) {
return false;
}
for (size_t i = 0; i < attrLen; i++) {
if (attributes[i] != other.attributes[i]) {
return false;
}
}
return true;
}
bool operator!=(const StencilModuleRequest& other) const {
return !(*this == other);
}
};
struct StencilModuleRequestHasher {
using Key = js::frontend::StencilModuleRequest;
using Lookup = Key;
static HashNumber hash(const Lookup& l) {
HashNumber hash = 0;
size_t attrLen = l.attributes.length();
for (size_t i = 0; i < attrLen; i++) {
hash = mozilla::AddToHash(
hash, TaggedParserAtomIndexHasher::hash(l.attributes[i].key),
TaggedParserAtomIndexHasher::hash(l.attributes[i].value));
}
return mozilla::AddToHash(hash,
TaggedParserAtomIndexHasher::hash(l.specifier));
}
static bool match(const Key& k, const Lookup& l) { return k == l; }
};
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.
//
//
// 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 */