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/. */
#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 nam