Bug 1165486 - Add StaticNonSyntacticScopeObjects and teach scope iterators about it. (r=luke)

This commit is contained in:
Shu-yu Guo 2015-06-15 17:38:01 -07:00
parent c27c964ec1
commit 3c634f08a1
6 changed files with 204 additions and 66 deletions

View File

@ -1548,14 +1548,14 @@ BytecodeEmitter::tryConvertFreeName(ParseNode* pn)
// Use generic ops if a catch block is encountered. // Use generic ops if a catch block is encountered.
return false; return false;
} }
if (ssi.hasDynamicScopeObject()) if (ssi.hasSyntacticDynamicScopeObject())
hops++; hops++;
continue; continue;
} }
RootedScript script(cx, ssi.funScript()); RootedScript script(cx, ssi.funScript());
if (script->functionNonDelazifying()->atom() == pn->pn_atom) if (script->functionNonDelazifying()->atom() == pn->pn_atom)
return false; return false;
if (ssi.hasDynamicScopeObject()) { if (ssi.hasSyntacticDynamicScopeObject()) {
uint32_t slot; uint32_t slot;
if (lookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot, pn)) { if (lookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot, pn)) {
JSOp op; JSOp op;

View File

@ -1177,6 +1177,7 @@ PopScope(JSContext* cx, ScopeIter& si)
break; break;
case ScopeIter::Call: case ScopeIter::Call:
case ScopeIter::Eval: case ScopeIter::Eval:
case ScopeIter::NonSyntactic:
break; break;
} }
} }
@ -3934,7 +3935,7 @@ CASE(JSOP_SUPERBASE)
{ {
ScopeIter si(cx, REGS.fp()->scopeChain(), REGS.fp()->script()->innermostStaticScope(REGS.pc)); ScopeIter si(cx, REGS.fp()->scopeChain(), REGS.fp()->script()->innermostStaticScope(REGS.pc));
for (; !si.done(); ++si) { for (; !si.done(); ++si) {
if (si.hasScopeObject() && si.type() == ScopeIter::Call) { if (si.hasSyntacticScopeObject() && si.type() == ScopeIter::Call) {
JSFunction& callee = si.scope().as<CallObject>().callee(); JSFunction& callee = si.scope().as<CallObject>().callee();
// Arrow functions don't have the information we're looking for, // Arrow functions don't have the information we're looking for,

View File

@ -84,6 +84,8 @@ StaticScopeIter<allowGC>::operator++(int)
obj = obj->template as<NestedScopeObject>().enclosingScopeForStaticScopeIter(); obj = obj->template as<NestedScopeObject>().enclosingScopeForStaticScopeIter();
} else if (obj->template is<StaticEvalObject>()) { } else if (obj->template is<StaticEvalObject>()) {
obj = obj->template as<StaticEvalObject>().enclosingScopeForStaticScopeIter(); obj = obj->template as<StaticEvalObject>().enclosingScopeForStaticScopeIter();
} else if (obj->template is<StaticNonSyntacticScopeObjects>()) {
obj = obj->template as<StaticNonSyntacticScopeObjects>().enclosingScopeForStaticScopeIter();
} else if (onNamedLambda || !obj->template as<JSFunction>().isNamedLambda()) { } else if (onNamedLambda || !obj->template as<JSFunction>().isNamedLambda()) {
onNamedLambda = false; onNamedLambda = false;
obj = obj->template as<JSFunction>().nonLazyScript()->enclosingStaticScope(); obj = obj->template as<JSFunction>().nonLazyScript()->enclosingStaticScope();
@ -92,27 +94,32 @@ StaticScopeIter<allowGC>::operator++(int)
} }
MOZ_ASSERT_IF(obj, obj->template is<NestedScopeObject>() || MOZ_ASSERT_IF(obj, obj->template is<NestedScopeObject>() ||
obj->template is<StaticEvalObject>() || obj->template is<StaticEvalObject>() ||
obj->template is<StaticNonSyntacticScopeObjects>() ||
obj->template is<JSFunction>()); obj->template is<JSFunction>());
MOZ_ASSERT_IF(onNamedLambda, obj->template is<JSFunction>()); MOZ_ASSERT_IF(onNamedLambda, obj->template is<JSFunction>());
} }
template <AllowGC allowGC> template <AllowGC allowGC>
inline bool inline bool
StaticScopeIter<allowGC>::hasDynamicScopeObject() const StaticScopeIter<allowGC>::hasSyntacticDynamicScopeObject() const
{ {
return obj->template is<StaticBlockObject>() if (obj->template is<JSFunction>())
? obj->template as<StaticBlockObject>().needsClone() return obj->template as<JSFunction>().isHeavyweight();
: (obj->template is<StaticEvalObject>() if (obj->template is<StaticBlockObject>())
? obj->template as<StaticEvalObject>().isStrict() return obj->template as<StaticBlockObject>().needsClone();
: (obj->template is<StaticWithObject>() || if (obj->template is<StaticWithObject>())
obj->template as<JSFunction>().isHeavyweight())); return true;
if (obj->template is<StaticEvalObject>())
return obj->template as<StaticEvalObject>().isStrict();
MOZ_ASSERT(obj->template is<StaticNonSyntacticScopeObjects>());
return false;
} }
template <AllowGC allowGC> template <AllowGC allowGC>
inline Shape* inline Shape*
StaticScopeIter<allowGC>::scopeShape() const StaticScopeIter<allowGC>::scopeShape() const
{ {
MOZ_ASSERT(hasDynamicScopeObject()); MOZ_ASSERT(hasSyntacticDynamicScopeObject());
MOZ_ASSERT(type() != NamedLambda && type() != Eval); MOZ_ASSERT(type() != NamedLambda && type() != Eval);
if (type() == Block) if (type() == Block)
return block().lastProperty(); return block().lastProperty();
@ -131,6 +138,8 @@ StaticScopeIter<allowGC>::type() const
? With ? With
: (obj->template is<StaticEvalObject>() : (obj->template is<StaticEvalObject>()
? Eval ? Eval
: (obj->template is<StaticNonSyntacticScopeObjects>())
? NonSyntactic
: Function)); : Function));
} }
@ -158,6 +167,14 @@ StaticScopeIter<allowGC>::eval() const
return obj->template as<StaticEvalObject>(); return obj->template as<StaticEvalObject>();
} }
template <AllowGC allowGC>
inline StaticNonSyntacticScopeObjects&
StaticScopeIter<allowGC>::nonSyntactic() const
{
MOZ_ASSERT(type() == NonSyntactic);
return obj->template as<StaticNonSyntacticScopeObjects>();
}
template <AllowGC allowGC> template <AllowGC allowGC>
inline JSScript* inline JSScript*
StaticScopeIter<allowGC>::funScript() const StaticScopeIter<allowGC>::funScript() const

