mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-24 08:07:30 +09:00
1467 lines
46 KiB
C++
1467 lines
46 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* 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 "mozilla/ScopeExit.h"
|
|
|
|
#include "jsscript.h"
|
|
#include "builtin/ModuleObject.h"
|
|
#include "gc/Allocator.h"
|
|
#include "vm/EnvironmentObject.h"
|
|
#include "vm/Runtime.h"
|
|
|
|
#include "vm/Shape-inl.h"
|
|
|
|
using namespace js;
|
|
|
|
using mozilla::Maybe;
|
|
using mozilla::MakeScopeExit;
|
|
using mozilla::Move;
|
|
using mozilla::Nothing;
|
|
using mozilla::Some;
|
|
|
|
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";
|
|
}
|
|
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::ParameterExpressionVar:
|
|
return "parameter expression 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::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";
|
|
}
|
|
MOZ_CRASH("Bad ScopeKind");
|
|
}
|
|
|
|
static Shape*
|
|
EmptyEnvironmentShape(ExclusiveContext* cx, const Class* cls, uint32_t numSlots,
|
|
uint32_t baseShapeFlags)
|
|
{
|
|
// Put as many slots into the object header as possible.
|
|
uint32_t numFixed = gc::GetGCKindSlots(gc::GetGCObjectKind(numSlots));
|
|
return EmptyShape::getInitialShape(cx, cls, TaggedProto(nullptr), numFixed,
|
|
baseShapeFlags);
|
|
}
|
|
|
|
static Shape*
|
|
NextEnvironmentShape(ExclusiveContext* cx, HandleAtom name, BindingKind bindKind, uint32_t slot,
|
|
StackBaseShape& stackBase, HandleShape shape)
|
|
{
|
|
UnownedBaseShape* base = BaseShape::getUnowned(cx, stackBase);
|
|
if (!base)
|
|
return nullptr;
|
|
|
|
unsigned attrs = JSPROP_PERMANENT | JSPROP_ENUMERATE;
|
|
switch (bindKind) {
|
|
case BindingKind::Const:
|
|
case BindingKind::NamedLambdaCallee:
|
|
attrs |= JSPROP_READONLY;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
jsid id = NameToId(name->asPropertyName());
|
|
Rooted<StackShape> child(cx, StackShape(base, id, slot, attrs, 0));
|
|
return cx->zone()->propertyTree.getChild(cx, shape, child);
|
|
}
|
|
|
|
static Shape*
|
|
CreateEnvironmentShape(ExclusiveContext* cx, BindingIter& bi, const Class* cls,
|
|
uint32_t numSlots, uint32_t baseShapeFlags)
|
|
{
|
|
RootedShape shape(cx, EmptyEnvironmentShape(cx, cls, numSlots, baseShapeFlags));
|
|
if (!shape)
|
|
return nullptr;
|
|
|
|
RootedAtom name(cx);
|
|
StackBaseShape stackBase(cx, cls, baseShapeFlags);
|
|
for (; bi; bi++) {
|
|
BindingLocation loc = bi.location();
|
|
if (loc.kind() == BindingLocation::Kind::Environment) {
|
|
name = bi.name();
|
|
shape = NextEnvironmentShape(cx, name, bi.kind(), loc.slot(), stackBase, shape);
|
|
if (!shape)
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return shape;
|
|
}
|
|
|
|
template <typename ConcreteScope>
|
|
static UniquePtr<typename ConcreteScope::Data>
|
|
CopyScopeData(ExclusiveContext* cx, Handle<typename ConcreteScope::Data*> data)
|
|
{
|
|
size_t dataSize = ConcreteScope::sizeOfData(data->length);
|
|
size_t headerSize = sizeof(typename ConcreteScope::Data);
|
|
MOZ_ASSERT(dataSize >= headerSize);
|
|
size_t extraSize = dataSize - headerSize;
|
|
|
|
uint8_t* copyBytes = cx->zone()->pod_malloc<uint8_t>(dataSize);
|
|
if (!copyBytes) {
|
|
ReportOutOfMemory(cx);
|
|
return nullptr;
|
|
}
|
|
|
|
auto dataCopy = reinterpret_cast<typename ConcreteScope::Data*>(copyBytes);
|
|
new (dataCopy) typename ConcreteScope::Data(*data);
|
|
|
|
uint8_t* extra = reinterpret_cast<uint8_t*>(data.get()) + headerSize;
|
|
uint8_t* extraCopy = copyBytes + headerSize;
|
|
|
|
mozilla::PodCopy<uint8_t>(extraCopy, extra, extraSize);
|
|
return UniquePtr<typename ConcreteScope::Data>(dataCopy);
|
|
}
|
|
|
|
template <typename ConcreteScope>
|
|
static bool
|
|
PrepareScopeData(ExclusiveContext* cx, BindingIter& bi, Handle<UniquePtr<typename ConcreteScope::Data>> data,
|
|
const Class* cls, uint32_t baseShapeFlags, MutableHandleShape envShape)
|
|
{
|
|
// Copy a fresh BindingIter for use below.
|
|
BindingIter freshBi(bi);
|
|
|
|
// Iterate through all bindings. This counts the number of environment
|
|
// slots needed and computes the maximum frame slot.
|
|
while (bi)
|
|
bi++;
|
|
data->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.set(nullptr);
|
|
} else {
|
|
envShape.set(CreateEnvironmentShape(cx, freshBi, cls, bi.nextEnvironmentSlot(),
|
|
baseShapeFlags));
|
|
if (!envShape)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename ConcreteScope>
|
|
static UniquePtr<typename ConcreteScope::Data>
|
|
NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0)
|
|
{
|
|
uint8_t* bytes = cx->zone()->pod_malloc<uint8_t>(ConcreteScope::sizeOfData(length));
|
|
if (!bytes)
|
|
ReportOutOfMemory(cx);
|
|
auto data = reinterpret_cast<typename ConcreteScope::Data*>(bytes);
|
|
if (data)
|
|
new (data) typename ConcreteScope::Data(length);
|
|
return UniquePtr<typename ConcreteScope::Data>(data);
|
|
}
|
|
|
|
static bool
|
|
XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
|
|
{
|
|
ExclusiveContext* cx = xdr->cx();
|
|
|
|
RootedAtom atom(cx, bindingName->name());
|
|
bool hasAtom = !!atom;
|
|
|
|
uint8_t u8 = uint8_t(hasAtom << 1) | uint8_t(bindingName->closedOver());
|
|
if (!xdr->codeUint8(&u8))
|
|
return false;
|
|
|
|
if (atom && !XDRAtom(xdr, &atom))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
XDRBindingName(XDRState<XDR_DECODE>* xdr, BindingName* bindingName)
|
|
{
|
|
ExclusiveContext* cx = xdr->cx();
|
|
|
|
uint8_t u8;
|
|
if (!xdr->codeUint8(&u8))
|
|
return false;
|
|
|
|
bool closedOver = u8 & 1;
|
|
bool hasAtom = u8 >> 1;
|
|
|
|
RootedAtom atom(cx);
|
|
if (hasAtom && !XDRAtom(xdr, &atom))
|
|
return false;
|
|
|
|
*bindingName = BindingName(atom, closedOver);
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename ConcreteScopeData>
|
|
static void
|
|
DeleteScopeData(ConcreteScopeData* data)
|
|
{
|
|
// Some scope Data classes have GCManagedDeletePolicy because then contain
|
|
// GCPtrs. Dispose of them in the appropriate way.
|
|
JS::DeletePolicy<ConcreteScopeData>()(data);
|
|
}
|
|
|
|
template <typename ConcreteScope, XDRMode mode>
|
|
/* static */ bool
|
|
Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
|
|
MutableHandle<typename ConcreteScope::Data*> data)
|
|
{
|
|
MOZ_ASSERT(!data);
|
|
|
|
ExclusiveContext* cx = xdr->cx();
|
|
|
|
uint32_t length;
|
|
if (mode == XDR_ENCODE)
|
|
length = scope->data().length;
|
|
if (!xdr->codeUint32(&length))
|
|
return false;
|
|
|
|
if (mode == XDR_ENCODE) {
|
|
data.set(&scope->data());
|
|
} else {
|
|
data.set(NewEmptyScopeData<ConcreteScope>(cx, length).release());
|
|
if (!data)
|
|
return false;
|
|
data->length = length;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < length; i++) {
|
|
if (!XDRBindingName(xdr, &data->trailingNames[i])) {
|
|
if (mode == XDR_DECODE) {
|
|
DeleteScopeData(data.get());
|
|
data.set(nullptr);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* static */ Scope*
|
|
Scope::create(ExclusiveContext* cx, ScopeKind kind, HandleScope enclosing, HandleShape envShape)
|
|
{
|
|
Scope* scope = Allocate<Scope>(cx);
|
|
if (scope)
|
|
new (scope) Scope(kind, enclosing, envShape);
|
|
return scope;
|
|
}
|
|
|
|
template <typename T, typename D>
|
|
/* static */ Scope*
|
|
Scope::create(ExclusiveContext* cx, ScopeKind kind, HandleScope enclosing,
|
|
HandleShape envShape, mozilla::UniquePtr<T, D> 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(Move(data));
|
|
|
|
return scope;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
Shape*
|
|
Scope::maybeCloneEnvironmentShape(JSContext* cx)
|
|
{
|
|
// Clone the environment shape if cloning into a different zone.
|
|
if (environmentShape_ && environmentShape_->zoneFromAnyThread() != cx->zone()) {
|
|
BindingIter bi(this);
|
|
return CreateEnvironmentShape(cx, bi,
|
|
environmentShape_->getObjectClass(),
|
|
environmentShape_->slotSpan(),
|
|
environmentShape_->getObjectFlags());
|
|
}
|
|
return environmentShape_;
|
|
}
|
|
|
|
/* static */ Scope*
|
|
Scope::clone(JSContext* cx, HandleScope scope, HandleScope enclosing)
|
|
{
|
|
RootedShape envShape(cx);
|
|
if (scope->environmentShape()) {
|
|
envShape = scope->maybeCloneEnvironmentShape(cx);
|
|
if (!envShape)
|
|
return nullptr;
|
|
}
|
|
|
|
switch (scope->kind_) {
|
|
case ScopeKind::Function:
|
|
MOZ_CRASH("Use FunctionScope::clone.");
|
|
break;
|
|
|
|
case ScopeKind::FunctionBodyVar:
|
|
case ScopeKind::ParameterExpressionVar: {
|
|
Rooted<VarScope::Data*> original(cx, &scope->as<VarScope>().data());
|
|
UniquePtr<VarScope::Data> dataClone = CopyScopeData<VarScope>(cx, original);
|
|
if (!dataClone)
|
|
return nullptr;
|
|
return create(cx, scope->kind_, enclosing, envShape, Move(dataClone));
|
|
}
|
|
|
|
case ScopeKind::Lexical:
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
case ScopeKind::NamedLambda:
|
|
case ScopeKind::StrictNamedLambda: {
|
|
Rooted<LexicalScope::Data*> original(cx, &scope->as<LexicalScope>().data());
|
|
UniquePtr<LexicalScope::Data> dataClone = CopyScopeData<LexicalScope>(cx, original);
|
|
if (!dataClone)
|
|
return nullptr;
|
|
return create(cx, scope->kind_, enclosing, envShape, Move(dataClone));
|
|
}
|
|
|
|
case ScopeKind::With:
|
|
return create(cx, scope->kind_, enclosing, envShape);
|
|
|
|
case ScopeKind::Eval:
|
|
case ScopeKind::StrictEval: {
|
|
Rooted<EvalScope::Data*> original(cx, &scope->as<EvalScope>().data());
|
|
UniquePtr<EvalScope::Data> dataClone = CopyScopeData<EvalScope>(cx, original);
|
|
if (!dataClone)
|
|
return nullptr;
|
|
return create(cx, scope->kind_, enclosing, envShape, Move(dataClone));
|
|
}
|
|
|
|
case ScopeKind::Global:
|
|
case ScopeKind::NonSyntactic:
|
|
MOZ_CRASH("Use GlobalScope::clone.");
|
|
break;
|
|
|
|
case ScopeKind::Module:
|
|
MOZ_CRASH("NYI");
|
|
break;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
Scope::finalize(FreeOp* fop)
|
|
{
|
|
MOZ_ASSERT(CurrentThreadIsGCSweeping());
|
|
if (data_) {
|
|
// We don't need to call the destructors for any GCPtrs in Data because
|
|
// this only happens during a GC.
|
|
fop->free_(reinterpret_cast<void*>(data_));
|
|
data_ = 0;
|
|
}
|
|
}
|
|
|
|
size_t
|
|
Scope::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
|
{
|
|
if (data_)
|
|
return mallocSizeOf(reinterpret_cast<void*>(data_));
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
Scope::dump()
|
|
{
|
|
for (ScopeIter si(this); si; si++) {
|
|
fprintf(stderr, "%s [%p]", ScopeKindString(si.kind()), si.scope());
|
|
if (si.scope()->enclosing())
|
|
fprintf(stderr, " -> ");
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
uint32_t
|
|
LexicalScope::firstFrameSlot() const
|
|
{
|
|
switch (kind()) {
|
|
case ScopeKind::Lexical:
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
// For intra-frame scopes, find the enclosing scope's next frame slot.
|
|
return nextFrameSlot(enclosing());
|
|
case ScopeKind::NamedLambda:
|
|
case ScopeKind::StrictNamedLambda:
|
|
// Named lambda scopes cannot have frame slots.
|
|
return LOCALNO_LIMIT;
|
|
default:
|
|
// Otherwise start at 0.
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* static */ uint32_t
|
|
LexicalScope::nextFrameSlot(Scope* scope)
|
|
{
|
|
for (ScopeIter si(scope); si; si++) {
|
|
switch (si.kind()) {
|
|
case ScopeKind::Function:
|
|
return si.scope()->as<FunctionScope>().nextFrameSlot();
|
|
case ScopeKind::FunctionBodyVar:
|
|
case ScopeKind::ParameterExpressionVar:
|
|
return si.scope()->as<VarScope>().nextFrameSlot();
|
|
case ScopeKind::Lexical:
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
return si.scope()->as<LexicalScope>().nextFrameSlot();
|
|
case ScopeKind::NamedLambda:
|
|
case ScopeKind::StrictNamedLambda:
|
|
// Named lambda scopes cannot have frame slots.
|
|
return 0;
|
|
case ScopeKind::With:
|
|
continue;
|
|
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();
|
|
}
|
|
}
|
|
MOZ_CRASH("Not an enclosing intra-frame Scope");
|
|
}
|
|
|
|
/* static */ LexicalScope*
|
|
LexicalScope::create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> data,
|
|
uint32_t firstFrameSlot, HandleScope enclosing)
|
|
{
|
|
MOZ_ASSERT(data, "LexicalScopes should not be created if there are no bindings.");
|
|
|
|
// The data that's passed in is be from the frontend and is LifoAlloc'd.
|
|
// Copy it now that we're creating a permanent VM scope.
|
|
Rooted<UniquePtr<Data>> copy(cx, CopyScopeData<LexicalScope>(cx, data));
|
|
if (!copy)
|
|
return nullptr;
|
|
|
|
return createWithData(cx, kind, ©, firstFrameSlot, enclosing);
|
|
}
|
|
|
|
/* static */ LexicalScope*
|
|
LexicalScope::createWithData(ExclusiveContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data,
|
|
uint32_t firstFrameSlot, HandleScope enclosing)
|
|
{
|
|
bool isNamedLambda = kind == ScopeKind::NamedLambda || kind == ScopeKind::StrictNamedLambda;
|
|
|
|
MOZ_ASSERT_IF(!isNamedLambda && firstFrameSlot != 0,
|
|
firstFrameSlot == nextFrameSlot(enclosing));
|
|
MOZ_ASSERT_IF(isNamedLambda, firstFrameSlot == LOCALNO_LIMIT);
|
|
|
|
RootedShape envShape(cx);
|
|
BindingIter bi(*data, firstFrameSlot, isNamedLambda);
|
|
if (!PrepareScopeData<LexicalScope>(cx, bi, data, &LexicalEnvironmentObject::class_,
|
|
BaseShape::NOT_EXTENSIBLE | BaseShape::DELEGATE, &envShape))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
Scope* scope = Scope::create(cx, kind, enclosing, envShape, Move(data.get()));
|
|
if (!scope)
|
|
return nullptr;
|
|
MOZ_ASSERT(scope->as<LexicalScope>().firstFrameSlot() == firstFrameSlot);
|
|
return &scope->as<LexicalScope>();
|
|
}
|
|
|
|
/* static */ Shape*
|
|
LexicalScope::getEmptyExtensibleEnvironmentShape(ExclusiveContext* cx)
|
|
{
|
|
const Class* cls = &LexicalEnvironmentObject::class_;
|
|
return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), BaseShape::DELEGATE);
|
|
}
|
|
|
|
template <XDRMode mode>
|
|
/* static */ bool
|
|
LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope)
|
|
{
|
|
ExclusiveContext* cx = xdr->cx();
|
|
|
|
Rooted<Data*> data(cx);
|
|
if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data))
|
|
return false;
|
|
|
|
{
|
|
Maybe<Rooted<UniquePtr<Data>>> uniqueData;
|
|
if (mode == XDR_DECODE)
|
|
uniqueData.emplace(cx, data);
|
|
|
|
uint32_t firstFrameSlot;
|
|
uint32_t nextFrameSlot;
|
|
if (mode == XDR_ENCODE) {
|
|
firstFrameSlot = scope->as<LexicalScope>().firstFrameSlot();
|
|
nextFrameSlot = data->nextFrameSlot;
|
|
}
|
|
|
|
if (!xdr->codeUint32(&data->constStart))
|
|
return false;
|
|
if (!xdr->codeUint32(&firstFrameSlot))
|
|
return false;
|
|
if (!xdr->codeUint32(&nextFrameSlot))
|
|
return false;
|
|
|
|
if (mode == XDR_DECODE) {
|
|
scope.set(createWithData(cx, kind, &uniqueData.ref(), firstFrameSlot, enclosing));
|
|
if (!scope)
|
|
return false;
|
|
|
|
// nextFrameSlot is used only for this correctness check.
|
|
MOZ_ASSERT(nextFrameSlot == scope->as<LexicalScope>().data().nextFrameSlot);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template
|
|
/* static */ bool
|
|
LexicalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope);
|
|
|
|
template
|
|
/* static */ bool
|
|
LexicalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope);
|
|
|
|
static inline uint32_t
|
|
FunctionScopeEnvShapeFlags(bool hasParameterExprs)
|
|
{
|
|
if (hasParameterExprs)
|
|
return BaseShape::DELEGATE;
|
|
return BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
|
|
}
|
|
|
|
/* static */ FunctionScope*
|
|
FunctionScope::create(ExclusiveContext* cx, Handle<Data*> dataArg,
|
|
bool hasParameterExprs, bool needsEnvironment,
|
|
HandleFunction fun, HandleScope enclosing)
|
|
{
|
|
// The data that's passed in is be from the frontend and is LifoAlloc'd.
|
|
// Copy it now that we're creating a permanent VM scope.
|
|
Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<FunctionScope>(cx, dataArg)
|
|
: NewEmptyScopeData<FunctionScope>(cx));
|
|
if (!data)
|
|
return nullptr;
|
|
|
|
return createWithData(cx, &data, hasParameterExprs, needsEnvironment, fun, enclosing);
|
|
}
|
|
|
|
/* static */ FunctionScope*
|
|
FunctionScope::createWithData(ExclusiveContext* cx, MutableHandle<UniquePtr<Data>> data,
|
|
bool hasParameterExprs, bool needsEnvironment,
|
|
HandleFunction fun, HandleScope enclosing)
|
|
{
|
|
MOZ_ASSERT(data);
|
|
MOZ_ASSERT(fun->isTenured());
|
|
|
|
// FunctionScope::Data has GCManagedDeletePolicy because it contains a
|
|
// GCPtr. Destruction of |data| below may trigger calls into the GC.
|
|
Rooted<FunctionScope*> funScope(cx);
|
|
|
|
{
|
|
RootedShape envShape(cx);
|
|
|
|
BindingIter bi(*data, hasParameterExprs);
|
|
uint32_t shapeFlags = FunctionScopeEnvShapeFlags(hasParameterExprs);
|
|
if (!PrepareScopeData<FunctionScope>(cx, bi, data, &CallObject::class_, shapeFlags,
|
|
&envShape))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
data->hasParameterExprs = hasParameterExprs;
|
|
data->canonicalFunction.init(fun);
|
|
|
|
// 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
|
|
if (!envShape && needsEnvironment) {
|
|
envShape = getEmptyEnvironmentShape(cx, hasParameterExprs);
|
|
if (!envShape)
|
|
return nullptr;
|
|
}
|
|
|
|
Scope* scope = Scope::create(cx, ScopeKind::Function, enclosing, envShape);
|
|
if (!scope)
|
|
return nullptr;
|
|
|
|
funScope = &scope->as<FunctionScope>();
|
|
funScope->initData(Move(data.get()));
|
|
}
|
|
|
|
return funScope;
|
|
}
|
|
|
|
JSScript*
|
|
FunctionScope::script() const
|
|
{
|
|
return canonicalFunction()->nonLazyScript();
|
|
}
|
|
|
|
/* static */ bool
|
|
FunctionScope::isSpecialName(ExclusiveContext* cx, JSAtom* name)
|
|
{
|
|
return name == cx->names().arguments ||
|
|
name == cx->names().dotThis ||
|
|
name == cx->names().dotGenerator;
|
|
}
|
|
|
|
/* static */ Shape*
|
|
FunctionScope::getEmptyEnvironmentShape(ExclusiveContext* cx, bool hasParameterExprs)
|
|
{
|
|
const Class* cls = &CallObject::class_;
|
|
uint32_t shapeFlags = FunctionScopeEnvShapeFlags(hasParameterExprs);
|
|
return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), shapeFlags);
|
|
}
|
|
|
|
/* static */ FunctionScope*
|
|
FunctionScope::clone(JSContext* cx, Handle<FunctionScope*> scope, HandleFunction fun,
|
|
HandleScope enclosing)
|
|
{
|
|
MOZ_ASSERT(fun != scope->canonicalFunction());
|
|
|
|
// FunctionScope::Data has GCManagedDeletePolicy because it contains a
|
|
// GCPtr. Destruction of |dataClone| below may trigger calls into the GC.
|
|
Rooted<FunctionScope*> funScopeClone(cx);
|
|
|
|
{
|
|
RootedShape envShape(cx);
|
|
if (scope->environmentShape()) {
|
|
envShape = scope->maybeCloneEnvironmentShape(cx);
|
|
if (!envShape)
|
|
return nullptr;
|
|
}
|
|
|
|
Rooted<Data*> dataOriginal(cx, &scope->as<FunctionScope>().data());
|
|
Rooted<UniquePtr<Data>> dataClone(cx, CopyScopeData<FunctionScope>(cx, dataOriginal));
|
|
if (!dataClone)
|
|
return nullptr;
|
|
|
|
dataClone->canonicalFunction.init(fun);
|
|
|
|
Scope* scopeClone = Scope::create(cx, scope->kind(), enclosing, envShape);
|
|
if (!scopeClone)
|
|
return nullptr;
|
|
|
|
funScopeClone = &scopeClone->as<FunctionScope>();
|
|
funScopeClone->initData(Move(dataClone.get()));
|
|
}
|
|
|
|
return funScopeClone;
|
|
}
|
|
|
|
template <XDRMode mode>
|
|
/* static */ bool
|
|
FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosing,
|
|
MutableHandleScope scope)
|
|
{
|
|
ExclusiveContext* cx = xdr->cx();
|
|
Rooted<Data*> data(cx);
|
|
if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data))
|
|
return false;
|
|
|
|
{
|
|
Maybe<Rooted<UniquePtr<Data>>> uniqueData;
|
|
if (mode == XDR_DECODE)
|
|
uniqueData.emplace(cx, data);
|
|
|
|
uint8_t needsEnvironment;
|
|
uint8_t hasParameterExprs;
|
|
uint32_t nextFrameSlot;
|
|
if (mode == XDR_ENCODE) {
|
|
needsEnvironment = scope->hasEnvironment();
|
|
hasParameterExprs = data->hasParameterExprs;
|
|
nextFrameSlot = data->nextFrameSlot;
|
|
}
|
|
if (!xdr->codeUint8(&needsEnvironment))
|
|
return false;
|
|
if (!xdr->codeUint8(&hasParameterExprs))
|
|
return false;
|
|
if (!xdr->codeUint16(&data->nonPositionalFormalStart))
|
|
return false;
|
|
if (!xdr->codeUint16(&data->varStart))
|
|
return false;
|
|
if (!xdr->codeUint32(&nextFrameSlot))
|
|
return false;
|
|
|
|
if (mode == XDR_DECODE) {
|
|
if (!data->length) {
|
|
MOZ_ASSERT(!data->nonPositionalFormalStart);
|
|
MOZ_ASSERT(!data->varStart);
|
|
MOZ_ASSERT(!data->nextFrameSlot);
|
|
}
|
|
|
|
scope.set(createWithData(cx, &uniqueData.ref(), hasParameterExprs, needsEnvironment, fun,
|
|
enclosing));
|
|
if (!scope)
|
|
return false;
|
|
|
|
// nextFrameSlot is used only for this correctness check.
|
|
MOZ_ASSERT(nextFrameSlot == scope->as<FunctionScope>().data().nextFrameSlot);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template
|
|
/* static */ bool
|
|
FunctionScope::XDR(XDRState<XDR_ENCODE>* xdr, HandleFunction fun, HandleScope enclosing,
|
|
MutableHandleScope scope);
|
|
|
|
template
|
|
/* static */ bool
|
|
FunctionScope::XDR(XDRState<XDR_DECODE>* xdr, HandleFunction fun, HandleScope enclosing,
|
|
MutableHandleScope scope);
|
|
|
|
static const uint32_t VarScopeEnvShapeFlags =
|
|
BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
|
|
|
|
static UniquePtr<VarScope::Data>
|
|
NewEmptyVarScopeData(ExclusiveContext* cx, uint32_t firstFrameSlot)
|
|
{
|
|
UniquePtr<VarScope::Data> data(NewEmptyScopeData<VarScope>(cx));
|
|
if (data)
|
|
data->nextFrameSlot = firstFrameSlot;
|
|
|
|
return data;
|
|
}
|
|
|
|
/* static */ VarScope*
|
|
VarScope::create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> dataArg,
|
|
uint32_t firstFrameSlot, bool needsEnvironment, HandleScope enclosing)
|
|
{
|
|
// The data that's passed in is be from the frontend and is LifoAlloc'd.
|
|
// Copy it now that we're creating a permanent VM scope.
|
|
Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<VarScope>(cx, dataArg)
|
|
: NewEmptyVarScopeData(cx, firstFrameSlot));
|
|
if (!data)
|
|
return nullptr;
|
|
|
|
return createWithData(cx, kind, &data, firstFrameSlot, needsEnvironment, enclosing);
|
|
}
|
|
|
|
/* static */ VarScope*
|
|
VarScope::createWithData(ExclusiveContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> data,
|
|
uint32_t firstFrameSlot, bool needsEnvironment, HandleScope enclosing)
|
|
{
|
|
MOZ_ASSERT(data);
|
|
|
|
RootedShape envShape(cx);
|
|
BindingIter bi(*data, firstFrameSlot);
|
|
if (!PrepareScopeData<VarScope>(cx, bi, data, &VarEnvironmentObject::class_, VarScopeEnvShapeFlags,
|
|
&envShape))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// An environment may be needed regardless of existence of any closed over
|
|
// bindings:
|
|
// - Extensible scopes (i.e., due to direct eval)
|
|
// - Being a generator
|
|
if (!envShape && needsEnvironment) {
|
|
envShape = getEmptyEnvironmentShape(cx);
|
|
if (!envShape)
|
|
return nullptr;
|
|
}
|
|
|
|
Scope* scope = Scope::create(cx, kind, enclosing, envShape, Move(data.get()));
|
|
if (!scope)
|
|
return nullptr;
|
|
return &scope->as<VarScope>();
|
|
}
|
|
|
|
/* static */ Shape*
|
|
VarScope::getEmptyEnvironmentShape(ExclusiveContext* cx)
|
|
{
|
|
const Class* cls = &VarEnvironmentObject::class_;
|
|
return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), VarScopeEnvShapeFlags);
|
|
}
|
|
|
|
uint32_t
|
|
VarScope::firstFrameSlot() const
|
|
{
|
|
if (enclosing()->is<FunctionScope>())
|
|
return enclosing()->as<FunctionScope>().nextFrameSlot();
|
|
return 0;
|
|
}
|
|
|
|
template <XDRMode mode>
|
|
/* static */ bool
|
|
VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope)
|
|
{
|
|
ExclusiveContext* cx = xdr->cx();
|
|
Rooted<Data*> data(cx);
|
|
if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data))
|
|
return false;
|
|
|
|
{
|
|
Maybe<Rooted<UniquePtr<Data>>> uniqueData;
|
|
if (mode == XDR_DECODE)
|
|
uniqueData.emplace(cx, data);
|
|
|
|
uint8_t needsEnvironment;
|
|
uint32_t firstFrameSlot;
|
|
uint32_t nextFrameSlot;
|
|
if (mode == XDR_ENCODE) {
|
|
needsEnvironment = scope->hasEnvironment();
|
|
firstFrameSlot = scope->as<VarScope>().firstFrameSlot();
|
|
nextFrameSlot = data->nextFrameSlot;
|
|
}
|
|
if (!xdr->codeUint8(&needsEnvironment))
|
|
return false;
|
|
if (!xdr->codeUint32(&firstFrameSlot))
|
|
return false;
|
|
if (!xdr->codeUint32(&nextFrameSlot))
|
|
return false;
|
|
|
|
if (mode == XDR_DECODE) {
|
|
if (!data->length) {
|
|
MOZ_ASSERT(!data->nextFrameSlot);
|
|
}
|
|
|
|
scope.set(createWithData(cx, kind, &uniqueData.ref(), firstFrameSlot, needsEnvironment,
|
|
enclosing));
|
|
if (!scope)
|
|
return false;
|
|
|
|
// nextFrameSlot is used only for this correctness check.
|
|
MOZ_ASSERT(nextFrameSlot == scope->as<VarScope>().data().nextFrameSlot);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template
|
|
/* static */ bool
|
|
VarScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope);
|
|
|
|
template
|
|
/* static */ bool
|
|
VarScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope);
|
|
|
|
/* static */ GlobalScope*
|
|
GlobalScope::create(ExclusiveContext* cx, ScopeKind kind, Handle<Data*> dataArg)
|
|
{
|
|
// The data that's passed in is be from the frontend and is LifoAlloc'd.
|
|
// Copy it now that we're creating a permanent VM scope.
|
|
Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<GlobalScope>(cx, dataArg)
|
|
: NewEmptyScopeData<GlobalScope>(cx));
|
|
if (!data)
|
|
return nullptr;
|
|
|
|
return createWithData(cx, kind, &data);
|
|
}
|
|
|
|
/* static */ GlobalScope*
|
|
GlobalScope::createWithData(ExclusiveContext* cx, ScopeKind kind, MutableHandle<UniquePtr<Data>> 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.
|
|
Scope* scope = Scope::create(cx, kind, nullptr, nullptr, Move(data.get()));
|
|
if (!scope)
|
|
return nullptr;
|
|
return &scope->as<GlobalScope>();
|
|
}
|
|
|
|
/* static */ GlobalScope*
|
|
GlobalScope::clone(JSContext* cx, Handle<GlobalScope*> scope, ScopeKind kind)
|
|
{
|
|
Rooted<Data*> dataOriginal(cx, &scope->as<GlobalScope>().data());
|
|
Rooted<UniquePtr<Data>> dataClone(cx, CopyScopeData<GlobalScope>(cx, dataOriginal));
|
|
if (!dataClone)
|
|
return nullptr;
|
|
|
|
Scope* scopeClone = Scope::create(cx, kind, nullptr, nullptr, Move(dataClone.get()));
|
|
if (!scopeClone)
|
|
return nullptr;
|
|
return &scopeClone->as<GlobalScope>();
|
|
}
|
|
|
|
template <XDRMode mode>
|
|
/* static */ bool
|
|
GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
|
|
{
|
|
MOZ_ASSERT((mode == XDR_DECODE) == !scope);
|
|
|
|
ExclusiveContext* cx = xdr->cx();
|
|
Rooted<Data*> data(cx);
|
|
if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data))
|
|
return false;
|
|
|
|
{
|
|
Maybe<Rooted<UniquePtr<Data>>> uniqueData;
|
|
if (mode == XDR_DECODE)
|
|
uniqueData.emplace(cx, data);
|
|
|
|
if (!xdr->codeUint32(&data->letStart))
|
|
return false;
|
|
if (!xdr->codeUint32(&data->constStart))
|
|
return false;
|
|
|
|
if (mode == XDR_DECODE) {
|
|
if (!data->length) {
|
|
MOZ_ASSERT(!data->letStart);
|
|
MOZ_ASSERT(!data->constStart);
|
|
}
|
|
|
|
scope.set(createWithData(cx, kind, &uniqueData.ref()));
|
|
if (!scope)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template
|
|
/* static */ bool
|
|
GlobalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, MutableHandleScope scope);
|
|
|
|
template
|
|
/* static */ bool
|
|
GlobalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, MutableHandleScope scope);
|
|
|
|
/* static */ WithScope*
|
|
WithScope::create(ExclusiveContext* cx, HandleScope enclosing)
|
|
{
|
|
Scope* scope = Scope::create(cx, ScopeKind::With, enclosing, nullptr);
|
|
return static_cast<WithScope*>(scope);
|
|
}
|
|
|
|
static const uint32_t EvalScopeEnvShapeFlags =
|
|
BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
|
|
|
|
/* static */ EvalScope*
|
|
EvalScope::create(ExclusiveContext* cx, ScopeKind scopeKind, Handle<Data*> dataArg,
|
|
HandleScope enclosing)
|
|
{
|
|
// The data that's passed in is be from the frontend and is LifoAlloc'd.
|
|
// Copy it now that we're creating a permanent VM scope.
|
|
Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<EvalScope>(cx, dataArg)
|
|
: NewEmptyScopeData<EvalScope>(cx));
|
|
if (!data)
|
|
return nullptr;
|
|
|
|
return createWithData(cx, scopeKind, &data, enclosing);
|
|
}
|
|
|
|
/* static */ EvalScope*
|
|
EvalScope::createWithData(ExclusiveContext* cx, ScopeKind scopeKind, MutableHandle<UniquePtr<Data>> data,
|
|
HandleScope enclosing)
|
|
{
|
|
MOZ_ASSERT(data);
|
|
|
|
RootedShape envShape(cx);
|
|
if (scopeKind == ScopeKind::StrictEval) {
|
|
BindingIter bi(*data, true);
|
|
if (!PrepareScopeData<EvalScope>(cx, bi, data, &VarEnvironmentObject::class_,
|
|
EvalScopeEnvShapeFlags, &envShape))
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Strict eval and direct eval in parameter expressions always get their own
|
|
// var environment even if there are no bindings.
|
|
if (!envShape && scopeKind == ScopeKind::StrictEval) {
|
|
envShape = getEmptyEnvironmentShape(cx);
|
|
if (!envShape)
|
|
return nullptr;
|
|
}
|
|
|
|
Scope* scope = Scope::create(cx, scopeKind, enclosing, envShape, Move(data.get()));
|
|
if (!scope)
|
|
return nullptr;
|
|
return &scope->as<EvalScope>();
|
|
}
|
|
|
|
/* static */ Scope*
|
|
EvalScope::nearestVarScopeForDirectEval(Scope* scope)
|
|
{
|
|
for (ScopeIter si(scope); si; si++) {
|
|
switch (si.kind()) {
|
|
case ScopeKind::Function:
|
|
case ScopeKind::FunctionBodyVar:
|
|
case ScopeKind::ParameterExpressionVar:
|
|
case ScopeKind::Global:
|
|
case ScopeKind::NonSyntactic:
|
|
return scope;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/* static */ Shape*
|
|
EvalScope::getEmptyEnvironmentShape(ExclusiveContext* cx)
|
|
{
|
|
const Class* cls = &VarEnvironmentObject::class_;
|
|
return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), EvalScopeEnvShapeFlags);
|
|
}
|
|
|
|
template <XDRMode mode>
|
|
/* static */ bool
|
|
EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope)
|
|
{
|
|
ExclusiveContext* cx = xdr->cx();
|
|
Rooted<Data*> data(cx);
|
|
|
|
{
|
|
Maybe<Rooted<UniquePtr<Data>>> uniqueData;
|
|
if (mode == XDR_DECODE)
|
|
uniqueData.emplace(cx, data);
|
|
|
|
if (!XDRSizedBindingNames<EvalScope>(xdr, scope.as<EvalScope>(), &data))
|
|
return false;
|
|
|
|
if (mode == XDR_DECODE) {
|
|
if (!data->length)
|
|
MOZ_ASSERT(!data->nextFrameSlot);
|
|
|
|
scope.set(createWithData(cx, kind, &uniqueData.ref(), enclosing));
|
|
if (!scope)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template
|
|
/* static */ bool
|
|
EvalScope::XDR(XDRState<XDR_ENCODE>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope);
|
|
|
|
template
|
|
/* static */ bool
|
|
EvalScope::XDR(XDRState<XDR_DECODE>* xdr, ScopeKind kind, HandleScope enclosing,
|
|
MutableHandleScope scope);
|
|
|
|
static const uint32_t ModuleScopeEnvShapeFlags =
|
|
BaseShape::NOT_EXTENSIBLE | BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
|
|
|
|
/* static */ ModuleScope*
|
|
ModuleScope::create(ExclusiveContext* cx, Handle<Data*> dataArg,
|
|
HandleModuleObject module, HandleScope enclosing)
|
|
{
|
|
Rooted<UniquePtr<Data>> data(cx, dataArg ? CopyScopeData<ModuleScope>(cx, dataArg)
|
|
: NewEmptyScopeData<ModuleScope>(cx));
|
|
if (!data)
|
|
return nullptr;
|
|
|
|
return createWithData(cx, &data, module, enclosing);
|
|
}
|
|
|
|
/* static */ ModuleScope*
|
|
ModuleScope::createWithData(ExclusiveContext* cx, MutableHandle<UniquePtr<Data>> data,
|
|
HandleModuleObject module, HandleScope enclosing)
|
|
{
|
|
MOZ_ASSERT(data);
|
|
MOZ_ASSERT(enclosing->is<GlobalScope>());
|
|
|
|
// ModuleScope::Data has GCManagedDeletePolicy because it contains a
|
|
// GCPtr. Destruction of |copy| below may trigger calls into the GC.
|
|
Rooted<ModuleScope*> moduleScope(cx);
|
|
|
|
{
|
|
// The data that's passed in is be from the frontend and is LifoAlloc'd.
|
|
// Copy it now that we're creating a permanent VM scope.
|
|
RootedShape envShape(cx);
|
|
BindingIter bi(*data);
|
|
if (!PrepareScopeData<ModuleScope>(cx, bi, data, &ModuleEnvironmentObject::class_,
|
|
ModuleScopeEnvShapeFlags, &envShape))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// Modules always need an environment object for now.
|
|
if (!envShape) {
|
|
envShape = getEmptyEnvironmentShape(cx);
|
|
if (!envShape)
|
|
return nullptr;
|
|
}
|
|
|
|
Scope* scope = Scope::create(cx, ScopeKind::Module, enclosing, envShape);
|
|
if (!scope)
|
|
return nullptr;
|
|
|
|
data->module.init(module);
|
|
|
|
moduleScope = &scope->as<ModuleScope>();
|
|
moduleScope->initData(Move(data.get()));
|
|
}
|
|
|
|
return moduleScope;
|
|
}
|
|
|
|
/* static */ Shape*
|
|
ModuleScope::getEmptyEnvironmentShape(ExclusiveContext* cx)
|
|
{
|
|
const Class* cls = &ModuleEnvironmentObject::class_;
|
|
return EmptyEnvironmentShape(cx, cls, JSSLOT_FREE(cls), ModuleScopeEnvShapeFlags);
|
|
}
|
|
|
|
JSScript*
|
|
ModuleScope::script() const
|
|
{
|
|
return module()->script();
|
|
}
|
|
|
|
ScopeIter::ScopeIter(JSScript* script)
|
|
: scope_(script->bodyScope())
|
|
{ }
|
|
|
|
bool
|
|
ScopeIter::hasSyntacticEnvironment() const
|
|
{
|
|
return scope()->hasEnvironment() && scope()->kind() != ScopeKind::NonSyntactic;
|
|
}
|
|
|
|
BindingIter::BindingIter(Scope* scope)
|
|
{
|
|
switch (scope->kind()) {
|
|
case ScopeKind::Lexical:
|
|
case ScopeKind::SimpleCatch:
|
|
case ScopeKind::Catch:
|
|
init(scope->as<LexicalScope>().data(),
|
|
scope->as<LexicalScope>().firstFrameSlot(), 0);
|
|
break;
|
|
case ScopeKind::NamedLambda:
|
|
case ScopeKind::StrictNamedLambda:
|
|
init(scope->as<LexicalScope>().data(), LOCALNO_LIMIT, IsNamedLambda);
|
|
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 (scope->as<FunctionScope>().hasParameterExprs())
|
|
flags |= HasFormalParameterExprs;
|
|
init(scope->as<FunctionScope>().data(), flags);
|
|
break;
|
|
}
|
|
case ScopeKind::FunctionBodyVar:
|
|
case ScopeKind::ParameterExpressionVar:
|
|
init(scope->as<VarScope>().data(),
|
|
scope->as<VarScope>().firstFrameSlot());
|
|
break;
|
|
case ScopeKind::Eval:
|
|
case ScopeKind::StrictEval:
|
|
init(scope->as<EvalScope>().data(), scope->kind() == ScopeKind::StrictEval);
|
|
break;
|
|
case ScopeKind::Global:
|
|
case ScopeKind::NonSyntactic:
|
|
init(scope->as<GlobalScope>().data());
|
|
break;
|
|
case ScopeKind::Module:
|
|
init(scope->as<ModuleScope>().data());
|
|
break;
|
|
}
|
|
}
|
|
|
|
BindingIter::BindingIter(JSScript* script)
|
|
: BindingIter(script->bodyScope())
|
|
{ }
|
|
|
|
void
|
|
BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t flags)
|
|
{
|
|
// 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(0, 0, 0, 0, 0, 0,
|
|
CanHaveEnvironmentSlots | flags,
|
|
firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
|
|
data.trailingNames.start(), data.length);
|
|
} else {
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// top-level funcs - [0, 0)
|
|
// vars - [0, 0)
|
|
// lets - [0, data.constStart)
|
|
// consts - [data.constStart, data.length)
|
|
init(0, 0, 0, 0, 0, data.constStart,
|
|
CanHaveFrameSlots | CanHaveEnvironmentSlots | flags,
|
|
firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
|
|
data.trailingNames.start(), data.length);
|
|
}
|
|
}
|
|
|
|
void
|
|
BindingIter::init(FunctionScope::Data& data, uint8_t flags)
|
|
{
|
|
flags = CanHaveFrameSlots | CanHaveEnvironmentSlots | flags;
|
|
if (!(flags & HasFormalParameterExprs))
|
|
flags |= CanHaveArgumentSlots;
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, data.nonPositionalFormalStart)
|
|
// other formals - [data.nonPositionalParamStart, data.varStart)
|
|
// top-level funcs - [data.varStart, data.varStart)
|
|
// vars - [data.varStart, data.length)
|
|
// lets - [data.length, data.length)
|
|
// consts - [data.length, data.length)
|
|
init(0, data.nonPositionalFormalStart, data.varStart, data.varStart, data.length, data.length,
|
|
flags,
|
|
0, JSSLOT_FREE(&CallObject::class_),
|
|
data.trailingNames.start(), data.length);
|
|
}
|
|
|
|
void
|
|
BindingIter::init(VarScope::Data& data, uint32_t firstFrameSlot)
|
|
{
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// top-level funcs - [0, 0)
|
|
// vars - [0, data.length)
|
|
// lets - [data.length, data.length)
|
|
// consts - [data.length, data.length)
|
|
init(0, 0, 0, 0, data.length, data.length,
|
|
CanHaveFrameSlots | CanHaveEnvironmentSlots,
|
|
firstFrameSlot, JSSLOT_FREE(&VarEnvironmentObject::class_),
|
|
data.trailingNames.start(), data.length);
|
|
}
|
|
|
|
void
|
|
BindingIter::init(GlobalScope::Data& data)
|
|
{
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// top-level funcs - [0, data.varStart)
|
|
// vars - [data.varStart, data.letStart)
|
|
// lets - [data.letStart, data.constStart)
|
|
// consts - [data.constStart, data.length)
|
|
init(0, 0, 0, data.varStart, data.letStart, data.constStart,
|
|
CannotHaveSlots,
|
|
UINT32_MAX, UINT32_MAX,
|
|
data.trailingNames.start(), data.length);
|
|
}
|
|
|
|
void
|
|
BindingIter::init(EvalScope::Data& 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;
|
|
}
|
|
|
|
// imports - [0, 0)
|
|
// positional formals - [0, 0)
|
|
// other formals - [0, 0)
|
|
// top-level funcs - [0, data.varStart)
|
|
// vars - [data.varStart, data.length)
|
|
// lets - [data.length, data.length)
|
|
// consts - [data.length, data.length)
|
|
init(0, 0, 0, data.varStart, data.length, data.length,
|
|
flags, firstFrameSlot, firstEnvironmentSlot,
|
|
data.trailingNames.start(), data.length);
|
|
}
|
|
|
|
void
|
|
BindingIter::init(ModuleScope::Data& data)
|
|
{
|
|
// imports - [0, data.varStart)
|
|
// positional formals - [data.varStart, data.varStart)
|
|
// other formals - [data.varStart, data.varStart)
|
|
// top-level funcs - [data.varStart, data.varStart)
|
|
// vars - [data.varStart, data.letStart)
|
|
// lets - [data.letStart, data.constStart)
|
|
// consts - [data.constStart, data.length)
|
|
init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart,
|
|
CanHaveFrameSlots | CanHaveEnvironmentSlots,
|
|
0, JSSLOT_FREE(&ModuleEnvironmentObject::class_),
|
|
data.trailingNames.start(), data.length);
|
|
}
|
|
|
|
PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script)
|
|
: BindingIter(script)
|
|
{
|
|
// Reinit with flags = 0, i.e., iterate over all positional parameters.
|
|
if (script->bodyScope()->is<FunctionScope>())
|
|
init(script->bodyScope()->as<FunctionScope>().data(), /* flags = */ 0);
|
|
settle();
|
|
}
|
|
|
|
void
|
|
js::DumpBindings(JSContext* cx, Scope* scopeArg)
|
|
{
|
|
RootedScope scope(cx, scopeArg);
|
|
for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
|
|
JSAutoByteString bytes;
|
|
if (!AtomToPrintableString(cx, bi.name(), &bytes))
|
|
return;
|
|
fprintf(stderr, "%s %s ", BindingKindString(bi.kind()), bytes.ptr());
|
|
switch (bi.location().kind()) {
|
|
case BindingLocation::Kind::Global:
|
|
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>())
|
|
continue;
|
|
LexicalScope& lexicalScope = si.scope()->as<LexicalScope>();
|
|
|
|
// Is the slot within bounds of the current lexical scope?
|
|
if (slot < lexicalScope.firstFrameSlot())
|
|
continue;
|
|
if (slot >= lexicalScope.nextFrameSlot())
|
|
break;
|
|
|
|
// If so, get the name.
|
|
if (JSAtom* name = GetFrameSlotNameInScope(&lexicalScope, 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);
|
|
}
|