☠☠ backed out by bfc988bd3c77 ☠ ☠ | |
author | Shu-yu Guo <shu@rfrn.org> |
Fri, 19 Jun 2015 01:21:13 -0700 | |
changeset 249749 | 6318eba2d3fe5df3d10eb46658677ddab7ea4121 |
parent 249748 | a103caa361833549bb9a868301fb614863439bfa |
child 249750 | 17d21020a7864e8612f976737731b98e875a7115 |
push id | 28936 |
push user | ryanvm@gmail.com |
push date | Fri, 19 Jun 2015 20:34:42 +0000 |
treeherder | mozilla-central@c319f262ce3e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | luke |
bugs | 1165486 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1543,24 +1543,24 @@ BytecodeEmitter::tryConvertFreeName(Pars return false; RootedObject outerScope(cx, script->enclosingStaticScope()); for (StaticScopeIter<CanGC> ssi(cx, outerScope); !ssi.done(); ssi++) { if (ssi.type() != StaticScopeIter<CanGC>::Function) { if (ssi.type() == StaticScopeIter<CanGC>::Block) { // Use generic ops if a catch block is encountered. return false; } - if (ssi.hasDynamicScopeObject()) + if (ssi.hasSyntacticDynamicScopeObject()) hops++; continue; } RootedScript script(cx, ssi.funScript()); if (script->functionNonDelazifying()->atom() == pn->pn_atom) return false; - if (ssi.hasDynamicScopeObject()) { + if (ssi.hasSyntacticDynamicScopeObject()) { uint32_t slot; if (lookupAliasedName(script, pn->pn_atom->asPropertyName(), &slot, pn)) { JSOp op; switch (pn->getOp()) { case JSOP_GETNAME: op = JSOP_GETALIASEDVAR; break; case JSOP_SETNAME: op = JSOP_SETALIASEDVAR; break; default: return false; }
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1177,16 +1177,17 @@ PopScope(JSContext* cx, ScopeIter& si) if (si.staticBlock().needsClone()) si.initialFrame().popBlock(cx); break; case ScopeIter::With: si.initialFrame().popWith(cx); break; case ScopeIter::Call: case ScopeIter::Eval: + case ScopeIter::NonSyntactic: break; } } // Unwind scope chain and iterator to match the static scope corresponding to // the given bytecode position. void js::UnwindScope(JSContext* cx, ScopeIter& si, jsbytecode* pc) @@ -3936,17 +3937,17 @@ CASE(JSOP_INITHOMEOBJECT) func->setExtendedSlot(FunctionExtended::METHOD_HOMEOBJECT_SLOT, ObjectValue(*obj)); } END_CASE(JSOP_INITHOMEOBJECT) CASE(JSOP_SUPERBASE) { ScopeIter si(cx, REGS.fp()->scopeChain(), REGS.fp()->script()->innermostStaticScope(REGS.pc)); 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(); // Arrow functions don't have the information we're looking for, // their enclosing scopes do. Nevertheless, they might have call // objects. Skip them to find what we came for. if (callee.isArrow()) continue;
--- a/js/src/vm/ScopeObject-inl.h +++ b/js/src/vm/ScopeObject-inl.h @@ -79,45 +79,52 @@ StaticScopeIter<allowGC>::done() const template <AllowGC allowGC> inline void StaticScopeIter<allowGC>::operator++(int) { if (obj->template is<NestedScopeObject>()) { obj = obj->template as<NestedScopeObject>().enclosingScopeForStaticScopeIter(); } else if (obj->template is<StaticEvalObject>()) { 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()) { onNamedLambda = false; obj = obj->template as<JSFunction>().nonLazyScript()->enclosingStaticScope(); } else { onNamedLambda = true; } MOZ_ASSERT_IF(obj, obj->template is<NestedScopeObject>() || obj->template is<StaticEvalObject>() || + obj->template is<StaticNonSyntacticScopeObjects>() || obj->template is<JSFunction>()); MOZ_ASSERT_IF(onNamedLambda, obj->template is<JSFunction>()); } template <AllowGC allowGC> inline bool -StaticScopeIter<allowGC>::hasDynamicScopeObject() const +StaticScopeIter<allowGC>::hasSyntacticDynamicScopeObject() const { - return obj->template is<StaticBlockObject>() - ? obj->template as<StaticBlockObject>().needsClone() - : (obj->template is<StaticEvalObject>() - ? obj->template as<StaticEvalObject>().isStrict() - : (obj->template is<StaticWithObject>() || - obj->template as<JSFunction>().isHeavyweight())); + if (obj->template is<JSFunction>()) + return obj->template as<JSFunction>().isHeavyweight(); + if (obj->template is<StaticBlockObject>()) + return obj->template as<StaticBlockObject>().needsClone(); + if (obj->template is<StaticWithObject>()) + return true; + if (obj->template is<StaticEvalObject>()) + return obj->template as<StaticEvalObject>().isStrict(); + MOZ_ASSERT(obj->template is<StaticNonSyntacticScopeObjects>()); + return false; } template <AllowGC allowGC> inline Shape* StaticScopeIter<allowGC>::scopeShape() const { - MOZ_ASSERT(hasDynamicScopeObject()); + MOZ_ASSERT(hasSyntacticDynamicScopeObject()); MOZ_ASSERT(type() != NamedLambda && type() != Eval); if (type() == Block) return block().lastProperty(); return funScript()->callObjShape(); } template <AllowGC allowGC> inline typename StaticScopeIter<allowGC>::Type @@ -126,16 +133,18 @@ StaticScopeIter<allowGC>::type() const if (onNamedLambda) return NamedLambda; return obj->template is<StaticBlockObject>() ? Block : (obj->template is<StaticWithObject>() ? With : (obj->template is<StaticEvalObject>() ? Eval + : (obj->template is<StaticNonSyntacticScopeObjects>()) + ? NonSyntactic : Function)); } template <AllowGC allowGC> inline StaticBlockObject& StaticScopeIter<allowGC>::block() const { MOZ_ASSERT(type() == Block); @@ -154,16 +163,24 @@ template <AllowGC allowGC> inline StaticEvalObject& StaticScopeIter<allowGC>::eval() const { MOZ_ASSERT(type() == Eval); 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> inline JSScript* StaticScopeIter<allowGC>::funScript() const { MOZ_ASSERT(type() == Function); return obj->template as<JSFunction>().nonLazyScript(); } template <AllowGC allowGC>
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -38,17 +38,17 @@ typedef MutableHandle<ArgumentsObject*> Shape* js::ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc) { MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD); StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc)); uint32_t hops = ScopeCoordinate(pc).hops(); while (true) { MOZ_ASSERT(!ssi.done()); - if (ssi.hasDynamicScopeObject()) { + if (ssi.hasSyntacticDynamicScopeObject()) { if (!hops) break; hops--; } ssi++; } return ssi.scopeShape(); } @@ -102,17 +102,17 @@ js::ScopeCoordinateName(ScopeCoordinateN JSScript* js::ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc) { MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD); StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc)); uint32_t hops = ScopeCoordinate(pc).hops(); while (true) { - if (ssi.hasDynamicScopeObject()) { + if (ssi.hasSyntacticDynamicScopeObject()) { if (!hops) break; hops--; } ssi++; } if (ssi.type() != StaticScopeIter<NoGC>::Function) return nullptr; @@ -207,17 +207,17 @@ CallObject::createTemplateObject(JSConte CallObject* CallObject::create(JSContext* cx, HandleScript script, HandleObject enclosing, HandleFunction callee) { gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap; CallObject* callobj = CallObject::createTemplateObject(cx, script, heap); if (!callobj) return nullptr; - callobj->as<ScopeObject>().setEnclosingScope(enclosing); + callobj->setEnclosingScope(enclosing); callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee)); if (script->treatAsRunOnce()) { Rooted<CallObject*> ncallobj(cx, callobj); if (!JSObject::setSingleton(cx, ncallobj)) return nullptr; return ncallobj; } @@ -415,17 +415,17 @@ DynamicWithObject::create(JSContext* cx, BaseShape::DELEGATE); if (!obj) return nullptr; JSObject* thisp = GetThisObject(cx, object); if (!thisp) return nullptr; - obj->as<ScopeObject>().setEnclosingScope(enclosing); + obj->setEnclosingScope(enclosing); obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object)); obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp)); obj->setFixedSlot(KIND_SLOT, Int32Value(kind)); return obj; } static bool @@ -546,16 +546,35 @@ StaticEvalObject::create(JSContext* cx, } const Class StaticEvalObject::class_ = { "StaticEval", JSCLASS_HAS_RESERVED_SLOTS(StaticEvalObject::RESERVED_SLOTS) | 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* ClonedBlockObject::create(JSContext* cx, Handle<StaticBlockObject*> block, HandleObject enclosing) { MOZ_ASSERT(block->getClass() == &BlockObject::class_); RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &BlockObject::class_, @@ -840,17 +859,17 @@ js::CloneNestedScopeObject(JSContext* cx /* static */ UninitializedLexicalObject* UninitializedLexicalObject::create(JSContext* cx, HandleObject enclosing) { UninitializedLexicalObject* obj = NewObjectWithNullTaggedProto<UninitializedLexicalObject>(cx, GenericObject, BaseShape::DELEGATE); if (!obj) return nullptr; - obj->as<ScopeObject>().setEnclosingScope(enclosing); + obj->setEnclosingScope(enclosing); return obj; } static void ReportUninitializedLexicalId(JSContext* cx, HandleId id) { if (JSID_IS_ATOM(id)) { RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName()); @@ -978,17 +997,24 @@ ScopeIter::ScopeIter(JSContext* cx, Abst assertSameCompartment(cx, frame); settle(); MOZ_GUARD_OBJECT_NOTIFIER_INIT; } void 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 // CallObjects. Skip it here, as they are special cased in users of // ScopeIter. if (!ssi_.done() && ssi_.type() == StaticScopeIter<CanGC>::NamedLambda) ssi_++; } @@ -1005,41 +1031,44 @@ ScopeIter::settle() } // Check if we have left the extent of the initial frame after we've // settled on a static scope. if (frame_ && (ssi_.done() || maybeStaticScope() == frame_.script()->enclosingStaticScope())) frame_ = NullFramePtr(); #ifdef DEBUG - if (!ssi_.done() && hasScopeObject()) { + if (!ssi_.done() && hasAnyScopeObject()) { switch (ssi_.type()) { case StaticScopeIter<CanGC>::Function: MOZ_ASSERT(scope_->as<CallObject>().callee().nonLazyScript() == ssi_.funScript()); break; case StaticScopeIter<CanGC>::Block: MOZ_ASSERT(scope_->as<ClonedBlockObject>().staticBlock() == staticBlock()); break; case StaticScopeIter<CanGC>::With: MOZ_ASSERT(scope_->as<DynamicWithObject>().staticScope() == &staticWith()); break; case StaticScopeIter<CanGC>::Eval: MOZ_ASSERT(scope_->as<CallObject>().isForEval()); break; + case StaticScopeIter<CanGC>::NonSyntactic: + MOZ_ASSERT(!IsSyntacticScope(scope_)); + break; case StaticScopeIter<CanGC>::NamedLambda: MOZ_CRASH("named lambda static scopes should have been skipped"); } } #endif } ScopeIter& ScopeIter::operator++() { - if (hasScopeObject()) { + if (hasAnyScopeObject()) { scope_ = &scope_->as<ScopeObject>().enclosingScope(); if (scope_->is<DeclEnvObject>()) scope_ = &scope_->as<DeclEnvObject>().enclosingScope(); } incrementStaticScopeIter(); settle(); @@ -1055,27 +1084,29 @@ ScopeIter::type() const case StaticScopeIter<CanGC>::Function: return Call; case StaticScopeIter<CanGC>::Block: return Block; case StaticScopeIter<CanGC>::With: return With; case StaticScopeIter<CanGC>::Eval: return Eval; + case StaticScopeIter<CanGC>::NonSyntactic: + return NonSyntactic; case StaticScopeIter<CanGC>::NamedLambda: MOZ_CRASH("named lambda static scopes should have been skipped"); default: MOZ_CRASH("bad SSI type"); } } ScopeObject& ScopeIter::scope() const { - MOZ_ASSERT(hasScopeObject()); + MOZ_ASSERT(hasAnyScopeObject()); return scope_->as<ScopeObject>(); } JSObject* ScopeIter::maybeStaticScope() const { if (ssi_.done()) return nullptr; @@ -1084,16 +1115,18 @@ ScopeIter::maybeStaticScope() const case StaticScopeIter<CanGC>::Function: return &fun(); case StaticScopeIter<CanGC>::Block: return &staticBlock(); case StaticScopeIter<CanGC>::With: return &staticWith(); case StaticScopeIter<CanGC>::Eval: return &staticEval(); + case StaticScopeIter<CanGC>::NonSyntactic: + return &staticNonSyntactic(); case StaticScopeIter<CanGC>::NamedLambda: MOZ_CRASH("named lambda static scopes should have been skipped"); default: MOZ_CRASH("bad SSI type"); } } /* static */ HashNumber @@ -1683,17 +1716,17 @@ class DebugScopeProxy : public BaseProxy const char DebugScopeProxy::family = 0; const DebugScopeProxy DebugScopeProxy::singleton; /* static */ DebugScopeObject* DebugScopeObject::create(JSContext* cx, ScopeObject& scope, HandleObject enclosing) { MOZ_ASSERT(scope.compartment() == cx->compartment()); - MOZ_ASSERT(!IsSyntacticScope(enclosing)); + MOZ_ASSERT(!enclosing->is<ScopeObject>()); RootedValue priv(cx, ObjectValue(scope)); JSObject* obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv, nullptr /* proto */); if (!obj) return nullptr; DebugScopeObject* debugScope = &obj->as<DebugScopeObject>(); @@ -1945,33 +1978,33 @@ DebugScopes::addDebugScope(JSContext* cx return false; return scopes->proxiedScopes.add(cx, &scope, &debugScope); } DebugScopeObject* DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si) { - MOZ_ASSERT(!si.hasScopeObject()); + MOZ_ASSERT(!si.hasSyntacticScopeObject()); DebugScopes* scopes = cx->compartment()->debugScopes; if (!scopes) return nullptr; if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(MissingScopeKey(si))) { MOZ_ASSERT(CanUseDebugScopeMaps(cx)); return p->value(); } return nullptr; } bool 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_IF(si.withinInitialFrame() && si.initialFrame().isFunctionFrame(), !si.initialFrame().callee()->isGenerator()); // Generators should always reify their scopes. MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().isGenerator()); if (!CanUseDebugScopeMaps(cx)) return true; @@ -2179,17 +2212,17 @@ DebugScopes::updateLiveScopes(JSContext* if (frame.isFunctionFrame() && frame.callee()->isGenerator()) continue; if (!frame.isDebuggee()) continue; for (ScopeIter si(cx, frame, i.pc()); si.withinInitialFrame(); ++si) { - if (si.hasScopeObject()) { + if (si.hasSyntacticScopeObject()) { MOZ_ASSERT(si.scope().compartment() == cx->compartment()); DebugScopes* scopes = ensureCompartmentData(cx); if (!scopes) return false; if (!scopes->liveScopes.put(&si.scope(), LiveScopeVal(si))) return false; liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &si.scope()); } @@ -2297,17 +2330,17 @@ GetDebugScopeForScope(JSContext* cx, con return nullptr; return debugScope; } static DebugScopeObject* 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)) return debugScope; ScopeIter copy(cx, si); RootedObject enclosingDebug(cx, GetDebugScope(cx, ++copy)); if (!enclosingDebug) return nullptr; @@ -2364,51 +2397,53 @@ GetDebugScopeForMissing(JSContext* cx, c return nullptr; debugScope = DebugScopeObject::create(cx, *block, enclosingDebug); break; } case ScopeIter::With: case ScopeIter::Eval: MOZ_CRASH("should already have a scope"); + case ScopeIter::NonSyntactic: + MOZ_CRASH("non-syntactic scopes cannot be synthesized"); } if (!debugScope) return nullptr; if (!DebugScopes::addDebugScope(cx, si, *debugScope)) return nullptr; return debugScope; } static JSObject* GetDebugScopeForNonScopeObject(const ScopeIter& si) { JSObject& enclosing = si.enclosingScope(); - MOZ_ASSERT(!IsSyntacticScope(&enclosing)); + MOZ_ASSERT(!enclosing.is<ScopeObject>()); #ifdef DEBUG JSObject* o = &enclosing; while ((o = o->enclosingScope())) - MOZ_ASSERT(!IsSyntacticScope(o)); + MOZ_ASSERT(!o->is<ScopeObject>()); #endif return &enclosing; } static JSObject* GetDebugScope(JSContext* cx, const ScopeIter& si) { JS_CHECK_RECURSION(cx, return nullptr); if (si.done()) return GetDebugScopeForNonScopeObject(si); - if (si.hasScopeObject()) + if (si.hasAnyScopeObject()) return GetDebugScopeForScope(cx, si); - if (si.canHaveScopeObject()) + if (si.canHaveSyntacticScopeObject()) return GetDebugScopeForMissing(cx, si); ScopeIter copy(cx, si); return GetDebugScope(cx, ++copy); } JSObject* js::GetDebugScopeForFunction(JSContext* cx, HandleFunction fun)
--- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -17,16 +17,17 @@ #include "vm/WeakMapObject.h" namespace js { namespace frontend { struct Definition; } class StaticWithObject; class StaticEvalObject; +class StaticNonSyntacticScopeObjects; /*****************************************************************************/ /* * All function scripts have an "enclosing static scope" that refers to the * innermost enclosing let or function in the program text. This allows full * reconstruction of the lexical scope for debugging or compiling efficient * access to variables in enclosing scopes. The static scope is represented at @@ -57,16 +58,17 @@ class StaticScopeIter { static_assert(allowGC == CanGC, "the context-accepting constructor should only be used " "in CanGC code"); MOZ_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<StaticWithObject>() || obj->is<StaticEvalObject>() || + obj->is<StaticNonSyntacticScopeObjects>() || obj->is<JSFunction>()); } StaticScopeIter(ExclusiveContext* cx, const StaticScopeIter<CanGC>& ssi) : obj(cx, ssi.obj), onNamedLambda(ssi.onNamedLambda) { JS_STATIC_ASSERT(allowGC == CanGC); } @@ -76,40 +78,44 @@ class StaticScopeIter { static_assert(allowGC == NoGC, "the constructor not taking a context should only be " "used in NoGC code"); MOZ_ASSERT_IF(obj, obj->is<StaticBlockObject>() || obj->is<StaticWithObject>() || obj->is<StaticEvalObject>() || + obj->is<StaticNonSyntacticScopeObjects>() || obj->is<JSFunction>()); } explicit StaticScopeIter(const StaticScopeIter<NoGC>& ssi) : obj((ExclusiveContext*) nullptr, ssi.obj), onNamedLambda(ssi.onNamedLambda) { static_assert(allowGC == NoGC, "the constructor not taking a context should only be " "used in NoGC code"); } bool done() const; void operator++(int); - /* Return whether this static scope will be on the dynamic scope chain. */ - bool hasDynamicScopeObject() const; + // Return whether this static scope will have a syntactic scope (i.e. a + // ScopeObject that isn't a non-syntactic With or + // NonSyntacticVariablesObject) on the dynamic scope chain. + bool hasSyntacticDynamicScopeObject() const; Shape* scopeShape() const; - enum Type { Function, Block, With, NamedLambda, Eval }; + enum Type { Function, Block, With, NamedLambda, Eval, NonSyntactic }; Type type() const; StaticBlockObject& block() const; StaticWithObject& staticWith() const; StaticEvalObject& eval() const; + StaticNonSyntacticScopeObjects& nonSyntactic() const; JSScript* funScript() const; JSFunction& fun() const; }; /*****************************************************************************/ /* * A "scope coordinate" describes how to get from head of the scope chain to a @@ -170,47 +176,52 @@ ScopeCoordinateFunctionScript(JSScript* /* * Scope objects * * Scope objects are technically real JSObjects but only belong on the scope * chain (that is, fp->scopeChain() or fun->environment()). The hierarchy of * scope objects is: * * JSObject Generic object - * \ - * ScopeObject Engine-internal scope - * \ \ \ \ - * \ \ \ StaticEvalObject Placeholder so eval scopes may be iterated through - * \ \ \ - * \ \ DeclEnvObject Holds name of recursive/heavyweight named lambda - * \ \ - * \ CallObject Scope of entire function or strict eval - * \ - * NestedScopeObject Scope created for a statement - * \ \ \ - * \ \ StaticWithObject Template for "with" object in static scope chain - * \ \ - * \ DynamicWithObject Run-time "with" object on scope chain - * \ - * BlockObject Shared interface of cloned/static block objects - * \ \ - * \ ClonedBlockObject let, switch, catch, for - * \ - * StaticBlockObject See NB + * | + * ScopeObject---+---+ Engine-internal scope + * | | | | | + * | | | | StaticNonSyntacticScopeObjects See NB2 + * | | | | + * | | | StaticEvalObject Placeholder so eval scopes may be iterated through + * | | | + * | | DeclEnvObject Holds name of recursive/heavyweight named lambda + * | | + * | CallObject Scope of entire function or strict eval + * | + * NestedScopeObject Scope created for a statement + * | | | + * | | StaticWithObject Template for "with" object in static scope chain + * | | + * | DynamicWithObject Run-time "with" object on scope chain + * | + * BlockObject Shared interface of cloned/static block objects + * | | + * | ClonedBlockObject let, switch, catch, for + * | + * StaticBlockObject See NB * * This hierarchy represents more than just the interface hierarchy: reserved * slots in base classes are fixed for all derived classes. Thus, for example, * ScopeObject::enclosingScope() can simply access a fixed slot without further * dynamic type information. * * NB: Static block objects are a special case: these objects are created at * compile time to hold the shape/binding information from which block objects * are cloned at runtime. These objects should never escape into the wild and * 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. */ class ScopeObject : public NativeObject { protected: static const uint32_t SCOPE_CHAIN_SLOT = 0; @@ -347,18 +358,19 @@ class DeclEnvObject : public ScopeObject static DeclEnvObject* create(JSContext* cx, HandleObject enclosing, HandleFunction callee); static inline size_t lambdaSlot() { return LAMBDA_SLOT; } }; -// Static eval scope template objects on the static scope. Created at the -// time of compiling the eval script, and set as its static enclosing scope. +// Static eval scope placeholder objects on the static scope chain. Created at +// the time of compiling the eval script, and set as its static enclosing +// scope. class StaticEvalObject : public ScopeObject { static const uint32_t STRICT_SLOT = 1; public: static const unsigned RESERVED_SLOTS = 2; static const Class class_; @@ -378,16 +390,44 @@ class StaticEvalObject : public ScopeObj // Indirect evals terminate in the global at run time, and has no static // enclosing scope. bool isDirect() const { return getReservedSlot(SCOPE_CHAIN_SLOT).isObject(); } }; +// 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 { public: /* * A refinement of enclosingScope that returns nullptr if the enclosing * scope is not a NestedScopeObject. */ inline NestedScopeObject* enclosingNestedScope() const; @@ -742,27 +782,30 @@ class ScopeIter inline bool done() const; ScopeIter& operator++(); // If done(): inline JSObject& enclosingScope() const; // If !done(): - enum Type { Call, Block, With, Eval }; + enum Type { Call, Block, With, Eval, NonSyntactic }; Type type() const; - inline bool hasScopeObject() const; - inline bool canHaveScopeObject() const; + inline bool hasNonSyntacticScopeObject() const; + inline bool hasSyntacticScopeObject() const; + inline bool hasAnyScopeObject() const; + inline bool canHaveSyntacticScopeObject() const; ScopeObject& scope() const; JSObject* maybeStaticScope() const; StaticBlockObject& staticBlock() const { return ssi_.block(); } StaticWithObject& staticWith() const { return ssi_.staticWith(); } StaticEvalObject& staticEval() const { return ssi_.eval(); } + StaticNonSyntacticScopeObjects& staticNonSyntactic() const { return ssi_.nonSyntactic(); } JSFunction& fun() const { return ssi_.fun(); } bool withinInitialFrame() const { return !!frame_; } AbstractFramePtr initialFrame() const { MOZ_ASSERT(withinInitialFrame()); return frame_; } AbstractFramePtr maybeInitialFrame() const { return frame_; } MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; @@ -1049,26 +1092,60 @@ NestedScopeObject::enclosingNestedScope( inline bool ScopeIter::done() const { return ssi_.done(); } inline bool -ScopeIter::hasScopeObject() const +ScopeIter::hasSyntacticScopeObject() const +{ + return ssi_.hasSyntacticDynamicScopeObject(); +} + +inline bool +ScopeIter::hasNonSyntacticScopeObject() const { - return ssi_.hasDynamicScopeObject(); + // The case we're worrying about here is a NonSyntactic static scope which + // 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::canHaveScopeObject() const +ScopeIter::hasAnyScopeObject() const +{ + return hasSyntacticScopeObject() || hasNonSyntacticScopeObject(); +} + +inline bool +ScopeIter::canHaveSyntacticScopeObject() const { - // Non-strict eval scopes cannot have dynamic scope objects. - return !ssi_.done() && (type() != Eval || staticEval().isStrict()); + 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& ScopeIter::enclosingScope() const { // As an engine invariant (maintained internally and asserted by Execute), // ScopeObjects and non-ScopeObjects cannot be interleaved on the scope // chain; every scope chain must start with zero or more ScopeObjects and
--- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -148,17 +148,22 @@ InterpreterFrame::createRestParameter(JS } static inline void AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* scope) { #ifdef DEBUG RootedObject enclosingScope(cx, script->enclosingStaticScope()); 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()) { case StaticScopeIter<NoGC>::Function: MOZ_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript()); scope = &scope->as<CallObject>().enclosingScope(); break; case StaticScopeIter<NoGC>::Block: MOZ_ASSERT(&i.block() == scope->as<ClonedBlockObject>().staticScope()); scope = &scope->as<ClonedBlockObject>().enclosingScope(); @@ -168,23 +173,26 @@ AssertDynamicScopeMatchesStaticScope(JSC scope = &scope->as<DynamicWithObject>().enclosingScope(); break; case StaticScopeIter<NoGC>::NamedLambda: scope = &scope->as<DeclEnvObject>().enclosingScope(); break; case StaticScopeIter<NoGC>::Eval: scope = &scope->as<CallObject>().enclosingScope(); 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 - // ScopeObjects (viz. GlobalObject or a non-syntactic WithObject). - MOZ_ASSERT(!IsSyntacticScope(scope)); + // ScopeObjects (viz. GlobalObject or an unqualified varobj). + MOZ_ASSERT(!scope->is<ScopeObject>()); #endif } bool InterpreterFrame::initFunctionScopeObjects(JSContext* cx) { CallObject* callobj = CallObject::createForFunction(cx, this); if (!callobj) @@ -241,17 +249,17 @@ InterpreterFrame::epilogue(JSContext* cx if (isEvalFrame()) { if (isStrictEvalFrame()) { MOZ_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval()); if (MOZ_UNLIKELY(cx->compartment()->isDebuggee())) DebugScopes::onPopStrictEvalScope(this); } else if (isDirectEvalFrame()) { if (isDebuggerEvalFrame()) - MOZ_ASSERT(!IsSyntacticScope(scopeChain())); + MOZ_ASSERT(!scopeChain()->is<ScopeObject>()); } else { /* * Debugger.Object.prototype.evalInGlobal creates indirect eval * frames scoped to the given global; * Debugger.Object.prototype.evalInGlobalWithBindings creates * indirect eval frames scoped to an object carrying the introduced * bindings. */