View File

@ -43,7 +43,7 @@ js::ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc)
uint32_t hops = ScopeCoordinate(pc).hops(); uint32_t hops = ScopeCoordinate(pc).hops();
while (true) { while (true) {
MOZ_ASSERT(!ssi.done()); MOZ_ASSERT(!ssi.done());
if (ssi.hasDynamicScopeObject()) { if (ssi.hasSyntacticDynamicScopeObject()) {
if (!hops) if (!hops)
break; break;
hops--; hops--;
@ -107,7 +107,7 @@ js::ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc)
StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc)); StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc));
uint32_t hops = ScopeCoordinate(pc).hops(); uint32_t hops = ScopeCoordinate(pc).hops();
while (true) { while (true) {
if (ssi.hasDynamicScopeObject()) { if (ssi.hasSyntacticDynamicScopeObject()) {
if (!hops) if (!hops)
break; break;
hops--; hops--;
@ -212,7 +212,7 @@ CallObject::create(JSContext* cx, HandleScript script, HandleObject enclosing, H
if (!callobj) if (!callobj)
return nullptr; return nullptr;
callobj->as<ScopeObject>().setEnclosingScope(enclosing); callobj->setEnclosingScope(enclosing);
callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee)); callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
if (script->treatAsRunOnce()) { if (script->treatAsRunOnce()) {
@ -420,7 +420,7 @@ DynamicWithObject::create(JSContext* cx, HandleObject object, HandleObject enclo
if (!thisp) if (!thisp)
return nullptr; return nullptr;
obj->as<ScopeObject>().setEnclosingScope(enclosing); obj->setEnclosingScope(enclosing);
obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object)); obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp)); obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
obj->setFixedSlot(KIND_SLOT, Int32Value(kind)); obj->setFixedSlot(KIND_SLOT, Int32Value(kind));
@ -551,6 +551,25 @@ const Class StaticEvalObject::class_ = {
JSCLASS_IS_ANONYMOUS JSCLASS_IS_ANONYMOUS
}; };
/* static */ StaticNonSyntacticScopeObjects*
StaticNonSyntacticScopeObjects::create(JSContext*cx, HandleObject enclosing)
{
StaticNonSyntacticScopeObjects* obj =
NewObjectWithNullTaggedProto<StaticNonSyntacticScopeObjects>(cx, TenuredObject,
BaseShape::DELEGATE);
if (!obj)
return nullptr;
obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(enclosing));
return obj;
}
const Class StaticNonSyntacticScopeObjects::class_ = {
"StaticNonSyntacticScopeObjects",
JSCLASS_HAS_RESERVED_SLOTS(StaticNonSyntacticScopeObjects::RESERVED_SLOTS) |
JSCLASS_IS_ANONYMOUS
};
/*****************************************************************************/ /*****************************************************************************/
/* static */ ClonedBlockObject* /* static */ ClonedBlockObject*
@ -845,7 +864,7 @@ UninitializedLexicalObject::create(JSContext* cx, HandleObject enclosing)
BaseShape::DELEGATE); BaseShape::DELEGATE);
if (!obj) if (!obj)
return nullptr; return nullptr;
obj->as<ScopeObject>().setEnclosingScope(enclosing); obj->setEnclosingScope(enclosing);
return obj; return obj;
} }
@ -983,7 +1002,14 @@ ScopeIter::ScopeIter(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc
void void
ScopeIter::incrementStaticScopeIter() ScopeIter::incrementStaticScopeIter()
{ {
ssi_++; // If settled on a non-syntactic static scope, only increment ssi_ once
// we've iterated through all the non-syntactic dynamic ScopeObjects.
if (ssi_.type() == StaticScopeIter<CanGC>::NonSyntactic) {
if (!hasNonSyntacticScopeObject())
ssi_++;
} else {
ssi_++;
}
// For named lambdas, DeclEnvObject scopes are always attached to their // For named lambdas, DeclEnvObject scopes are always attached to their
// CallObjects. Skip it here, as they are special cased in users of // CallObjects. Skip it here, as they are special cased in users of
@ -1010,7 +1036,7 @@ ScopeIter::settle()
frame_ = NullFramePtr(); frame_ = NullFramePtr();
#ifdef DEBUG #ifdef DEBUG
if (!ssi_.done() && hasScopeObject()) { if (!ssi_.done() && hasAnyScopeObject()) {
switch (ssi_.type()) { switch (ssi_.type()) {
case StaticScopeIter<CanGC>::Function: case StaticScopeIter<CanGC>::Function:
MOZ_ASSERT(scope_->as<CallObject>().callee().nonLazyScript() == ssi_.funScript()); MOZ_ASSERT(scope_->as<CallObject>().callee().nonLazyScript() == ssi_.funScript());
@ -1024,6 +1050,9 @@ ScopeIter::settle()
case StaticScopeIter<CanGC>::Eval: case StaticScopeIter<CanGC>::Eval:
MOZ_ASSERT(scope_->as<CallObject>().isForEval()); MOZ_ASSERT(scope_->as<CallObject>().isForEval());
break; break;
case StaticScopeIter<CanGC>::NonSyntactic:
MOZ_ASSERT(!IsSyntacticScope(scope_));
break;
case StaticScopeIter<CanGC>::NamedLambda: case StaticScopeIter<CanGC>::NamedLambda:
MOZ_CRASH("named lambda static scopes should have been skipped"); MOZ_CRASH("named lambda static scopes should have been skipped");
} }
@ -1034,7 +1063,7 @@ ScopeIter::settle()
ScopeIter& ScopeIter&
ScopeIter::operator++() ScopeIter::operator++()
{ {
if (hasScopeObject()) { if (hasAnyScopeObject()) {
scope_ = &scope_->as<ScopeObject>().enclosingScope(); scope_ = &scope_->as<ScopeObject>().enclosingScope();
if (scope_->is<DeclEnvObject>()) if (scope_->is<DeclEnvObject>())
scope_ = &scope_->as<DeclEnvObject>().enclosingScope(); scope_ = &scope_->as<DeclEnvObject>().enclosingScope();
@ -1060,6 +1089,8 @@ ScopeIter::type() const
return With; return With;
case StaticScopeIter<CanGC>::Eval: case StaticScopeIter<CanGC>::Eval:
return Eval; return Eval;
case StaticScopeIter<CanGC>::NonSyntactic:
return NonSyntactic;
case StaticScopeIter<CanGC>::NamedLambda: case StaticScopeIter<CanGC>::NamedLambda:
MOZ_CRASH("named lambda static scopes should have been skipped"); MOZ_CRASH("named lambda static scopes should have been skipped");
default: default:
@ -1070,7 +1101,7 @@ ScopeIter::type() const
ScopeObject& ScopeObject&
ScopeIter::scope() const ScopeIter::scope() const
{ {
MOZ_ASSERT(hasScopeObject()); MOZ_ASSERT(hasAnyScopeObject());
return scope_->as<ScopeObject>(); return scope_->as<ScopeObject>();
} }
@ -1089,6 +1120,8 @@ ScopeIter::maybeStaticScope() const
return &staticWith(); return &staticWith();
case StaticScopeIter<CanGC>::Eval: case StaticScopeIter<CanGC>::Eval:
return &staticEval(); return &staticEval();
case StaticScopeIter<CanGC>::NonSyntactic:
return &staticNonSyntactic();
case StaticScopeIter<CanGC>::NamedLambda: case StaticScopeIter<CanGC>::NamedLambda:
MOZ_CRASH("named lambda static scopes should have been skipped"); MOZ_CRASH("named lambda static scopes should have been skipped");
default: default:
@ -1688,7 +1721,7 @@ const DebugScopeProxy DebugScopeProxy::singleton;
DebugScopeObject::create(JSContext* cx, ScopeObject& scope, HandleObject enclosing) DebugScopeObject::create(JSContext* cx, ScopeObject& scope, HandleObject enclosing)
{ {
MOZ_ASSERT(scope.compartment() == cx->compartment()); MOZ_ASSERT(scope.compartment() == cx->compartment());
MOZ_ASSERT(!IsSyntacticScope(enclosing)); MOZ_ASSERT(!enclosing->is<ScopeObject>());
RootedValue priv(cx, ObjectValue(scope)); RootedValue priv(cx, ObjectValue(scope));
JSObject* obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv, JSObject* obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv,
@ -1950,7 +1983,7 @@ DebugScopes::addDebugScope(JSContext* cx, ScopeObject& scope, DebugScopeObject&
DebugScopeObject* DebugScopeObject*
DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si) DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si)
{ {
MOZ_ASSERT(!si.hasScopeObject()); MOZ_ASSERT(!si.hasSyntacticScopeObject());
DebugScopes* scopes = cx->compartment()->debugScopes; DebugScopes* scopes = cx->compartment()->debugScopes;
if (!scopes) if (!scopes)
@ -1966,7 +1999,7 @@ DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si)
bool bool
DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope) DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope)
{ {
MOZ_ASSERT(!si.hasScopeObject()); MOZ_ASSERT(!si.hasSyntacticScopeObject());
MOZ_ASSERT(cx->compartment() == debugScope.compartment()); MOZ_ASSERT(cx->compartment() == debugScope.compartment());
MOZ_ASSERT_IF(si.withinInitialFrame() && si.initialFrame().isFunctionFrame(), MOZ_ASSERT_IF(si.withinInitialFrame() && si.initialFrame().isFunctionFrame(),
!si.initialFrame().callee()->isGenerator()); !si.initialFrame().callee()->isGenerator());
@ -2184,7 +2217,7 @@ DebugScopes::updateLiveScopes(JSContext* cx)
continue; continue;
for (ScopeIter si(cx, frame, i.pc()); si.withinInitialFrame(); ++si) { for (ScopeIter si(cx, frame, i.pc()); si.withinInitialFrame(); ++si) {
if (si.hasScopeObject()) { if (si.hasSyntacticScopeObject()) {
MOZ_ASSERT(si.scope().compartment() == cx->compartment()); MOZ_ASSERT(si.scope().compartment() == cx->compartment());
DebugScopes* scopes = ensureCompartmentData(cx); DebugScopes* scopes = ensureCompartmentData(cx);
if (!scopes) if (!scopes)
@ -2302,7 +2335,7 @@ GetDebugScopeForScope(JSContext* cx, const ScopeIter& si)
static DebugScopeObject* static DebugScopeObject*
GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si) GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si)
{ {
MOZ_ASSERT(!si.hasScopeObject() && si.canHaveScopeObject()); MOZ_ASSERT(!si.hasSyntacticScopeObject() && si.canHaveSyntacticScopeObject());
if (DebugScopeObject* debugScope = DebugScopes::hasDebugScope(cx, si)) if (DebugScopeObject* debugScope = DebugScopes::hasDebugScope(cx, si))
return debugScope; return debugScope;
@ -2369,6 +2402,8 @@ GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si)
case ScopeIter::With: case ScopeIter::With:
case ScopeIter::Eval: case ScopeIter::Eval:
MOZ_CRASH("should already have a scope"); MOZ_CRASH("should already have a scope");
case ScopeIter::NonSyntactic:
MOZ_CRASH("non-syntactic scopes cannot be synthesized");
} }
if (!debugScope) if (!debugScope)
return nullptr; return nullptr;
@ -2383,11 +2418,11 @@ static JSObject*
GetDebugScopeForNonScopeObject(const ScopeIter& si) GetDebugScopeForNonScopeObject(const ScopeIter& si)
{ {
JSObject& enclosing = si.enclosingScope(); JSObject& enclosing = si.enclosingScope();
MOZ_ASSERT(!IsSyntacticScope(&enclosing)); MOZ_ASSERT(!enclosing.is<ScopeObject>());
#ifdef DEBUG #ifdef DEBUG
JSObject* o = &enclosing; JSObject* o = &enclosing;
while ((o = o->enclosingScope())) while ((o = o->enclosingScope()))
MOZ_ASSERT(!IsSyntacticScope(o)); MOZ_ASSERT(!o->is<ScopeObject>());
#endif #endif
return &enclosing; return &enclosing;
} }
@ -2400,10 +2435,10 @@ GetDebugScope(JSContext* cx, const ScopeIter& si)
if (si.done()) if (si.done())
return GetDebugScopeForNonScopeObject(si); return GetDebugScopeForNonScopeObject(si);
if (si.hasScopeObject()) if (si.hasAnyScopeObject())
return GetDebugScopeForScope(cx, si); return GetDebugScopeForScope(cx, si);
if (si.canHaveScopeObject()) if (si.canHaveSyntacticScopeObject())
return GetDebugScopeForMissing(cx, si); return GetDebugScopeForMissing(cx, si);
ScopeIter copy(cx, si); ScopeIter copy(cx, si);

View File

@ -22,6 +22,7 @@ namespace frontend { struct Definition; }
class StaticWithObject; class StaticWithObject;
class StaticEvalObject; class StaticEvalObject;
class StaticNonSyntacticScopeObjects;
/*****************************************************************************/ /*****************************************************************************/
@ -62,6 +63,7 @@ class StaticScopeIter
obj->is<StaticBlockObject>() || obj->is<StaticBlockObject>() ||
obj->is<StaticWithObject>() || obj->is<StaticWithObject>() ||
obj->is<StaticEvalObject>() || obj->is<StaticEvalObject>() ||
obj->is<StaticNonSyntacticScopeObjects>() ||
obj->is<JSFunction>()); obj->is<JSFunction>());
} }
@ -81,6 +83,7 @@ class StaticScopeIter
obj->is<StaticBlockObject>() || obj->is<StaticBlockObject>() ||
obj->is<StaticWithObject>() || obj->is<StaticWithObject>() ||
obj->is<StaticEvalObject>() || obj->is<StaticEvalObject>() ||
obj->is<StaticNonSyntacticScopeObjects>() ||
obj->is<JSFunction>()); obj->is<JSFunction>());
} }
@ -95,16 +98,19 @@ class StaticScopeIter
bool done() const; bool done() const;
void operator++(int); void operator++(int);
/* Return whether this static scope will be on the dynamic scope chain. */ // Return whether this static scope will have a syntactic scope (i.e. a
bool hasDynamicScopeObject() const; // ScopeObject that isn't a non-syntactic With or
// NonSyntacticVariablesObject) on the dynamic scope chain.
bool hasSyntacticDynamicScopeObject() const;
Shape* scopeShape() const; Shape* scopeShape() const;
enum Type { Function, Block, With, NamedLambda, Eval }; enum Type { Function, Block, With, NamedLambda, Eval, NonSyntactic };
Type type() const; Type type() const;
StaticBlockObject& block() const; StaticBlockObject& block() const;
StaticWithObject& staticWith() const; StaticWithObject& staticWith() const;
StaticEvalObject& eval() const; StaticEvalObject& eval() const;
StaticNonSyntacticScopeObjects& nonSyntactic() const;
JSScript* funScript() const; JSScript* funScript() const;
JSFunction& fun() const; JSFunction& fun() const;
}; };
@ -175,26 +181,28 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc);
* scope objects is: * scope objects is:
* *
* JSObject Generic object * JSObject Generic object
* \ * |
* ScopeObject Engine-internal scope * ScopeObject---+---+ Engine-internal scope
* \ \ \ \ * | | | | |
* \ \ \ StaticEvalObject Placeholder so eval scopes may be iterated through * | | | | StaticNonSyntacticScopeObjects See NB2
* \ \ \ * | | | |
* \ \ DeclEnvObject Holds name of recursive/heavyweight named lambda * | | | StaticEvalObject Placeholder so eval scopes may be iterated through
* \ \ * | | |
* \ CallObject Scope of entire function or strict eval * | | DeclEnvObject Holds name of recursive/heavyweight named lambda
* \ * | |
* NestedScopeObject Scope created for a statement * | CallObject Scope of entire function or strict eval
* \ \ \ * |
* \ \ StaticWithObject Template for "with" object in static scope chain * NestedScopeObject Scope created for a statement
* \ \ * | | |
* \ DynamicWithObject Run-time "with" object on scope chain * | | StaticWithObject Template for "with" object in static scope chain
* \ * | |
* BlockObject Shared interface of cloned/static block objects * | DynamicWithObject Run-time "with" object on scope chain
* \ \ * |
* \ ClonedBlockObject let, switch, catch, for * BlockObject Shared interface of cloned/static block objects
* \ * | |
* StaticBlockObject See NB * | ClonedBlockObject let, switch, catch, for
* |
* StaticBlockObject See NB
* *
* This hierarchy represents more than just the interface hierarchy: reserved * This hierarchy represents more than just the interface hierarchy: reserved
* slots in base classes are fixed for all derived classes. Thus, for example, * slots in base classes are fixed for all derived classes. Thus, for example,
@ -206,6 +214,9 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc);
* are cloned at runtime. These objects should never escape into the wild and * are cloned at runtime. These objects should never escape into the wild and
* support a restricted set of ScopeObject operations. * support a restricted set of ScopeObject operations.
* *
* NB2: StaticNonSyntacticScopeObjects notify either of 0+ non-syntactic
* DynamicWithObjects on the dynamic scope chain or a NonSyntacticScopeObject.
*
* See also "Debug scope objects" below. * See also "Debug scope objects" below.
*/ */
@ -352,8 +363,9 @@ class DeclEnvObject : public ScopeObject
} }
}; };
// Static eval scope template objects on the static scope. Created at the // Static eval scope placeholder objects on the static scope chain. Created at
// time of compiling the eval script, and set as its static enclosing scope. // the time of compiling the eval script, and set as its static enclosing
// scope.
class StaticEvalObject : public ScopeObject class StaticEvalObject : public ScopeObject
{ {
static const uint32_t STRICT_SLOT = 1; static const uint32_t STRICT_SLOT = 1;
@ -383,6 +395,34 @@ class StaticEvalObject : public ScopeObject
} }
}; };
// Static scope objects that stand in for one or more "polluting global"
// scopes on the dynamic scope chain.
//
// There are two flavors of polluting global scopes on the dynamic scope
// chain:
//
// 1. 0+ non-syntactic DynamicWithObjects. This static scope helps ScopeIter
// iterate these DynamicWithObjects.
//
// 2. 1 PlainObject that is a both a QualifiedVarObj and an UnqualifiedVarObj,
// created exclusively in js::ExecuteInGlobalAndReturnScope.
//
// Since this PlainObject is not a ScopeObject, ScopeIter cannot iterate
// through it. Instead, this PlainObject always comes after the syntactic
// portion of the dynamic scope chain in front of a GlobalObject.
class StaticNonSyntacticScopeObjects : public ScopeObject
{
public:
static const unsigned RESERVED_SLOTS = 1;
static const Class class_;
static StaticNonSyntacticScopeObjects* create(JSContext* cx, HandleObject enclosing);
JSObject* enclosingScopeForStaticScopeIter() {
return getReservedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
}
};
class NestedScopeObject : public ScopeObject class NestedScopeObject : public ScopeObject
{ {
public: public:
@ -747,17 +787,20 @@ class ScopeIter
inline JSObject& enclosingScope() const; inline JSObject& enclosingScope() const;
// If !done(): // If !done():
enum Type { Call, Block, With, Eval }; enum Type { Call, Block, With, Eval, NonSyntactic };
Type type() const; Type type() const;
inline bool hasScopeObject() const; inline bool hasNonSyntacticScopeObject() const;
inline bool canHaveScopeObject() const; inline bool hasSyntacticScopeObject() const;
inline bool hasAnyScopeObject() const;
inline bool canHaveSyntacticScopeObject() const;
ScopeObject& scope() const; ScopeObject& scope() const;
JSObject* maybeStaticScope() const; JSObject* maybeStaticScope() const;
StaticBlockObject& staticBlock() const { return ssi_.block(); } StaticBlockObject& staticBlock() const { return ssi_.block(); }
StaticWithObject& staticWith() const { return ssi_.staticWith(); } StaticWithObject& staticWith() const { return ssi_.staticWith(); }
StaticEvalObject& staticEval() const { return ssi_.eval(); } StaticEvalObject& staticEval() const { return ssi_.eval(); }
StaticNonSyntacticScopeObjects& staticNonSyntactic() const { return ssi_.nonSyntactic(); }
JSFunction& fun() const { return ssi_.fun(); } JSFunction& fun() const { return ssi_.fun(); }
bool withinInitialFrame() const { return !!frame_; } bool withinInitialFrame() const { return !!frame_; }
@ -1054,16 +1097,50 @@ ScopeIter::done() const
} }
inline bool inline bool
ScopeIter::hasScopeObject() const ScopeIter::hasSyntacticScopeObject() const
{ {
return ssi_.hasDynamicScopeObject(); return ssi_.hasSyntacticDynamicScopeObject();
} }
inline bool inline bool
ScopeIter::canHaveScopeObject() const ScopeIter::hasNonSyntacticScopeObject() const
{ {
// Non-strict eval scopes cannot have dynamic scope objects. // The case we're worrying about here is a NonSyntactic static scope which
return !ssi_.done() && (type() != Eval || staticEval().isStrict()); // has 0+ corresponding non-syntactic DynamicWithObject scopes or a
// NonSyntacticVariablesObject.
if (ssi_.type() == StaticScopeIter<CanGC>::NonSyntactic) {
MOZ_ASSERT_IF(scope_->is<DynamicWithObject>(),
!scope_->as<DynamicWithObject>().isSyntactic());
return scope_->is<DynamicWithObject>();
}
return false;
}
inline bool
ScopeIter::hasAnyScopeObject() const
{
return hasSyntacticScopeObject() || hasNonSyntacticScopeObject();
}
inline bool
ScopeIter::canHaveSyntacticScopeObject() const
{
if (ssi_.done())
return false;
switch (type()) {
case Call:
return true;
case Block:
return true;
case With:
return true;
case Eval:
// Only strict eval scopes can have dynamic scope objects.
return staticEval().isStrict();
case NonSyntactic:
return false;
}
} }
inline JSObject& inline JSObject&

