1767 lines
60 KiB
C++
1767 lines
60 KiB
C++
/* -*- 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/. */
|
|
|
|
#include "vm/Scope.h"
|
|
|
|
#include <new>
|
|
|
|
#include "jsnum.h"
|
|
|
|
#include "frontend/CompilationStencil.h" // ScopeStencilRef, CompilationStencil, CompilationState, CompilationAtomCache
|
|
#include "frontend/ParserAtom.h" // frontend::ParserAtomsTable, frontend::ParserAtom
|
|
#include "frontend/ScriptIndex.h" // ScriptIndex
|
|
#include "frontend/Stencil.h"
|
|
#include "util/StringBuilder.h"
|
|
#include "vm/EnvironmentObject.h"
|
|
#include "vm/ErrorReporting.h" // MaybePrintAndClearPendingException
|
|
#include "vm/JSScript.h"
|
|
#include "wasm/WasmDebug.h"
|
|
#include "wasm/WasmInstance.h"
|
|
|
|
#include "gc/GCContext-inl.h"
|
|
#include "gc/ObjectKind-inl.h"
|
|
#include "gc/TraceMethods-inl.h"
|
|
#include "vm/JSContext-inl.h"
|
|
#include "wasm/WasmInstance-inl.h"
|
|
|
|
using namespace js;
|
|
using namespace js::frontend;
|
|
|
|
const char* js::BindingKindString(BindingKind kind) {
|
|
switch (kind) {
|
|
case BindingKind::Import:
|
|
return "import";
|
|
case BindingKind::FormalParameter:
|
|
return "formal parameter";
|
|
case BindingKind::Var:
|
|
return "var";
|
|
case BindingKind::Let:
|
|
return "let";
|
|
case BindingKind::Const:
|
|
return "const";
|
|
case BindingKind::NamedLambdaCallee:
|
|
return "named lambda callee";
|
|
case BindingKind::Synthetic:
|
|
return "synthetic";
|
|
case BindingKind::PrivateMethod:
|
|
return "private method";
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
case BindingKind::Using:
|
|
return "using";
|
|
#endif
|
|
}
|
|
MOZ_CRASH("Bad BindingKind");
|
|
}
|
|
|
|
const char* js::ScopeKindString(ScopeKind kind) {
|
|
switch (kind) {
|
|
case ScopeKind::Function:
|
|
return "function";
|
|
case ScopeKind::FunctionBodyVar:
|
|
return "function body var";
|
|
case ScopeKind::Lexical:
|
|
return "lexical";
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
return "catch";
|
|
case ScopeKind::NamedLambda:
|
|
return "named lambda";
|
|
case ScopeKind::StrictNamedLambda:
|
|
return "strict named lambda";
|
|
case ScopeKind::FunctionLexical:
|
|
return "function lexical";
|
|
case ScopeKind::ClassBody:
|
|
return "class body";
|
|
case ScopeKind::With:
|
|
return "with";
|
|
case ScopeKind::Eval:
|
|
return "eval";
|
|
case ScopeKind::StrictEval:
|
|
return "strict eval";
|
|
case ScopeKind::Global:
|
|
return "global";
|
|
case ScopeKind::NonSyntactic:
|
|
return "non-syntactic";
|
|
case ScopeKind::Module:
|
|
return "module";
|
|
case ScopeKind::WasmInstance:
|
|
return "wasm instance";
|
|
case ScopeKind::WasmFunction:
|
|
return "wasm function";
|
|
}
|
|
MOZ_CRASH("Bad ScopeKind");
|
|
}
|
|
|
|
SharedShape* js::EmptyEnvironmentShape(JSContext* cx, const JSClass* cls,
|
|
uint32_t numSlots,
|
|
ObjectFlags objectFlags) {
|
|
// Put as many slots into the object header as possible.
|
|
uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots));
|
|
return SharedShape::getInitialShape(
|
|
cx, cls, cx->realm(), TaggedProto(nullptr), numFixed, objectFlags);
|
|
}
|
|
|
|
static bool AddToEnvironmentMap(JSContext* cx, const JSClass* clasp,
|
|
HandleId id, BindingKind bindKind,
|
|
uint32_t slot,
|
|
MutableHandle<SharedPropMap*> map,
|
|
uint32_t* mapLength, ObjectFlags* objectFlags) {
|
|
PropertyFlags propFlags = {PropertyFlag::Enumerable};
|
|
switch (bindKind) {
|
|
case BindingKind::Const:
|
|
case BindingKind::NamedLambdaCallee:
|
|
// Non-writable.
|
|
break;
|
|
default:
|
|
propFlags.setFlag(PropertyFlag::Writable);
|
|
break;
|
|
}
|
|
|
|
return SharedPropMap::addPropertyWithKnownSlot(cx, clasp, map, mapLength, id,
|
|
propFlags, slot, objectFlags);
|
|
}
|
|
|
|
SharedShape* js::CreateEnvironmentShape(JSContext* cx, BindingIter& bi,
|
|
const JSClass* cls, uint32_t numSlots,
|
|
ObjectFlags objectFlags) {
|
|
Rooted<SharedPropMap*> map(cx);
|
|
uint32_t mapLength = 0;
|
|
|
|
RootedId id(cx);
|
|
for (; bi; bi++) {
|
|
BindingLocation loc = bi.location();
|
|
if (loc.kind() == BindingLocation::Kind::Environment) {
|
|
JSAtom* name = bi.name();
|
|
MOZ_ASSERT(AtomIsMarked(cx->zone(), name));
|
|
id = NameToId(name->asPropertyName());
|
|
if (!AddToEnvironmentMap(cx, cls, id, bi.kind(), loc.slot(), &map,
|
|
&mapLength, &objectFlags)) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots));
|
|
return SharedShape::getInitialOrPropMapShape(cx, cls, cx->realm(),
|
|
TaggedProto(nullptr), numFixed,
|
|
map, mapLength, objectFlags);
|
|
}
|
|
|
|
SharedShape* js::CreateEnvironmentShapeForSyntheticModule(
|
|
JSContext* cx, const JSClass* cls, uint32_t numSlots,
|
|
Handle<ModuleObject*> module) {
|
|
Rooted<SharedPropMap*> map(cx);
|
|
uint32_t mapLength = 0;
|
|
|
|
PropertyFlags propFlags = {PropertyFlag::Enumerable};
|
|
ObjectFlags objectFlags = ModuleEnvironmentObject::OBJECT_FLAGS;
|
|
|
|
RootedId id(cx);
|
|
uint32_t slotIndex = numSlots;
|
|
for (JSAtom* exportName : module->syntheticExportNames()) {
|
|
id = NameToId(exportName->asPropertyName());
|
|
if (!SharedPropMap::addPropertyWithKnownSlot(cx, cls, &map, &mapLength, id,
|
|
propFlags, slotIndex,
|
|
&objectFlags)) {
|
|
return nullptr;
|
|
}
|
|
slotIndex++;
|
|
}
|
|
|
|
uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots));
|
|
return SharedShape::getInitialOrPropMapShape(cx, cls, cx->realm(),
|
|
TaggedProto(nullptr), numFixed,
|
|
map, mapLength, objectFlags);
|
|
}
|
|
|
|
template <class DataT>
|
|
inline size_t SizeOfAllocatedData(DataT* data) {
|
|
return SizeOfScopeData<DataT>(data->length);
|
|
}
|
|
|
|
template <typename ConcreteScope>
|
|
static void MarkParserScopeData(typename ConcreteScope::ParserData* data,
|
|
frontend::CompilationState& compilationState) {
|
|
auto names = GetScopeDataTrailingNames(data);
|
|
for (auto& binding : names) {
|
|
auto index = binding.name();
|
|
if (!index) {
|
|
continue;
|
|
}
|
|
compilationState.parserAtoms.markUsedByStencil(
|
|
index, frontend::ParserAtom::Atomize::Yes);
|
|
}
|
|
}
|
|
|
|
template <typename ConcreteScope, typename EnvironmentT>
|
|
static void PrepareScopeData(ParserBindingIter& bi,
|
|
typename ConcreteScope::ParserData* data,
|
|
uint32_t firstFrameSlot,
|
|
mozilla::Maybe<uint32_t>* envShape) {
|
|
const JSClass* cls = &EnvironmentT::class_;
|
|
|
|
// Iterate through all bindings. This counts the number of environment
|
|
// slots needed and computes the maximum frame slot.
|
|
while (bi) {
|
|
bi++;
|
|
}
|
|
data->slotInfo.nextFrameSlot =
|
|
bi.canHaveFrameSlots() ? bi.nextFrameSlot() : LOCALNO_LIMIT;
|
|
|
|
// Make a new environment shape if any environment slots were used.
|
|
if (bi.nextEnvironmentSlot() != JSSLOT_FREE(cls)) {
|
|
envShape->emplace(bi.nextEnvironmentSlot());
|
|
}
|
|
}
|
|
|
|
template <typename ConcreteScope>
|
|
static typename ConcreteScope::ParserData* NewEmptyParserScopeData(
|
|
FrontendContext* fc, LifoAlloc& alloc, uint32_t length = 0) {
|
|
using Data = typename ConcreteScope::ParserData;
|
|
|
|
size_t dataSize = SizeOfScopeData<Data>(length);
|
|
void* raw = alloc.alloc(dataSize);
|
|
if (!raw) {
|
|
js::ReportOutOfMemory(fc);
|
|
return nullptr;
|
|
}
|
|
|
|
return new (raw) Data(length);
|
|
}
|
|
|
|
template <typename ConcreteScope, typename AtomT>
|
|
static UniquePtr<AbstractScopeData<ConcreteScope, AtomT>> NewEmptyScopeData(
|
|
JSContext* cx, uint32_t length = 0) {
|
|
using Data = AbstractScopeData<ConcreteScope, AtomT>;
|
|
|
|
size_t dataSize = SizeOfScopeData<Data>(length);
|
|
uint8_t* bytes = cx->pod_malloc<uint8_t>(dataSize);
|
|
auto data = reinterpret_cast<Data*>(bytes);
|
|
if (data) {
|
|
new (data) Data(length);
|
|
}
|
|
return UniquePtr<Data>(data);
|
|
}
|
|
|
|
template <typename ConcreteScope>
|
|
static UniquePtr<typename ConcreteScope::RuntimeData> LiftParserScopeData(
|
|
JSContext* cx, frontend::CompilationAtomCache& atomCache,
|
|
BaseParserScopeData* baseData) {
|
|
using ConcreteData = typename ConcreteScope::RuntimeData;
|
|
|
|
auto* data = static_cast<typename ConcreteScope::ParserData*>(baseData);
|
|
|
|
// Convert all scope ParserAtoms to rooted JSAtoms.
|
|
// Rooting is necessary as conversion can gc.
|
|
JS::RootedVector<JSAtom*> jsatoms(cx);
|
|
if (!jsatoms.reserve(data->length)) {
|
|
return nullptr;
|
|
}
|
|
auto names = GetScopeDataTrailingNames(data);
|
|
for (size_t i = 0; i < names.size(); i++) {
|
|
JSAtom* jsatom = nullptr;
|
|
if (names[i].name()) {
|
|
jsatom = atomCache.getExistingAtomAt(cx, names[i].name());
|
|
MOZ_ASSERT(jsatom);
|
|
}
|
|
jsatoms.infallibleAppend(jsatom);
|
|
}
|
|
|
|
// Allocate a new scope-data of the right kind.
|
|
UniquePtr<ConcreteData> scopeData(
|
|
NewEmptyScopeData<ConcreteScope, JSAtom>(cx, data->length));
|
|
if (!scopeData) {
|
|
return nullptr;
|
|
}
|
|
|
|
// NOTE: There shouldn't be any fallible operation or GC between setting
|
|
// `length` and filling `trailingNames`.
|
|
scopeData.get()->length = data->length;
|
|
|
|
memcpy(&scopeData.get()->slotInfo, &data->slotInfo,
|
|
sizeof(typename ConcreteScope::SlotInfo));
|
|
|
|
// Initialize new scoped names.
|
|
auto namesOut = GetScopeDataTrailingNames(scopeData.get());
|
|
MOZ_ASSERT(data->length == namesOut.size());
|
|
for (size_t i = 0; i < namesOut.size(); i++) {
|
|
namesOut[i] = names[i].copyWithNewAtom(jsatoms[i].get());
|
|
}
|
|
|
|
return scopeData;
|
|
}
|
|
|
|
/* static */
|
|
Scope* Scope::create(JSContext* cx, ScopeKind kind, Handle<Scope*> enclosing,
|
|
Handle<SharedShape*> envShape) {
|
|
return cx->newCell<Scope>(kind, enclosing, envShape);
|
|
}
|
|
|
|
template <typename ConcreteScope>
|
|
/* static */
|
|
ConcreteScope* Scope::create(
|
|
JSContext* cx, ScopeKind kind, Handle<Scope*> enclosing,
|
|
Handle<SharedShape*> envShape,
|
|
MutableHandle<UniquePtr<typename ConcreteScope::RuntimeData>> data) {
|
|
Scope* scope = create(cx, kind, enclosing, envShape);
|
|
if (!scope) {
|
|
return nullptr;
|
|
}
|
|
|
|
// It is an invariant that all Scopes that have data (currently, all
|
|
// ScopeKinds except With) must have non-null data.
|
|
MOZ_ASSERT(data);
|
|
scope->initData<ConcreteScope>(data);
|
|
|
|
return &scope->as<ConcreteScope>();
|
|
}
|
|
|
|
template <typename ConcreteScope>
|
|
inline void Scope::initData(
|
|
MutableHandle<UniquePtr<typename ConcreteScope::RuntimeData>> data) {
|
|
MOZ_ASSERT(!rawData());
|
|
|
|
AddCellMemory(this, SizeOfAllocatedData(data.get().get()),
|
|
MemoryUse::ScopeData);
|
|
|
|
setHeaderPtr(data.get().release());
|
|
}
|
|
|
|
void Scope::updateEnvShapeIfRequired(mozilla::Maybe<uint32_t>* envShape,
|
|
bool needsEnvironment) {
|
|
if (envShape->isNothing() && needsEnvironment) {
|
|
uint32_t numSlots = 0;
|
|
envShape->emplace(numSlots);
|
|
}
|
|
}
|
|
|
|
uint32_t Scope::firstFrameSlot() const {
|
|
switch (kind()) {
|
|
case ScopeKind::Lexical:
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
case ScopeKind::FunctionLexical:
|
|
// For intra-frame scopes, find the enclosing scope's next frame slot.
|
|
MOZ_ASSERT(is<LexicalScope>());
|
|
return LexicalScope::nextFrameSlot(enclosing());
|
|
|
|
case ScopeKind::NamedLambda:
|
|
case ScopeKind::StrictNamedLambda:
|
|
// Named lambda scopes cannot have frame slots.
|
|
return LOCALNO_LIMIT;
|
|
|
|
case ScopeKind::ClassBody:
|
|
MOZ_ASSERT(is<ClassBodyScope>());
|
|
return ClassBodyScope::nextFrameSlot(enclosing());
|
|
|
|
case ScopeKind::FunctionBodyVar:
|
|
if (enclosing()->is<FunctionScope>()) {
|
|
return enclosing()->as<FunctionScope>().nextFrameSlot();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32_t Scope::chainLength() const {
|
|
uint32_t length = 0;
|
|
for (ScopeIter si(const_cast<Scope*>(this)); si; si++) {
|
|
length++;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
uint32_t Scope::environmentChainLength() const {
|
|
uint32_t length = 0;
|
|
for (ScopeIter si(const_cast<Scope*>(this)); si; si++) {
|
|
if (si.hasSyntacticEnvironment()) {
|
|
length++;
|
|
}
|
|
}
|
|
return length;
|
|
}
|
|
|
|
void Scope::finalize(JS::GCContext* gcx) {
|
|
MOZ_ASSERT(CurrentThreadIsGCFinalizing());
|
|
applyScopeDataTyped([this, gcx](auto data) {
|
|
gcx->delete_(this, data, SizeOfAllocatedData(data), MemoryUse::ScopeData);
|
|
});
|
|
setHeaderPtr(nullptr);
|
|
}
|
|
|
|
size_t Scope::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
|
|
if (rawData()) {
|
|
return mallocSizeOf(rawData());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void Scope::dump() {
|
|
JSContext* cx = TlsContext.get();
|
|
if (!cx) {
|
|
fprintf(stderr, "*** can't get JSContext for current thread\n");
|
|
return;
|
|
}
|
|
for (Rooted<ScopeIter> si(cx, ScopeIter(this)); si; si++) {
|
|
fprintf(stderr, "- %s [%p]\n", ScopeKindString(si.kind()), si.scope());
|
|
DumpBindings(cx, si.scope());
|
|
fprintf(stderr, "\n");
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
#if defined(DEBUG) || defined(JS_JITSPEW)
|
|
|
|
/* static */
|
|
bool Scope::dumpForDisassemble(JSContext* cx, JS::Handle<Scope*> scope,
|
|
GenericPrinter& out, const char* indent) {
|
|
out.put(ScopeKindString(scope->kind()));
|
|
out.put(" {");
|
|
|
|
size_t i = 0;
|
|
for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++, i++) {
|
|
if (i == 0) {
|
|
out.put("\n");
|
|
}
|
|
UniqueChars bytes = AtomToPrintableString(cx, bi.name());
|
|
if (!bytes) {
|
|
return false;
|
|
}
|
|
out.put(indent);
|
|
out.printf(" %2zu: %s %s ", i, BindingKindString(bi.kind()), bytes.get());
|
|
switch (bi.location().kind()) {
|
|
case BindingLocation::Kind::Global:
|
|
if (bi.isTopLevelFunction()) {
|
|
out.put("(global function)\n");
|
|
} else {
|
|
out.put("(global)\n");
|
|
}
|
|
break;
|
|
case BindingLocation::Kind::Argument:
|
|
out.printf("(arg slot %u)\n", bi.location().argumentSlot());
|
|
break;
|
|
case BindingLocation::Kind::Frame:
|
|
out.printf("(frame slot %u)\n", bi.location().slot());
|
|
break;
|
|
case BindingLocation::Kind::Environment:
|
|
out.printf("(env slot %u)\n", bi.location().slot());
|
|
break;
|
|
case BindingLocation::Kind::NamedLambdaCallee:
|
|
out.put("(named lambda callee)\n");
|
|
break;
|
|
case BindingLocation::Kind::Import:
|
|
out.put("(import)\n");
|
|
break;
|
|
}
|
|
}
|
|
if (i > 0) {
|
|
out.put(indent);
|
|
}
|
|
out.put("}");
|
|
|
|
ScopeIter si(scope);
|
|
si++;
|
|
for (; si; si++) {
|
|
out.put(" -> ");
|
|
out.put(ScopeKindString(si.kind()));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endif /* defined(DEBUG) || defined(JS_JITSPEW) */
|
|
|
|
static uint32_t NextFrameSlot(Scope* scope) {
|
|
for (ScopeIter si(scope); si; si++) {
|
|
switch (si.kind()) {
|
|
case ScopeKind::With:
|
|
continue;
|
|
|
|
case ScopeKind::Function:
|
|
return si.scope()->as<FunctionScope>().nextFrameSlot();
|
|
|
|
case ScopeKind::FunctionBodyVar:
|
|
return si.scope()->as<VarScope>().nextFrameSlot();
|
|
|
|
case ScopeKind::Lexical:
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
case ScopeKind::FunctionLexical:
|
|
return si.scope()->as<LexicalScope>().nextFrameSlot();
|
|
|
|
case ScopeKind::ClassBody:
|
|
return si.scope()->as<ClassBodyScope>().nextFrameSlot();
|
|
|
|
case ScopeKind::NamedLambda:
|
|
case ScopeKind::StrictNamedLambda:
|
|
// Named lambda scopes cannot have frame slots.
|
|
return 0;
|
|
|
|
case ScopeKind::Eval:
|
|
case ScopeKind::StrictEval:
|
|
return si.scope()->as<EvalScope>().nextFrameSlot();
|
|
|
|
case ScopeKind::Global:
|
|
case ScopeKind::NonSyntactic:
|
|
return 0;
|
|
|
|
case ScopeKind::Module:
|
|
return si.scope()->as<ModuleScope>().nextFrameSlot();
|
|
|
|
case ScopeKind::WasmInstance:
|
|
case ScopeKind::WasmFunction:
|
|
// Invalid; MOZ_CRASH below.
|
|
break;
|
|
}
|
|
}
|
|
MOZ_CRASH("Not an enclosing intra-frame Scope");
|
|
}
|
|
|
|
/* static */
|
|
uint32_t LexicalScope::nextFrameSlot(Scope* scope) {
|
|
return NextFrameSlot(scope);
|
|
}
|
|
|
|
/* static */
|
|
uint32_t ClassBodyScope::nextFrameSlot(Scope* scope) {
|
|
return NextFrameSlot(scope);
|
|
}
|
|
|
|
/* static */
|
|
void LexicalScope::prepareForScopeCreation(ScopeKind kind,
|
|
uint32_t firstFrameSlot,
|
|
LexicalScope::ParserData* data,
|
|
mozilla::Maybe<uint32_t>* envShape) {
|
|
bool isNamedLambda =
|
|
kind == ScopeKind::NamedLambda || kind == ScopeKind::StrictNamedLambda;
|
|
|
|
MOZ_ASSERT_IF(isNamedLambda, firstFrameSlot == LOCALNO_LIMIT);
|
|
|
|
ParserBindingIter bi(*data, firstFrameSlot, isNamedLambda);
|
|
PrepareScopeData<LexicalScope, BlockLexicalEnvironmentObject>(
|
|
bi, data, firstFrameSlot, envShape);
|
|
}
|
|
|
|
/* static */
|
|
SharedShape* LexicalScope::getEmptyExtensibleEnvironmentShape(JSContext* cx) {
|
|
const JSClass* cls = &LexicalEnvironmentObject::class_;
|
|
return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), ObjectFlags());
|
|
}
|
|
|
|
/* static */
|
|
void ClassBodyScope::prepareForScopeCreation(
|
|
ScopeKind kind, uint32_t firstFrameSlot, ClassBodyScope::ParserData* data,
|
|
mozilla::Maybe<uint32_t>* envShape) {
|
|
MOZ_ASSERT(kind == ScopeKind::ClassBody);
|
|
|
|
ParserBindingIter bi(*data, firstFrameSlot);
|
|
PrepareScopeData<ClassBodyScope, BlockLexicalEnvironmentObject>(
|
|
bi, data, firstFrameSlot, envShape);
|
|
}
|
|
|
|
/* static */
|
|
void FunctionScope::prepareForScopeCreation(
|
|
FunctionScope::ParserData* data, bool hasParameterExprs,
|
|
bool needsEnvironment, mozilla::Maybe<uint32_t>* envShape) {
|
|
uint32_t firstFrameSlot = 0;
|
|
ParserBindingIter bi(*data, hasParameterExprs);
|
|
PrepareScopeData<FunctionScope, CallObject>(bi, data, firstFrameSlot,
|
|
envShape);
|
|
|
|
if (hasParameterExprs) {
|
|
data->slotInfo.setHasParameterExprs();
|
|
}
|
|
|
|
// An environment may be needed regardless of existence of any closed over
|
|
// bindings:
|
|
// - Extensible scopes (i.e., due to direct eval)
|
|
// - Needing a home object
|
|
// - Being a derived class constructor
|
|
// - Being a generator or async function
|
|
// Also see |FunctionBox::needsExtraBodyVarEnvironmentRegardlessOfBindings()|.
|
|
updateEnvShapeIfRequired(envShape, needsEnvironment);
|
|
}
|
|
|
|
JSScript* FunctionScope::script() const {
|
|
return canonicalFunction()->nonLazyScript();
|
|
}
|
|
|
|
/* static */
|
|
bool FunctionScope::isSpecialName(frontend::TaggedParserAtomIndex name) {
|
|
return name == frontend::TaggedParserAtomIndex::WellKnown::arguments() ||
|
|
name == frontend::TaggedParserAtomIndex::WellKnown::dot_this_() ||
|
|
name == frontend::TaggedParserAtomIndex::WellKnown::dot_newTarget_() ||
|
|
name == frontend::TaggedParserAtomIndex::WellKnown::dot_generator_();
|
|
}
|
|
|
|
/* static */
|
|
void VarScope::prepareForScopeCreation(ScopeKind kind,
|
|
VarScope::ParserData* data,
|
|
uint32_t firstFrameSlot,
|
|
bool needsEnvironment,
|
|
mozilla::Maybe<uint32_t>* envShape) {
|
|
ParserBindingIter bi(*data, firstFrameSlot);
|
|
PrepareScopeData<VarScope, VarEnvironmentObject>(bi, data, firstFrameSlot,
|
|
envShape);
|
|
|
|
// An environment may be needed regardless of existence of any closed over
|
|
// bindings:
|
|
// - Extensible scopes (i.e., due to direct eval)
|
|
// - Being a generator
|
|
updateEnvShapeIfRequired(envShape, needsEnvironment);
|
|
}
|
|
|
|
GlobalScope* GlobalScope::createEmpty(JSContext* cx, ScopeKind kind) {
|
|
Rooted<UniquePtr<RuntimeData>> data(
|
|
cx, NewEmptyScopeData<GlobalScope, JSAtom>(cx));
|
|
if (!data) {
|
|
return nullptr;
|
|
}
|
|
|
|
return createWithData(cx, kind, &data);
|
|
}
|
|
|
|
/* static */
|
|
GlobalScope* GlobalScope::createWithData(
|
|
JSContext* cx, ScopeKind kind, MutableHandle<UniquePtr<RuntimeData>> data) {
|
|
MOZ_ASSERT(data);
|
|
|
|
// The global scope has no environment shape. Its environment is the
|
|
// global lexical scope and the global object or non-syntactic objects
|
|
// created by embedding, all of which are not only extensible but may
|
|
// have names on them deleted.
|
|
return Scope::create<GlobalScope>(cx, kind, nullptr, nullptr, data);
|
|
}
|
|
|
|
/* static */
|
|
WithScope* WithScope::create(JSContext* cx, Handle<Scope*> enclosing) {
|
|
Scope* scope = Scope::create(cx, ScopeKind::With, enclosing, nullptr);
|
|
return static_cast<WithScope*>(scope);
|
|
}
|
|
|
|
/* static */
|
|
void EvalScope::prepareForScopeCreation(ScopeKind scopeKind,
|
|
EvalScope::ParserData* data,
|
|
mozilla::Maybe<uint32_t>* envShape) {
|
|
if (scopeKind == ScopeKind::StrictEval) {
|
|
uint32_t firstFrameSlot = 0;
|
|
ParserBindingIter bi(*data, true);
|
|
PrepareScopeData<EvalScope, VarEnvironmentObject>(bi, data, firstFrameSlot,
|
|
envShape);
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
Scope* EvalScope::nearestVarScopeForDirectEval(Scope* scope) {
|
|
for (ScopeIter si(scope); si; si++) {
|
|
switch (si.kind()) {
|
|
case ScopeKind::Function:
|
|
case ScopeKind::FunctionBodyVar:
|
|
case ScopeKind::Global:
|
|
case ScopeKind::NonSyntactic:
|
|
return scope;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
ModuleScope::RuntimeData::RuntimeData(size_t length) {
|
|
PoisonNames(this, length);
|
|
}
|
|
|
|
/* static */
|
|
void ModuleScope::prepareForScopeCreation(ModuleScope::ParserData* data,
|
|
mozilla::Maybe<uint32_t>* envShape) {
|
|
uint32_t firstFrameSlot = 0;
|
|
ParserBindingIter bi(*data);
|
|
PrepareScopeData<ModuleScope, ModuleEnvironmentObject>(
|
|
bi, data, firstFrameSlot, envShape);
|
|
|
|
// Modules always need an environment object for now.
|
|
bool needsEnvironment = true;
|
|
updateEnvShapeIfRequired(envShape, needsEnvironment);
|
|
}
|
|
|
|
template <size_t ArrayLength>
|
|
static JSAtom* GenerateWasmName(JSContext* cx,
|
|
const char (&prefix)[ArrayLength],
|
|
uint32_t index) {
|
|
StringBuilder sb(cx);
|
|
if (!sb.append(prefix)) {
|
|
return nullptr;
|
|
}
|
|
if (!NumberValueToStringBuilder(NumberValue(index), sb)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return sb.finishAtom();
|
|
}
|
|
|
|
static void InitializeTrailingName(AbstractBindingName<JSAtom>* trailingNames,
|
|
size_t i, JSAtom* name) {
|
|
void* trailingName = &trailingNames[i];
|
|
new (trailingName) BindingName(name, false);
|
|
}
|
|
|
|
template <class DataT>
|
|
static void InitializeNextTrailingName(const Rooted<UniquePtr<DataT>>& data,
|
|
JSAtom* name) {
|
|
InitializeTrailingName(GetScopeDataTrailingNamesPointer(data.get().get()),
|
|
data->length, name);
|
|
data->length++;
|
|
}
|
|
|
|
WasmInstanceScope::RuntimeData::RuntimeData(size_t length) {
|
|
PoisonNames(this, length);
|
|
}
|
|
|
|
/* static */
|
|
WasmInstanceScope* WasmInstanceScope::create(JSContext* cx,
|
|
WasmInstanceObject* instance) {
|
|
size_t namesCount = 0;
|
|
|
|
size_t memoriesStart = namesCount;
|
|
size_t memoriesCount = instance->instance().codeMeta().memories.length();
|
|
namesCount += memoriesCount;
|
|
|
|
size_t globalsStart = namesCount;
|
|
size_t globalsCount = instance->instance().codeMeta().globals.length();
|
|
namesCount += globalsCount;
|
|
|
|
Rooted<UniquePtr<RuntimeData>> data(
|
|
cx, NewEmptyScopeData<WasmInstanceScope, JSAtom>(cx, namesCount));
|
|
if (!data) {
|
|
return nullptr;
|
|
}
|
|
|
|
Rooted<WasmInstanceObject*> rootedInstance(cx, instance);
|
|
for (size_t i = 0; i < memoriesCount; i++) {
|
|
JSAtom* wasmName = GenerateWasmName(cx, "memory", i);
|
|
if (!wasmName) {
|
|
return nullptr;
|
|
}
|
|
|
|
InitializeNextTrailingName(data, wasmName);
|
|
}
|
|
|
|
for (size_t i = 0; i < globalsCount; i++) {
|
|
JSAtom* wasmName = GenerateWasmName(cx, "global", i);
|
|
if (!wasmName) {
|
|
return nullptr;
|
|
}
|
|
|
|
InitializeNextTrailingName(data, wasmName);
|
|
}
|
|
|
|
MOZ_ASSERT(data->length == namesCount);
|
|
|
|
data->instance.init(rootedInstance);
|
|
data->slotInfo.memoriesStart = memoriesStart;
|
|
data->slotInfo.globalsStart = globalsStart;
|
|
|
|
Rooted<Scope*> enclosing(cx, &cx->global()->emptyGlobalScope());
|
|
return Scope::create<WasmInstanceScope>(cx, ScopeKind::WasmInstance,
|
|
enclosing,
|
|
/* envShape = */ nullptr, &data);
|
|
}
|
|
|
|
/* static */
|
|
WasmFunctionScope* WasmFunctionScope::create(JSContext* cx,
|
|
Handle<Scope*> enclosing,
|
|
uint32_t funcIndex) {
|
|
MOZ_ASSERT(enclosing->is<WasmInstanceScope>());
|
|
|
|
Rooted<WasmFunctionScope*> wasmFunctionScope(cx);
|
|
|
|
Rooted<WasmInstanceObject*> instance(
|
|
cx, enclosing->as<WasmInstanceScope>().instance());
|
|
|
|
// TODO pull the local variable names from the wasm function definition.
|
|
wasm::ValTypeVector locals;
|
|
size_t argsLength;
|
|
wasm::StackResults unusedStackResults;
|
|
if (!instance->instance().debug().debugGetLocalTypes(
|
|
funcIndex, &locals, &argsLength, &unusedStackResults)) {
|
|
return nullptr;
|
|
}
|
|
uint32_t namesCount = locals.length();
|
|
|
|
Rooted<UniquePtr<RuntimeData>> data(
|
|
cx, NewEmptyScopeData<WasmFunctionScope, JSAtom>(cx, namesCount));
|
|
if (!data) {
|
|
return nullptr;
|
|
}
|
|
|
|
for (size_t i = 0; i < namesCount; i++) {
|
|
JSAtom* wasmName = GenerateWasmName(cx, "var", i);
|
|
if (!wasmName) {
|
|
return nullptr;
|
|
}
|
|
|
|
InitializeNextTrailingName(data, wasmName);
|
|
}
|
|
MOZ_ASSERT(data->length == namesCount);
|
|
|
|
return Scope::create<WasmFunctionScope>(cx, ScopeKind::WasmFunction,
|
|
enclosing,
|
|
/* envShape = */ nullptr, &data);
|
|
}
|
|
|
|
ScopeIter::ScopeIter(JSScript* script) : scope_(script->bodyScope()) {}
|
|
|
|
bool ScopeIter::hasSyntacticEnvironment() const {
|
|
return scope()->hasEnvironment() &&
|
|
scope()->kind() != ScopeKind::NonSyntactic;
|
|
}
|
|
|
|
AbstractBindingIter<JSAtom>::AbstractBindingIter(ScopeKind kind,
|
|
BaseScopeData* data,
|
|
uint32_t firstFrameSlot)
|
|
: BaseAbstractBindingIter<JSAtom>() {
|
|
switch (kind) {
|
|
case ScopeKind::Lexical:
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
case ScopeKind::FunctionLexical:
|
|
init(*static_cast<LexicalScope::RuntimeData*>(data), firstFrameSlot, 0);
|
|
break;
|
|
case ScopeKind::NamedLambda:
|
|
case ScopeKind::StrictNamedLambda:
|
|
init(*static_cast<LexicalScope::RuntimeData*>(data), LOCALNO_LIMIT,
|
|
IsNamedLambda);
|
|
break;
|
|
case ScopeKind::ClassBody:
|
|
init(*static_cast<ClassBodyScope::RuntimeData*>(data), firstFrameSlot);
|
|
break;
|
|
case ScopeKind::With:
|
|
// With scopes do not have bindings.
|
|
index_ = length_ = 0;
|
|
MOZ_ASSERT(done());
|
|
break;
|
|
case ScopeKind::Function: {
|
|
uint8_t flags = IgnoreDestructuredFormalParameters;
|
|
if (static_cast<FunctionScope::RuntimeData*>(data)
|
|
->slotInfo.hasParameterExprs()) {
|
|
flags |= HasFormalParameterExprs;
|
|
}
|
|
init(*static_cast<FunctionScope::RuntimeData*>(data), flags);
|
|
break;
|
|
}
|
|
case ScopeKind::FunctionBodyVar:
|
|
init(*static_cast<VarScope::RuntimeData*>(data), firstFrameSlot);
|
|
break;
|
|
case ScopeKind::Eval:
|
|
case ScopeKind::StrictEval:
|
|
init(*static_cast<EvalScope::RuntimeData*>(data),
|
|
kind == ScopeKind::StrictEval);
|
|
break;
|
|
case ScopeKind::Global:
|
|
case ScopeKind::NonSyntactic:
|
|
init(*static_cast<GlobalScope::RuntimeData*>(data));
|
|
break;
|
|
case ScopeKind::Module:
|
|
init(*static_cast<ModuleScope::RuntimeData*>(data));
|
|
break;
|
|
case ScopeKind::WasmInstance:
|
|
init(*static_cast<WasmInstanceScope::RuntimeData*>(data));
|
|
break;
|
|
case ScopeKind::WasmFunction:
|
|
init(*static_cast<WasmFunctionScope::RuntimeData*>(data));
|
|
break;
|
|
}
|
|
}
|
|
|
|
AbstractBindingIter<JSAtom>::AbstractBindingIter(Scope* scope)
|
|
: AbstractBindingIter<JSAtom>(scope->kind(), scope->rawData(),
|
|
scope->firstFrameSlot()) {}
|
|
|
|
AbstractBindingIter<JSAtom>::AbstractBindingIter(JSScript* script)
|
|
: AbstractBindingIter<JSAtom>(script->bodyScope()) {}
|
|
|
|
AbstractBindingIter<frontend::TaggedParserAtomIndex>::AbstractBindingIter(
|
|
const frontend::ScopeStencilRef& ref)
|
|
: Base() {
|
|
const ScopeStencil& scope = ref.scope();
|
|
BaseParserScopeData* data = ref.context_.scopeNames[ref.scopeIndex_];
|
|
switch (scope.kind()) {
|
|
case ScopeKind::Lexical:
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
case ScopeKind::FunctionLexical:
|
|
init(*static_cast<LexicalScope::ParserData*>(data),
|
|
scope.firstFrameSlot(), 0);
|
|
break;
|
|
case ScopeKind::NamedLambda:
|
|
case ScopeKind::StrictNamedLambda:
|
|
init(*static_cast<LexicalScope::ParserData*>(data), LOCALNO_LIMIT,
|
|
IsNamedLambda);
|
|
break;
|
|
case ScopeKind::ClassBody:
|
|
init(*static_cast<ClassBodyScope::ParserData*>(data),
|
|
scope.firstFrameSlot());
|
|
break;
|
|
case ScopeKind::With:
|
|
// With scopes do not have bindings.
|
|
index_ = length_ = 0;
|
|
MOZ_ASSERT(done());
|
|
break;
|
|
case ScopeKind::Function: {
|
|
uint8_t flags = IgnoreDestructuredFormalParameters;
|
|
if (static_cast<FunctionScope::ParserData*>(data)
|
|
->slotInfo.hasParameterExprs()) {
|
|
flags |= HasFormalParameterExprs;
|
|
}
|
|
init(*static_cast<FunctionScope::ParserData*>(data), flags);
|
|
break;
|
|
}
|
|
case ScopeKind::FunctionBodyVar:
|
|
init(*static_cast<VarScope::ParserData*>(data), scope.firstFrameSlot());
|
|
break;
|
|
case ScopeKind::Eval:
|
|
case ScopeKind::StrictEval:
|
|
init(*static_cast<EvalScope::ParserData*>(data),
|
|
scope.kind() == ScopeKind::StrictEval);
|
|
break;
|
|
case ScopeKind::Global:
|
|
case ScopeKind::NonSyntactic:
|
|
init(*static_cast<GlobalScope::ParserData*>(data));
|
|
break;
|
|
case ScopeKind::Module:
|
|
init(*static_cast<ModuleScope::ParserData*>(data));
|
|
break;
|
|
case ScopeKind::WasmInstance:
|
|
init(*static_cast<WasmInstanceScope::ParserData*>(data));
|
|
break;
|
|
case ScopeKind::WasmFunction:
|
|
init(*static_cast<WasmFunctionScope::ParserData*>(data));
|
|
break;
|
|
}
|
|
}
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(
|
|
LexicalScope::AbstractData<NameT>& data, uint32_t firstFrameSlot,
|
|
uint8_t flags) {
|
|
auto& slotInfo = data.slotInfo;
|
|
|
|
// Named lambda scopes can only have environment slots. If the callee
|
|
// isn't closed over, it is accessed via JSOp::Callee.
|
|
if (flags & IsNamedLambda) {
|
|
// Named lambda binding is weird. Normal BindingKind ordering rules
|
|
// don't apply.
|
|
init(/* positionalFormalStart= */ 0,
|
|
/* nonPositionalFormalStart= */ 0,
|
|
/* varStart= */ 0,
|
|
/* letStart= */ 0,
|
|
/* constStart= */ 0,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ data.length,
|
|
#endif
|
|
/* syntheticStart= */ data.length,
|
|
/* privageMethodStart= */ data.length,
|
|
/* flags= */ CanHaveEnvironmentSlots | flags,
|
|
/* firstFrameSlot= */ firstFrameSlot,
|
|
/* firstEnvironmentSlot= */
|
|
JSSLOT_FREE(&LexicalEnvironmentObject::class_),
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
} else {
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// vars - [0, 0)
|
|
// lets - [0, slotInfo.constStart)
|
|
// consts - [slotInfo.constStart, data.length)
|
|
// synthetic - [data.length, data.length)
|
|
// private methods - [data.length, data.length)
|
|
//
|
|
// If ENABLE_EXPLICIT_RESOURCE_MANAGEMENT is set, the consts range is split
|
|
// into the following:
|
|
// consts - [slotInfo.constStart, slotInfo.usingStart)
|
|
// usings - [slotInfo.usingStart, data.length)
|
|
init(/* positionalFormalStart= */ 0,
|
|
/* nonPositionalFormalStart= */ 0,
|
|
/* varStart= */ 0,
|
|
/* letStart= */ 0,
|
|
/* constStart= */ slotInfo.constStart,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ slotInfo.usingStart,
|
|
#endif
|
|
/* syntheticStart= */ data.length,
|
|
/* privateMethodStart= */ data.length,
|
|
/* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots | flags,
|
|
/* firstFrameSlot= */ firstFrameSlot,
|
|
/* firstEnvironmentSlot= */
|
|
JSSLOT_FREE(&LexicalEnvironmentObject::class_),
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
}
|
|
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
LexicalScope::AbstractData<JSAtom>&, uint32_t, uint8_t);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
LexicalScope::AbstractData<frontend::TaggedParserAtomIndex>&, uint32_t,
|
|
uint8_t);
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(
|
|
ClassBodyScope::AbstractData<NameT>& data, uint32_t firstFrameSlot) {
|
|
auto& slotInfo = data.slotInfo;
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// vars - [0, 0)
|
|
// lets - [0, 0)
|
|
// consts - [0, 0)
|
|
// synthetic - [0, slotInfo.privateMethodStart)
|
|
// private methods - [slotInfo.privateMethodStart, data.length)
|
|
init(/* positionalFormalStart= */ 0,
|
|
/* nonPositionalFormalStart= */ 0,
|
|
/* varStart= */ 0,
|
|
/* letStart= */ 0,
|
|
/* constStart= */ 0,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ 0,
|
|
#endif
|
|
/* syntheticStart= */ 0,
|
|
/* privateMethodStart= */ slotInfo.privateMethodStart,
|
|
/* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots,
|
|
/* firstFrameSlot= */ firstFrameSlot,
|
|
/* firstEnvironmentSlot= */
|
|
JSSLOT_FREE(&ClassBodyLexicalEnvironmentObject::class_),
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
ClassBodyScope::AbstractData<JSAtom>&, uint32_t);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
ClassBodyScope::AbstractData<frontend::TaggedParserAtomIndex>&, uint32_t);
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(
|
|
FunctionScope::AbstractData<NameT>& data, uint8_t flags) {
|
|
flags = CanHaveFrameSlots | CanHaveEnvironmentSlots | flags;
|
|
if (!(flags & HasFormalParameterExprs)) {
|
|
flags |= CanHaveArgumentSlots;
|
|
}
|
|
|
|
auto length = data.length;
|
|
auto& slotInfo = data.slotInfo;
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, slotInfo.nonPositionalFormalStart)
|
|
// other formals - [slotInfo.nonPositionalParamStart, slotInfo.varStart)
|
|
// vars - [slotInfo.varStart, length)
|
|
// lets - [length, length)
|
|
// consts - [length, length)
|
|
// synthetic - [length, length)
|
|
// private methods - [length, length)
|
|
init(/* positionalFormalStart= */ 0,
|
|
/* nonPositionalFormalStart= */ slotInfo.nonPositionalFormalStart,
|
|
/* varStart= */ slotInfo.varStart,
|
|
/* letStart= */ length,
|
|
/* constStart= */ length,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ length,
|
|
#endif
|
|
/* syntheticStart= */ length,
|
|
/* privateMethodStart= */ length,
|
|
/* flags= */ flags,
|
|
/* firstFrameSlot= */ 0,
|
|
/* firstEnvironmentSlot= */ JSSLOT_FREE(&CallObject::class_),
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
FunctionScope::AbstractData<JSAtom>&, uint8_t);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
FunctionScope::AbstractData<frontend::TaggedParserAtomIndex>&, uint8_t);
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(VarScope::AbstractData<NameT>& data,
|
|
uint32_t firstFrameSlot) {
|
|
auto length = data.length;
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// vars - [0, length)
|
|
// lets - [length, length)
|
|
// consts - [length, length)
|
|
// synthetic - [length, length)
|
|
// private methods - [length, length)
|
|
init(/* positionalFormalStart= */ 0,
|
|
/* nonPositionalFormalStart= */ 0,
|
|
/* varStart= */ 0,
|
|
/* letStart= */ length,
|
|
/* constStart= */ length,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ length,
|
|
#endif
|
|
/* syntheticStart= */ length,
|
|
/* privateMethodStart= */ length,
|
|
/* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots,
|
|
/* firstFrameSlot= */ firstFrameSlot,
|
|
/* firstEnvironmentSlot= */ JSSLOT_FREE(&VarEnvironmentObject::class_),
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
VarScope::AbstractData<JSAtom>&, uint32_t);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
VarScope::AbstractData<frontend::TaggedParserAtomIndex>&, uint32_t);
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(
|
|
GlobalScope::AbstractData<NameT>& data) {
|
|
auto& slotInfo = data.slotInfo;
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// vars - [0, slotInfo.letStart)
|
|
// lets - [slotInfo.letStart, slotInfo.constStart)
|
|
// consts - [slotInfo.constStart, data.length)
|
|
// synthetic - [data.length, data.length)
|
|
// private methods - [data.length, data.length)
|
|
init(/* positionalFormalStart= */ 0,
|
|
/* nonPositionalFormalStart= */ 0,
|
|
/* varStart= */ 0,
|
|
/* letStart= */ slotInfo.letStart,
|
|
/* constStart= */ slotInfo.constStart,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ data.length,
|
|
#endif
|
|
/* syntheticStart= */ data.length,
|
|
/* privateMethoodStart= */ data.length,
|
|
/* flags= */ CannotHaveSlots,
|
|
/* firstFrameSlot= */ UINT32_MAX,
|
|
/* firstEnvironmentSlot= */ UINT32_MAX,
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
GlobalScope::AbstractData<JSAtom>&);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
GlobalScope::AbstractData<frontend::TaggedParserAtomIndex>&);
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(EvalScope::AbstractData<NameT>& data,
|
|
bool strict) {
|
|
uint32_t flags;
|
|
uint32_t firstFrameSlot;
|
|
uint32_t firstEnvironmentSlot;
|
|
if (strict) {
|
|
flags = CanHaveFrameSlots | CanHaveEnvironmentSlots;
|
|
firstFrameSlot = 0;
|
|
firstEnvironmentSlot = JSSLOT_FREE(&VarEnvironmentObject::class_);
|
|
} else {
|
|
flags = CannotHaveSlots;
|
|
firstFrameSlot = UINT32_MAX;
|
|
firstEnvironmentSlot = UINT32_MAX;
|
|
}
|
|
|
|
auto length = data.length;
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// vars - [0, length)
|
|
// lets - [length, length)
|
|
// consts - [length, length)
|
|
// synthetic - [length, length)
|
|
// private methods - [length, length)
|
|
init(/* positionalFormalStart= */ 0,
|
|
/* nonPositionalFormalStart= */ 0,
|
|
/* varStart= */ 0,
|
|
/* letStart= */ length,
|
|
/* constStart= */ length,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ length,
|
|
#endif
|
|
/* syntheticStart= */ length,
|
|
/* privateMethodStart= */ length,
|
|
/* flags= */ flags,
|
|
/* firstFrameSlot= */ firstFrameSlot,
|
|
/* firstEnvironmentSlot= */ firstEnvironmentSlot,
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
EvalScope::AbstractData<JSAtom>&, bool);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
EvalScope::AbstractData<frontend::TaggedParserAtomIndex>&, bool);
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(
|
|
ModuleScope::AbstractData<NameT>& data) {
|
|
auto& slotInfo = data.slotInfo;
|
|
|
|
// imports - [0, slotInfo.varStart)
|
|
// positional formals - [slotInfo.varStart, slotInfo.varStart)
|
|
// other formals - [slotInfo.varStart, slotInfo.varStart)
|
|
// vars - [slotInfo.varStart, slotInfo.letStart)
|
|
// lets - [slotInfo.letStart, slotInfo.constStart)
|
|
// consts - [slotInfo.constStart, data.length)
|
|
// synthetic - [data.length, data.length)
|
|
// private methods - [data.length, data.length)
|
|
//
|
|
// If ENABLE_EXPLICIT_RESOURCE_MANAGEMENT is set, the consts range is split
|
|
// into the following:
|
|
// consts - [slotInfo.constStart, slotInfo.usingStart)
|
|
// usings - [slotInfo.usingStart, data.length)
|
|
init(
|
|
/* positionalFormalStart= */ slotInfo.varStart,
|
|
/* nonPositionalFormalStart= */ slotInfo.varStart,
|
|
/* varStart= */ slotInfo.varStart,
|
|
/* letStart= */ slotInfo.letStart,
|
|
/* constStart= */ slotInfo.constStart,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ slotInfo.usingStart,
|
|
#endif
|
|
/* syntheticStart= */ data.length,
|
|
/* privateMethodStart= */ data.length,
|
|
/* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots,
|
|
/* firstFrameSlot= */ 0,
|
|
/* firstEnvironmentSlot= */ JSSLOT_FREE(&ModuleEnvironmentObject::class_),
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
ModuleScope::AbstractData<JSAtom>&);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
ModuleScope::AbstractData<frontend::TaggedParserAtomIndex>&);
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(
|
|
WasmInstanceScope::AbstractData<NameT>& data) {
|
|
auto length = data.length;
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// vars - [0, length)
|
|
// lets - [length, length)
|
|
// consts - [length, length)
|
|
// synthetic - [length, length)
|
|
// private methods - [length, length)
|
|
init(/* positionalFormalStart= */ 0,
|
|
/* nonPositionalFormalStart= */ 0,
|
|
/* varStart= */ 0,
|
|
/* letStart= */ length,
|
|
/* constStart= */ length,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ length,
|
|
#endif
|
|
/* syntheticStart= */ length,
|
|
/* privateMethodStart= */ length,
|
|
/* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots,
|
|
/* firstFrameSlot= */ UINT32_MAX,
|
|
/* firstEnvironmentSlot= */ UINT32_MAX,
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
WasmInstanceScope::AbstractData<JSAtom>&);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
WasmInstanceScope::AbstractData<frontend::TaggedParserAtomIndex>&);
|
|
|
|
template <typename NameT>
|
|
void BaseAbstractBindingIter<NameT>::init(
|
|
WasmFunctionScope::AbstractData<NameT>& data) {
|
|
auto length = data.length;
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// vars - [0, length)
|
|
// lets - [length, length)
|
|
// consts - [length, length)
|
|
// synthetic - [length, length)
|
|
// private methods - [length, length)
|
|
init(/* positionalFormalStart = */ 0,
|
|
/* nonPositionalFormalStart = */ 0,
|
|
/* varStart= */ 0,
|
|
/* letStart= */ length,
|
|
/* constStart= */ length,
|
|
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
|
|
/* usingStart= */ length,
|
|
#endif
|
|
/* syntheticStart= */ length,
|
|
/* privateMethodStart= */ length,
|
|
/* flags= */ CanHaveFrameSlots | CanHaveEnvironmentSlots,
|
|
/* firstFrameSlot= */ UINT32_MAX,
|
|
/* firstEnvironmentSlot= */ UINT32_MAX,
|
|
/* names= */ GetScopeDataTrailingNames(&data));
|
|
}
|
|
template void BaseAbstractBindingIter<JSAtom>::init(
|
|
WasmFunctionScope::AbstractData<JSAtom>&);
|
|
template void BaseAbstractBindingIter<frontend::TaggedParserAtomIndex>::init(
|
|
WasmFunctionScope::AbstractData<frontend::TaggedParserAtomIndex>&);
|
|
|
|
AbstractPositionalFormalParameterIter<
|
|
JSAtom>::AbstractPositionalFormalParameterIter(Scope* scope)
|
|
: Base(scope) {
|
|
// Reinit with flags = 0, i.e., iterate over all positional parameters.
|
|
if (scope->is<FunctionScope>()) {
|
|
init(scope->as<FunctionScope>().data(), /* flags = */ 0);
|
|
}
|
|
settle();
|
|
}
|
|
|
|
AbstractPositionalFormalParameterIter<
|
|
JSAtom>::AbstractPositionalFormalParameterIter(JSScript* script)
|
|
: AbstractPositionalFormalParameterIter(script->bodyScope()) {}
|
|
|
|
void js::DumpBindings(JSContext* cx, Scope* scopeArg) {
|
|
Rooted<Scope*> scope(cx, scopeArg);
|
|
for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
|
|
UniqueChars bytes = AtomToPrintableString(cx, bi.name());
|
|
if (!bytes) {
|
|
MaybePrintAndClearPendingException(cx);
|
|
return;
|
|
}
|
|
fprintf(stderr, " %s %s ", BindingKindString(bi.kind()), bytes.get());
|
|
switch (bi.location().kind()) {
|
|
case BindingLocation::Kind::Global:
|
|
if (bi.isTopLevelFunction()) {
|
|
fprintf(stderr, "global function\n");
|
|
} else {
|
|
fprintf(stderr, "global\n");
|
|
}
|
|
break;
|
|
case BindingLocation::Kind::Argument:
|
|
fprintf(stderr, "arg slot %u\n", bi.location().argumentSlot());
|
|
break;
|
|
case BindingLocation::Kind::Frame:
|
|
fprintf(stderr, "frame slot %u\n", bi.location().slot());
|
|
break;
|
|
case BindingLocation::Kind::Environment:
|
|
fprintf(stderr, "env slot %u\n", bi.location().slot());
|
|
break;
|
|
case BindingLocation::Kind::NamedLambdaCallee:
|
|
fprintf(stderr, "named lambda callee\n");
|
|
break;
|
|
case BindingLocation::Kind::Import:
|
|
fprintf(stderr, "import\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static JSAtom* GetFrameSlotNameInScope(Scope* scope, uint32_t slot) {
|
|
for (BindingIter bi(scope); bi; bi++) {
|
|
BindingLocation loc = bi.location();
|
|
if (loc.kind() == BindingLocation::Kind::Frame && loc.slot() == slot) {
|
|
return bi.name();
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
JSAtom* js::FrameSlotName(JSScript* script, jsbytecode* pc) {
|
|
MOZ_ASSERT(IsLocalOp(JSOp(*pc)));
|
|
uint32_t slot = GET_LOCALNO(pc);
|
|
MOZ_ASSERT(slot < script->nfixed());
|
|
|
|
// Look for it in the body scope first.
|
|
if (JSAtom* name = GetFrameSlotNameInScope(script->bodyScope(), slot)) {
|
|
return name;
|
|
}
|
|
|
|
// If this is a function script and there is an extra var scope, look for
|
|
// it there.
|
|
if (script->functionHasExtraBodyVarScope()) {
|
|
if (JSAtom* name = GetFrameSlotNameInScope(
|
|
script->functionExtraBodyVarScope(), slot)) {
|
|
return name;
|
|
}
|
|
}
|
|
// If not found, look for it in a lexical scope.
|
|
for (ScopeIter si(script->innermostScope(pc)); si; si++) {
|
|
if (!si.scope()->is<LexicalScope>() && !si.scope()->is<ClassBodyScope>()) {
|
|
continue;
|
|
}
|
|
|
|
// Is the slot within bounds of the current lexical scope?
|
|
if (slot < si.scope()->firstFrameSlot()) {
|
|
continue;
|
|
}
|
|
if (slot >= LexicalScope::nextFrameSlot(si.scope())) {
|
|
break;
|
|
}
|
|
|
|
// If so, get the name.
|
|
if (JSAtom* name = GetFrameSlotNameInScope(si.scope(), slot)) {
|
|
return name;
|
|
}
|
|
}
|
|
|
|
MOZ_CRASH("Frame slot not found");
|
|
}
|
|
|
|
JS::ubi::Node::Size JS::ubi::Concrete<Scope>::size(
|
|
mozilla::MallocSizeOf mallocSizeOf) const {
|
|
return js::gc::Arena::thingSize(get().asTenured().getAllocKind()) +
|
|
get().sizeOfExcludingThis(mallocSizeOf);
|
|
}
|
|
|
|
template <typename... Args>
|
|
/* static */ bool ScopeStencil::appendScopeStencilAndData(
|
|
FrontendContext* fc, CompilationState& compilationState,
|
|
BaseParserScopeData* data, ScopeIndex* indexOut, Args&&... args) {
|
|
*indexOut = ScopeIndex(compilationState.scopeData.length());
|
|
if (uint32_t(*indexOut) >= TaggedScriptThingIndex::IndexLimit) {
|
|
ReportAllocationOverflow(fc);
|
|
return false;
|
|
}
|
|
|
|
if (!compilationState.scopeData.emplaceBack(std::forward<Args>(args)...)) {
|
|
js::ReportOutOfMemory(fc);
|
|
return false;
|
|
}
|
|
if (!compilationState.scopeNames.append(data)) {
|
|
compilationState.scopeData.popBack();
|
|
MOZ_ASSERT(compilationState.scopeData.length() ==
|
|
compilationState.scopeNames.length());
|
|
|
|
js::ReportOutOfMemory(fc);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
bool ScopeStencil::createForFunctionScope(
|
|
FrontendContext* fc, frontend::CompilationState& compilationState,
|
|
FunctionScope::ParserData* data, bool hasParameterExprs,
|
|
bool needsEnvironment, ScriptIndex functionIndex, bool isArrow,
|
|
mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) {
|
|
auto kind = ScopeKind::Function;
|
|
using ScopeType = FunctionScope;
|
|
MOZ_ASSERT(matchScopeKind<ScopeType>(kind));
|
|
|
|
if (data) {
|
|
MarkParserScopeData<ScopeType>(data, compilationState);
|
|
} else {
|
|
data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint32_t firstFrameSlot = 0;
|
|
mozilla::Maybe<uint32_t> envShape;
|
|
FunctionScope::prepareForScopeCreation(data, hasParameterExprs,
|
|
needsEnvironment, &envShape);
|
|
|
|
return appendScopeStencilAndData(fc, compilationState, data, index, kind,
|
|
enclosing, firstFrameSlot, envShape,
|
|
mozilla::Some(functionIndex), isArrow);
|
|
}
|
|
|
|
/* static */
|
|
bool ScopeStencil::createForLexicalScope(
|
|
FrontendContext* fc, frontend::CompilationState& compilationState,
|
|
ScopeKind kind, LexicalScope::ParserData* data, uint32_t firstFrameSlot,
|
|
mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) {
|
|
using ScopeType = LexicalScope;
|
|
MOZ_ASSERT(matchScopeKind<ScopeType>(kind));
|
|
|
|
if (data) {
|
|
MarkParserScopeData<ScopeType>(data, compilationState);
|
|
} else {
|
|
data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mozilla::Maybe<uint32_t> envShape;
|
|
ScopeType::prepareForScopeCreation(kind, firstFrameSlot, data, &envShape);
|
|
|
|
return appendScopeStencilAndData(fc, compilationState, data, index, kind,
|
|
enclosing, firstFrameSlot, envShape);
|
|
}
|
|
|
|
/* static */
|
|
bool ScopeStencil::createForClassBodyScope(
|
|
FrontendContext* fc, frontend::CompilationState& compilationState,
|
|
ScopeKind kind, ClassBodyScope::ParserData* data, uint32_t firstFrameSlot,
|
|
mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) {
|
|
using ScopeType = ClassBodyScope;
|
|
MOZ_ASSERT(matchScopeKind<ScopeType>(kind));
|
|
|
|
if (data) {
|
|
MarkParserScopeData<ScopeType>(data, compilationState);
|
|
} else {
|
|
data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mozilla::Maybe<uint32_t> envShape;
|
|
ScopeType::prepareForScopeCreation(kind, firstFrameSlot, data, &envShape);
|
|
|
|
return appendScopeStencilAndData(fc, compilationState, data, index, kind,
|
|
enclosing, firstFrameSlot, envShape);
|
|
}
|
|
|
|
bool ScopeStencil::createForVarScope(
|
|
FrontendContext* fc, frontend::CompilationState& compilationState,
|
|
ScopeKind kind, VarScope::ParserData* data, uint32_t firstFrameSlot,
|
|
bool needsEnvironment, mozilla::Maybe<ScopeIndex> enclosing,
|
|
ScopeIndex* index) {
|
|
using ScopeType = VarScope;
|
|
MOZ_ASSERT(matchScopeKind<ScopeType>(kind));
|
|
|
|
if (data) {
|
|
MarkParserScopeData<ScopeType>(data, compilationState);
|
|
} else {
|
|
data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mozilla::Maybe<uint32_t> envShape;
|
|
VarScope::prepareForScopeCreation(kind, data, firstFrameSlot,
|
|
needsEnvironment, &envShape);
|
|
|
|
return appendScopeStencilAndData(fc, compilationState, data, index, kind,
|
|
enclosing, firstFrameSlot, envShape);
|
|
}
|
|
|
|
/* static */
|
|
bool ScopeStencil::createForGlobalScope(
|
|
FrontendContext* fc, frontend::CompilationState& compilationState,
|
|
ScopeKind kind, GlobalScope::ParserData* data, ScopeIndex* index) {
|
|
using ScopeType = GlobalScope;
|
|
MOZ_ASSERT(matchScopeKind<ScopeType>(kind));
|
|
|
|
if (data) {
|
|
MarkParserScopeData<ScopeType>(data, compilationState);
|
|
} else {
|
|
data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// The global scope has no environment shape. Its environment is the
|
|
// global lexical scope and the global object or non-syntactic objects
|
|
// created by embedding, all of which are not only extensible but may
|
|
// have names on them deleted.
|
|
uint32_t firstFrameSlot = 0;
|
|
mozilla::Maybe<uint32_t> envShape;
|
|
|
|
mozilla::Maybe<ScopeIndex> enclosing;
|
|
|
|
return appendScopeStencilAndData(fc, compilationState, data, index, kind,
|
|
enclosing, firstFrameSlot, envShape);
|
|
}
|
|
|
|
/* static */
|
|
bool ScopeStencil::createForEvalScope(
|
|
FrontendContext* fc, frontend::CompilationState& compilationState,
|
|
ScopeKind kind, EvalScope::ParserData* data,
|
|
mozilla::Maybe<ScopeIndex> enclosing, ScopeIndex* index) {
|
|
using ScopeType = EvalScope;
|
|
MOZ_ASSERT(matchScopeKind<ScopeType>(kind));
|
|
|
|
if (data) {
|
|
MarkParserScopeData<ScopeType>(data, compilationState);
|
|
} else {
|
|
data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
uint32_t firstFrameSlot = 0;
|
|
mozilla::Maybe<uint32_t> envShape;
|
|
EvalScope::prepareForScopeCreation(kind, data, &envShape);
|
|
|
|
return appendScopeStencilAndData(fc, compilationState, data, index, kind,
|
|
enclosing, firstFrameSlot, envShape);
|
|
}
|
|
|
|
/* static */
|
|
bool ScopeStencil::createForModuleScope(
|
|
FrontendContext* fc, frontend::CompilationState& compilationState,
|
|
ModuleScope::ParserData* data, mozilla::Maybe<ScopeIndex> enclosing,
|
|
ScopeIndex* index) {
|
|
auto kind = ScopeKind::Module;
|
|
using ScopeType = ModuleScope;
|
|
MOZ_ASSERT(matchScopeKind<ScopeType>(kind));
|
|
|
|
if (data) {
|
|
MarkParserScopeData<ScopeType>(data, compilationState);
|
|
} else {
|
|
data = NewEmptyParserScopeData<ScopeType>(fc, compilationState.alloc);
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
MOZ_ASSERT(enclosing.isNothing());
|
|
|
|
// The data that's passed in is from the frontend and is LifoAlloc'd.
|
|
// Copy it now that we're creating a permanent VM scope.
|
|
uint32_t firstFrameSlot = 0;
|
|
mozilla::Maybe<uint32_t> envShape;
|
|
ModuleScope::prepareForScopeCreation(data, &envShape);
|
|
|
|
return appendScopeStencilAndData(fc, compilationState, data, index, kind,
|
|
enclosing, firstFrameSlot, envShape);
|
|
}
|
|
|
|
template <typename SpecificEnvironmentT>
|
|
bool ScopeStencil::createSpecificShape(
|
|
JSContext* cx, ScopeKind kind, BaseScopeData* scopeData,
|
|
MutableHandle<SharedShape*> shape) const {
|
|
const JSClass* cls = &SpecificEnvironmentT::class_;
|
|
constexpr ObjectFlags objectFlags = SpecificEnvironmentT::OBJECT_FLAGS;
|
|
|
|
if (hasEnvironmentShape()) {
|
|
if (numEnvironmentSlots() > 0) {
|
|
BindingIter bi(kind, scopeData, firstFrameSlot_);
|
|
shape.set(CreateEnvironmentShape(cx, bi, cls, numEnvironmentSlots(),
|
|
objectFlags));
|
|
return shape;
|
|
}
|
|
|
|
shape.set(EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), objectFlags));
|
|
return shape;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
bool ScopeStencil::createForWithScope(FrontendContext* fc,
|
|
CompilationState& compilationState,
|
|
mozilla::Maybe<ScopeIndex> enclosing,
|
|
ScopeIndex* index) {
|
|
auto kind = ScopeKind::With;
|
|
MOZ_ASSERT(matchScopeKind<WithScope>(kind));
|
|
|
|
uint32_t firstFrameSlot = 0;
|
|
mozilla::Maybe<uint32_t> envShape;
|
|
|
|
return appendScopeStencilAndData(fc, compilationState, nullptr, index, kind,
|
|
enclosing, firstFrameSlot, envShape);
|
|
}
|
|
|
|
template <typename SpecificScopeT>
|
|
UniquePtr<typename SpecificScopeT::RuntimeData>
|
|
ScopeStencil::createSpecificScopeData(JSContext* cx,
|
|
CompilationAtomCache& atomCache,
|
|
BaseParserScopeData* baseData) const {
|
|
return LiftParserScopeData<SpecificScopeT>(cx, atomCache, baseData);
|
|
}
|
|
|
|
template <>
|
|
UniquePtr<FunctionScope::RuntimeData>
|
|
ScopeStencil::createSpecificScopeData<FunctionScope>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
BaseParserScopeData* baseData) const {
|
|
// Allocate a new vm function-scope.
|
|
UniquePtr<FunctionScope::RuntimeData> data =
|
|
LiftParserScopeData<FunctionScope>(cx, atomCache, baseData);
|
|
if (!data) {
|
|
return nullptr;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
template <>
|
|
UniquePtr<ModuleScope::RuntimeData>
|
|
ScopeStencil::createSpecificScopeData<ModuleScope>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
BaseParserScopeData* baseData) const {
|
|
// Allocate a new vm module-scope.
|
|
UniquePtr<ModuleScope::RuntimeData> data =
|
|
LiftParserScopeData<ModuleScope>(cx, atomCache, baseData);
|
|
if (!data) {
|
|
return nullptr;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// WithScope does not use binding data.
|
|
template <>
|
|
Scope* ScopeStencil::createSpecificScope<WithScope, std::nullptr_t>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const {
|
|
return Scope::create(cx, ScopeKind::With, enclosingScope, nullptr);
|
|
}
|
|
|
|
// GlobalScope has bindings but no environment shape.
|
|
template <>
|
|
Scope* ScopeStencil::createSpecificScope<GlobalScope, std::nullptr_t>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const {
|
|
Rooted<UniquePtr<GlobalScope::RuntimeData>> rootedData(
|
|
cx, createSpecificScopeData<GlobalScope>(cx, atomCache, baseData));
|
|
if (!rootedData) {
|
|
return nullptr;
|
|
}
|
|
|
|
MOZ_ASSERT(!hasEnclosing());
|
|
MOZ_ASSERT(!enclosingScope);
|
|
|
|
// Because we already baked the data here, we needn't do it again.
|
|
return Scope::create<GlobalScope>(cx, kind(), nullptr, nullptr, &rootedData);
|
|
}
|
|
|
|
template <typename SpecificScopeT, typename SpecificEnvironmentT>
|
|
Scope* ScopeStencil::createSpecificScope(JSContext* cx,
|
|
CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope,
|
|
BaseParserScopeData* baseData) const {
|
|
Rooted<UniquePtr<typename SpecificScopeT::RuntimeData>> rootedData(
|
|
cx, createSpecificScopeData<SpecificScopeT>(cx, atomCache, baseData));
|
|
if (!rootedData) {
|
|
return nullptr;
|
|
}
|
|
|
|
Rooted<SharedShape*> shape(cx);
|
|
if (!createSpecificShape<SpecificEnvironmentT>(
|
|
cx, kind(), rootedData.get().get(), &shape)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Because we already baked the data here, we needn't do it again.
|
|
return Scope::create<SpecificScopeT>(cx, kind(), enclosingScope, shape,
|
|
&rootedData);
|
|
}
|
|
|
|
template Scope* ScopeStencil::createSpecificScope<FunctionScope, CallObject>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const;
|
|
template Scope*
|
|
ScopeStencil::createSpecificScope<LexicalScope, BlockLexicalEnvironmentObject>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const;
|
|
template Scope* ScopeStencil::createSpecificScope<
|
|
ClassBodyScope, BlockLexicalEnvironmentObject>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const;
|
|
template Scope*
|
|
ScopeStencil::createSpecificScope<EvalScope, VarEnvironmentObject>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const;
|
|
template Scope*
|
|
ScopeStencil::createSpecificScope<VarScope, VarEnvironmentObject>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const;
|
|
template Scope*
|
|
ScopeStencil::createSpecificScope<ModuleScope, ModuleEnvironmentObject>(
|
|
JSContext* cx, CompilationAtomCache& atomCache,
|
|
Handle<Scope*> enclosingScope, BaseParserScopeData* baseData) const;
|