View File

@ -153,7 +153,12 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject*
#ifdef DEBUG #ifdef DEBUG
RootedObject enclosingScope(cx, script->enclosingStaticScope()); RootedObject enclosingScope(cx, script->enclosingStaticScope());
for (StaticScopeIter<NoGC> i(enclosingScope); !i.done(); i++) { for (StaticScopeIter<NoGC> i(enclosingScope); !i.done(); i++) {
if (i.hasDynamicScopeObject()) { if (i.type() == StaticScopeIter<NoGC>::NonSyntactic) {
while (scope->is<DynamicWithObject>()) {
MOZ_ASSERT(!scope->as<DynamicWithObject>().isSyntactic());
scope = &scope->as<DynamicWithObject>().enclosingScope();
}
} else if (i.hasSyntacticDynamicScopeObject()) {
switch (i.type()) { switch (i.type()) {
case StaticScopeIter<NoGC>::Function: case StaticScopeIter<NoGC>::Function:
MOZ_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript()); MOZ_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript());
@ -173,13 +178,16 @@ AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject*
case StaticScopeIter<NoGC>::Eval: case StaticScopeIter<NoGC>::Eval:
scope = &scope->as<CallObject>().enclosingScope(); scope = &scope->as<CallObject>().enclosingScope();
break; break;
case StaticScopeIter<NoGC>::NonSyntactic:
MOZ_CRASH("NonSyntactic should not have a syntactic scope");
break;
} }
} }
} }
// The scope chain is always ended by one or more non-syntactic // The scope chain is always ended by one or more non-syntactic
// ScopeObjects (viz. GlobalObject or a non-syntactic WithObject). // ScopeObjects (viz. GlobalObject or an unqualified varobj).
MOZ_ASSERT(!IsSyntacticScope(scope)); MOZ_ASSERT(!scope->is<ScopeObject>());
#endif #endif
} }
@ -246,7 +254,7 @@ InterpreterFrame::epilogue(JSContext* cx)
DebugScopes::onPopStrictEvalScope(this); DebugScopes::onPopStrictEvalScope(this);
} else if (isDirectEvalFrame()) { } else if (isDirectEvalFrame()) {
if (isDebuggerEvalFrame()) if (isDebuggerEvalFrame())
MOZ_ASSERT(!IsSyntacticScope(scopeChain())); MOZ_ASSERT(!scopeChain()->is<ScopeObject>());
} else { } else {
/* /*
* Debugger.Object.prototype.evalInGlobal creates indirect eval * Debugger.Object.prototype.evalInGlobal creates indirect eval