1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "vm/ScopeObject-inl.h"
9 #include "mozilla/PodOperations.h"
11 #include "jscompartment.h"
14 #include "vm/ArgumentsObject.h"
15 #include "vm/GlobalObject.h"
16 #include "vm/ProxyObject.h"
20 #include "jsatominlines.h"
21 #include "jsobjinlines.h"
22 #include "jsscriptinlines.h"
24 #include "vm/Stack-inl.h"
27 using namespace js::types
;
29 using mozilla::PodZero
;
31 typedef Rooted
<ArgumentsObject
*> RootedArgumentsObject
;
33 /*****************************************************************************/
36 InnermostStaticScope(JSScript
*script
, jsbytecode
*pc
)
38 JS_ASSERT(script
->containsPC(pc
));
39 JS_ASSERT(JOF_OPTYPE(*pc
) == JOF_SCOPECOORD
);
41 uint32_t blockIndex
= GET_UINT32_INDEX(pc
+ 2 * sizeof(uint16_t));
43 if (blockIndex
== UINT32_MAX
)
44 return script
->function();
45 return &script
->getObject(blockIndex
)->as
<StaticBlockObject
>();
49 js::ScopeCoordinateToStaticScopeShape(JSScript
*script
, jsbytecode
*pc
)
51 StaticScopeIter
<NoGC
> ssi(InnermostStaticScope(script
, pc
));
52 ScopeCoordinate
sc(pc
);
54 if (ssi
.hasDynamicScopeObject()) {
61 return ssi
.scopeShape();
64 static const uint32_t SCOPE_COORDINATE_NAME_THRESHOLD
= 20;
67 ScopeCoordinateNameCache::purge()
70 if (map
.initialized())
75 js::ScopeCoordinateName(ScopeCoordinateNameCache
&cache
, JSScript
*script
, jsbytecode
*pc
)
77 Shape
*shape
= ScopeCoordinateToStaticScopeShape(script
, pc
);
78 if (shape
!= cache
.shape
&& shape
->slot() >= SCOPE_COORDINATE_NAME_THRESHOLD
) {
80 if (cache
.map
.init(shape
->slot())) {
82 Shape::Range
<NoGC
> r(shape
);
84 if (!cache
.map
.putNew(r
.front().slot(), r
.front().propid())) {
94 ScopeCoordinate
sc(pc
);
95 if (shape
== cache
.shape
) {
96 ScopeCoordinateNameCache::Map::Ptr p
= cache
.map
.lookup(sc
.slot
);
99 Shape::Range
<NoGC
> r(shape
);
100 while (r
.front().slot() != sc
.slot
)
102 id
= r
.front().propid();
105 /* Beware nameless destructuring formal. */
106 if (!JSID_IS_ATOM(id
))
107 return script
->runtimeFromAnyThread()->atomState
.empty
;
108 return JSID_TO_ATOM(id
)->asPropertyName();
112 js::ScopeCoordinateFunctionScript(JSScript
*script
, jsbytecode
*pc
)
114 StaticScopeIter
<NoGC
> ssi(InnermostStaticScope(script
, pc
));
115 ScopeCoordinate
sc(pc
);
117 if (ssi
.hasDynamicScopeObject()) {
124 if (ssi
.type() != StaticScopeIter
<NoGC
>::FUNCTION
)
126 return ssi
.funScript();
129 /*****************************************************************************/
132 ScopeObject::setEnclosingScope(HandleObject obj
)
134 JS_ASSERT_IF(obj
->is
<CallObject
>() || obj
->is
<DeclEnvObject
>() || obj
->is
<BlockObject
>(),
136 setFixedSlot(SCOPE_CHAIN_SLOT
, ObjectValue(*obj
));
140 * Construct a bare-bones call object given a shape, type, and slots pointer.
141 * The call object must be further initialized to be usable.
144 CallObject::create(JSContext
*cx
, HandleScript script
, HandleShape shape
, HandleTypeObject type
, HeapSlot
*slots
)
146 gc::AllocKind kind
= gc::GetGCObjectKind(shape
->numFixedSlots());
147 JS_ASSERT(CanBeFinalizedInBackground(kind
, &CallObject::class_
));
148 kind
= gc::GetBackgroundAllocKind(kind
);
150 gc::InitialHeap heap
= script
->treatAsRunOnce
? gc::TenuredHeap
: gc::DefaultHeap
;
151 JSObject
*obj
= JSObject::create(cx
, kind
, heap
, shape
, type
, slots
);
155 if (script
->treatAsRunOnce
) {
156 RootedObject
nobj(cx
, obj
);
157 if (!JSObject::setSingletonType(cx
, nobj
))
159 return &nobj
->as
<CallObject
>();
162 return &obj
->as
<CallObject
>();
166 * Create a CallObject for a JSScript that is not initialized to any particular
167 * callsite. This object can either be initialized (with an enclosing scope and
168 * callee) or used as a template for jit compilation.
171 CallObject::createTemplateObject(JSContext
*cx
, HandleScript script
, gc::InitialHeap heap
)
173 RootedShape
shape(cx
, script
->bindings
.callObjShape());
174 JS_ASSERT(shape
->getObjectClass() == &class_
);
176 RootedTypeObject
type(cx
, cx
->getNewType(&class_
, nullptr));
180 gc::AllocKind kind
= gc::GetGCObjectKind(shape
->numFixedSlots());
181 JS_ASSERT(CanBeFinalizedInBackground(kind
, &class_
));
182 kind
= gc::GetBackgroundAllocKind(kind
);
184 JSObject
*obj
= JSObject::create(cx
, kind
, heap
, shape
, type
);
188 return &obj
->as
<CallObject
>();
192 * Construct a call object for the given bindings. If this is a call object
193 * for a function invocation, callee should be the function being called.
194 * Otherwise it must be a call object for eval of strict mode code, and callee
198 CallObject::create(JSContext
*cx
, HandleScript script
, HandleObject enclosing
, HandleFunction callee
)
200 gc::InitialHeap heap
= script
->treatAsRunOnce
? gc::TenuredHeap
: gc::DefaultHeap
;
201 CallObject
*callobj
= CallObject::createTemplateObject(cx
, script
, heap
);
205 callobj
->as
<ScopeObject
>().setEnclosingScope(enclosing
);
206 callobj
->initFixedSlot(CALLEE_SLOT
, ObjectOrNullValue(callee
));
208 if (script
->treatAsRunOnce
) {
209 Rooted
<CallObject
*> ncallobj(cx
, callobj
);
210 if (!JSObject::setSingletonType(cx
, ncallobj
))
219 CallObject::createForFunction(JSContext
*cx
, HandleObject enclosing
, HandleFunction callee
)
221 RootedObject
scopeChain(cx
, enclosing
);
222 JS_ASSERT(scopeChain
);
225 * For a named function expression Call's parent points to an environment
226 * object holding function's name.
228 if (callee
->isNamedLambda()) {
229 scopeChain
= DeclEnvObject::create(cx
, scopeChain
, callee
);
234 RootedScript
script(cx
, callee
->nonLazyScript());
235 return create(cx
, script
, scopeChain
, callee
);
239 CallObject::createForFunction(JSContext
*cx
, AbstractFramePtr frame
)
241 JS_ASSERT(frame
.isNonEvalFunctionFrame());
242 assertSameCompartment(cx
, frame
);
244 RootedObject
scopeChain(cx
, frame
.scopeChain());
245 RootedFunction
callee(cx
, frame
.callee());
247 CallObject
*callobj
= createForFunction(cx
, scopeChain
, callee
);
251 /* Copy in the closed-over formal arguments. */
252 for (AliasedFormalIter
i(frame
.script()); i
; i
++) {
253 callobj
->setAliasedVar(cx
, i
, i
->name(),
254 frame
.unaliasedFormal(i
.frameIndex(), DONT_CHECK_ALIASING
));
261 CallObject::createForStrictEval(JSContext
*cx
, AbstractFramePtr frame
)
263 JS_ASSERT(frame
.isStrictEvalFrame());
264 JS_ASSERT_IF(frame
.isStackFrame(), cx
->interpreterFrame() == frame
.asStackFrame());
265 JS_ASSERT_IF(frame
.isStackFrame(), cx
->interpreterRegs().pc
== frame
.script()->code());
267 RootedFunction
callee(cx
);
268 RootedScript
script(cx
, frame
.script());
269 RootedObject
scopeChain(cx
, frame
.scopeChain());
270 return create(cx
, script
, scopeChain
, callee
);
273 const Class
CallObject::class_
= {
275 JSCLASS_IS_ANONYMOUS
| JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS
),
276 JS_PropertyStub
, /* addProperty */
277 JS_DeletePropertyStub
, /* delProperty */
278 JS_PropertyStub
, /* getProperty */
279 JS_StrictPropertyStub
, /* setProperty */
282 nullptr /* convert: Leave it nullptr so we notice if calls ever escape */
285 const Class
DeclEnvObject::class_
= {
287 JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS
) |
288 JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
289 JS_PropertyStub
, /* addProperty */
290 JS_DeletePropertyStub
, /* delProperty */
291 JS_PropertyStub
, /* getProperty */
292 JS_StrictPropertyStub
, /* setProperty */
299 * Create a DeclEnvObject for a JSScript that is not initialized to any
300 * particular callsite. This object can either be initialized (with an enclosing
301 * scope and callee) or used as a template for jit compilation.
304 DeclEnvObject::createTemplateObject(JSContext
*cx
, HandleFunction fun
, gc::InitialHeap heap
)
306 JS_ASSERT(IsNurseryAllocable(FINALIZE_KIND
));
308 RootedTypeObject
type(cx
, cx
->getNewType(&class_
, nullptr));
312 RootedShape
emptyDeclEnvShape(cx
);
313 emptyDeclEnvShape
= EmptyShape::getInitialShape(cx
, &class_
, nullptr,
314 cx
->global(), nullptr, FINALIZE_KIND
,
315 BaseShape::DELEGATE
);
316 if (!emptyDeclEnvShape
)
319 RootedObject
obj(cx
, JSObject::create(cx
, FINALIZE_KIND
, heap
, emptyDeclEnvShape
, type
));
323 // Assign a fixed slot to a property with the same name as the lambda.
324 Rooted
<jsid
> id(cx
, AtomToId(fun
->atom()));
325 const Class
*clasp
= obj
->getClass();
326 unsigned attrs
= JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_READONLY
;
327 if (!JSObject::putProperty
<SequentialExecution
>(cx
, obj
, id
, clasp
->getProperty
,
328 clasp
->setProperty
, lambdaSlot(), attrs
, 0, 0))
333 JS_ASSERT(!obj
->hasDynamicSlots());
334 return &obj
->as
<DeclEnvObject
>();
338 DeclEnvObject::create(JSContext
*cx
, HandleObject enclosing
, HandleFunction callee
)
340 RootedObject
obj(cx
, createTemplateObject(cx
, callee
, gc::DefaultHeap
));
344 obj
->as
<ScopeObject
>().setEnclosingScope(enclosing
);
345 obj
->setFixedSlot(lambdaSlot(), ObjectValue(*callee
));
346 return &obj
->as
<DeclEnvObject
>();
350 WithObject::create(JSContext
*cx
, HandleObject proto
, HandleObject enclosing
, uint32_t depth
)
352 RootedTypeObject
type(cx
, cx
->getNewType(&class_
, proto
.get()));
356 RootedShape
shape(cx
, EmptyShape::getInitialShape(cx
, &class_
, TaggedProto(proto
),
357 &enclosing
->global(), nullptr,
362 RootedObject
obj(cx
, JSObject::create(cx
, FINALIZE_KIND
, gc::DefaultHeap
, shape
, type
));
366 obj
->as
<ScopeObject
>().setEnclosingScope(enclosing
);
367 obj
->setReservedSlot(DEPTH_SLOT
, PrivateUint32Value(depth
));
369 JSObject
*thisp
= JSObject::thisObject(cx
, proto
);
373 obj
->setFixedSlot(THIS_SLOT
, ObjectValue(*thisp
));
375 return &obj
->as
<WithObject
>();
379 with_LookupGeneric(JSContext
*cx
, HandleObject obj
, HandleId id
,
380 MutableHandleObject objp
, MutableHandleShape propp
)
382 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
383 return JSObject::lookupGeneric(cx
, actual
, id
, objp
, propp
);
387 with_LookupProperty(JSContext
*cx
, HandleObject obj
, HandlePropertyName name
,
388 MutableHandleObject objp
, MutableHandleShape propp
)
390 Rooted
<jsid
> id(cx
, NameToId(name
));
391 return with_LookupGeneric(cx
, obj
, id
, objp
, propp
);
395 with_LookupElement(JSContext
*cx
, HandleObject obj
, uint32_t index
,
396 MutableHandleObject objp
, MutableHandleShape propp
)
399 if (!IndexToId(cx
, index
, id
.address()))
401 return with_LookupGeneric(cx
, obj
, id
, objp
, propp
);
405 with_LookupSpecial(JSContext
*cx
, HandleObject obj
, HandleSpecialId sid
,
406 MutableHandleObject objp
, MutableHandleShape propp
)
408 Rooted
<jsid
> id(cx
, SPECIALID_TO_JSID(sid
));
409 return with_LookupGeneric(cx
, obj
, id
, objp
, propp
);
413 with_GetGeneric(JSContext
*cx
, HandleObject obj
, HandleObject receiver
, HandleId id
,
414 MutableHandleValue vp
)
416 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
417 return JSObject::getGeneric(cx
, actual
, actual
, id
, vp
);
421 with_GetProperty(JSContext
*cx
, HandleObject obj
, HandleObject receiver
, HandlePropertyName name
,
422 MutableHandleValue vp
)
424 Rooted
<jsid
> id(cx
, NameToId(name
));
425 return with_GetGeneric(cx
, obj
, receiver
, id
, vp
);
429 with_GetElement(JSContext
*cx
, HandleObject obj
, HandleObject receiver
, uint32_t index
,
430 MutableHandleValue vp
)
433 if (!IndexToId(cx
, index
, id
.address()))
435 return with_GetGeneric(cx
, obj
, receiver
, id
, vp
);
439 with_GetSpecial(JSContext
*cx
, HandleObject obj
, HandleObject receiver
, HandleSpecialId sid
,
440 MutableHandleValue vp
)
442 Rooted
<jsid
> id(cx
, SPECIALID_TO_JSID(sid
));
443 return with_GetGeneric(cx
, obj
, receiver
, id
, vp
);
447 with_SetGeneric(JSContext
*cx
, HandleObject obj
, HandleId id
,
448 MutableHandleValue vp
, bool strict
)
450 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
451 return JSObject::setGeneric(cx
, actual
, actual
, id
, vp
, strict
);
455 with_SetProperty(JSContext
*cx
, HandleObject obj
, HandlePropertyName name
,
456 MutableHandleValue vp
, bool strict
)
458 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
459 return JSObject::setProperty(cx
, actual
, actual
, name
, vp
, strict
);
463 with_SetElement(JSContext
*cx
, HandleObject obj
, uint32_t index
,
464 MutableHandleValue vp
, bool strict
)
466 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
467 return JSObject::setElement(cx
, actual
, actual
, index
, vp
, strict
);
471 with_SetSpecial(JSContext
*cx
, HandleObject obj
, HandleSpecialId sid
,
472 MutableHandleValue vp
, bool strict
)
474 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
475 return JSObject::setSpecial(cx
, actual
, actual
, sid
, vp
, strict
);
479 with_GetGenericAttributes(JSContext
*cx
, HandleObject obj
, HandleId id
, unsigned *attrsp
)
481 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
482 return JSObject::getGenericAttributes(cx
, actual
, id
, attrsp
);
486 with_SetGenericAttributes(JSContext
*cx
, HandleObject obj
, HandleId id
, unsigned *attrsp
)
488 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
489 return JSObject::setGenericAttributes(cx
, actual
, id
, attrsp
);
493 with_DeleteProperty(JSContext
*cx
, HandleObject obj
, HandlePropertyName name
,
496 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
497 return JSObject::deleteProperty(cx
, actual
, name
, succeeded
);
501 with_DeleteElement(JSContext
*cx
, HandleObject obj
, uint32_t index
,
504 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
505 return JSObject::deleteElement(cx
, actual
, index
, succeeded
);
509 with_DeleteSpecial(JSContext
*cx
, HandleObject obj
, HandleSpecialId sid
,
512 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
513 return JSObject::deleteSpecial(cx
, actual
, sid
, succeeded
);
517 with_Enumerate(JSContext
*cx
, HandleObject obj
, JSIterateOp enum_op
,
518 MutableHandleValue statep
, MutableHandleId idp
)
520 RootedObject
actual(cx
, &obj
->as
<WithObject
>().object());
521 return JSObject::enumerate(cx
, actual
, enum_op
, statep
, idp
);
525 with_ThisObject(JSContext
*cx
, HandleObject obj
)
527 return &obj
->as
<WithObject
>().withThis();
530 const Class
WithObject::class_
= {
532 JSCLASS_HAS_RESERVED_SLOTS(WithObject::RESERVED_SLOTS
) |
533 JSCLASS_IS_ANONYMOUS
,
534 JS_PropertyStub
, /* addProperty */
535 JS_DeletePropertyStub
, /* delProperty */
536 JS_PropertyStub
, /* getProperty */
537 JS_StrictPropertyStub
, /* setProperty */
541 nullptr, /* finalize */
542 nullptr, /* checkAccess */
544 nullptr, /* hasInstance */
545 nullptr, /* construct */
553 nullptr, /* defineGeneric */
554 nullptr, /* defineProperty */
555 nullptr, /* defineElement */
556 nullptr, /* defineSpecial */
560 nullptr, /* getElementIfPresent */
566 with_GetGenericAttributes
,
567 with_SetGenericAttributes
,
571 nullptr, nullptr, /* watch/unwatch */
577 /*****************************************************************************/
580 ClonedBlockObject::create(JSContext
*cx
, Handle
<StaticBlockObject
*> block
, AbstractFramePtr frame
)
582 assertSameCompartment(cx
, frame
);
583 JS_ASSERT(block
->getClass() == &BlockObject::class_
);
585 RootedTypeObject
type(cx
, cx
->getNewType(&BlockObject::class_
, block
.get()));
589 RootedShape
shape(cx
, block
->lastProperty());
591 RootedObject
obj(cx
, JSObject::create(cx
, FINALIZE_KIND
, gc::TenuredHeap
, shape
, type
));
595 /* Set the parent if necessary, as for call objects. */
596 if (&frame
.scopeChain()->global() != obj
->getParent()) {
597 JS_ASSERT(obj
->getParent() == nullptr);
598 Rooted
<GlobalObject
*> global(cx
, &frame
.scopeChain()->global());
599 if (!JSObject::setParent(cx
, obj
, global
))
603 JS_ASSERT(!obj
->inDictionaryMode());
604 JS_ASSERT(obj
->slotSpan() >= block
->slotCount() + RESERVED_SLOTS
);
606 obj
->setReservedSlot(SCOPE_CHAIN_SLOT
, ObjectValue(*frame
.scopeChain()));
607 obj
->setReservedSlot(DEPTH_SLOT
, PrivateUint32Value(block
->stackDepth()));
610 * Copy in the closed-over locals. Closed-over locals don't need
611 * any fixup since the initial value is 'undefined'.
613 unsigned nslots
= block
->slotCount();
614 unsigned base
= frame
.script()->nfixed
+ block
->stackDepth();
615 for (unsigned i
= 0; i
< nslots
; ++i
) {
616 if (block
->isAliased(i
))
617 obj
->as
<ClonedBlockObject
>().setVar(i
, frame
.unaliasedLocal(base
+ i
));
620 JS_ASSERT(obj
->isDelegate());
622 return &obj
->as
<ClonedBlockObject
>();
626 ClonedBlockObject::copyUnaliasedValues(AbstractFramePtr frame
)
628 StaticBlockObject
&block
= staticBlock();
629 unsigned base
= frame
.script()->nfixed
+ block
.stackDepth();
630 for (unsigned i
= 0; i
< slotCount(); ++i
) {
631 if (!block
.isAliased(i
))
632 setVar(i
, frame
.unaliasedLocal(base
+ i
), DONT_CHECK_ALIASING
);
637 StaticBlockObject::create(ExclusiveContext
*cx
)
639 RootedTypeObject
type(cx
, cx
->getNewType(&BlockObject::class_
, nullptr));
643 RootedShape
emptyBlockShape(cx
);
644 emptyBlockShape
= EmptyShape::getInitialShape(cx
, &BlockObject::class_
, nullptr, nullptr,
645 nullptr, FINALIZE_KIND
, BaseShape::DELEGATE
);
646 if (!emptyBlockShape
)
649 JSObject
*obj
= JSObject::create(cx
, FINALIZE_KIND
, gc::TenuredHeap
, emptyBlockShape
, type
);
653 return &obj
->as
<StaticBlockObject
>();
657 StaticBlockObject::addVar(ExclusiveContext
*cx
, Handle
<StaticBlockObject
*> block
, HandleId id
,
658 int index
, bool *redeclared
)
660 JS_ASSERT(JSID_IS_ATOM(id
) || (JSID_IS_INT(id
) && JSID_TO_INT(id
) == index
));
664 /* Inline JSObject::addProperty in order to trap the redefinition case. */
666 if (Shape::search(cx
, block
->lastProperty(), id
, &spp
, true)) {
672 * Don't convert this object to dictionary mode so that we can clone the
673 * block's shape later.
675 uint32_t slot
= JSSLOT_FREE(&BlockObject::class_
) + index
;
676 return JSObject::addPropertyInternal
<SequentialExecution
>(
678 /* getter = */ nullptr, /* setter = */ nullptr,
679 slot
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
,
680 Shape::HAS_SHORTID
, index
, spp
,
681 /* allowDictionary = */ false);
684 const Class
BlockObject::class_
= {
686 JSCLASS_IMPLEMENTS_BARRIERS
|
687 JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS
) |
688 JSCLASS_IS_ANONYMOUS
,
689 JS_PropertyStub
, /* addProperty */
690 JS_DeletePropertyStub
, /* delProperty */
691 JS_PropertyStub
, /* getProperty */
692 JS_StrictPropertyStub
, /* setProperty */
698 template<XDRMode mode
>
700 js::XDRStaticBlockObject(XDRState
<mode
> *xdr
, HandleObject enclosingScope
,
701 StaticBlockObject
**objp
)
703 /* NB: Keep this in sync with CloneStaticBlockObject. */
705 JSContext
*cx
= xdr
->cx();
707 Rooted
<StaticBlockObject
*> obj(cx
);
709 uint32_t depthAndCount
= 0;
711 if (mode
== XDR_ENCODE
) {
713 uint32_t depth
= obj
->stackDepth();
714 JS_ASSERT(depth
<= UINT16_MAX
);
715 count
= obj
->slotCount();
716 JS_ASSERT(count
<= UINT16_MAX
);
717 depthAndCount
= (depth
<< 16) | uint16_t(count
);
720 if (mode
== XDR_DECODE
) {
721 obj
= StaticBlockObject::create(cx
);
724 obj
->initEnclosingStaticScope(enclosingScope
);
728 if (!xdr
->codeUint32(&depthAndCount
))
731 if (mode
== XDR_DECODE
) {
732 uint32_t depth
= uint16_t(depthAndCount
>> 16);
733 count
= uint16_t(depthAndCount
);
734 obj
->setStackDepth(depth
);
737 * XDR the block object's properties. We know that there are 'count'
738 * properties to XDR, stored as id/shortid pairs.
740 for (unsigned i
= 0; i
< count
; i
++) {
742 if (!XDRAtom(xdr
, &atom
))
745 /* The empty string indicates an int id. */
746 RootedId
id(cx
, atom
!= cx
->runtime()->emptyString
751 if (!StaticBlockObject::addVar(cx
, obj
, id
, i
, &redeclared
)) {
752 JS_ASSERT(!redeclared
);
757 if (!xdr
->codeUint32(&aliased
))
760 JS_ASSERT(aliased
== 0 || aliased
== 1);
761 obj
->setAliased(i
, !!aliased
);
764 AutoShapeVector
shapes(cx
);
765 if (!shapes
.growBy(count
))
768 for (Shape::Range
<NoGC
> r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
769 Shape
*shape
= &r
.front();
770 shapes
[shape
->shortid()] = shape
;
774 * XDR the block object's properties. We know that there are 'count'
775 * properties to XDR, stored as id/shortid pairs.
777 RootedShape
shape(cx
);
780 for (unsigned i
= 0; i
< count
; i
++) {
782 JS_ASSERT(shape
->hasDefaultGetter());
783 JS_ASSERT(unsigned(shape
->shortid()) == i
);
785 propid
= shape
->propid();
786 JS_ASSERT(JSID_IS_ATOM(propid
) || JSID_IS_INT(propid
));
788 /* The empty string indicates an int id. */
789 atom
= JSID_IS_ATOM(propid
)
790 ? JSID_TO_ATOM(propid
)
791 : cx
->runtime()->emptyString
;
792 if (!XDRAtom(xdr
, &atom
))
795 uint32_t aliased
= obj
->isAliased(i
);
796 if (!xdr
->codeUint32(&aliased
))
804 js::XDRStaticBlockObject(XDRState
<XDR_ENCODE
> *, HandleObject
, StaticBlockObject
**);
807 js::XDRStaticBlockObject(XDRState
<XDR_DECODE
> *, HandleObject
, StaticBlockObject
**);
810 js::CloneStaticBlockObject(JSContext
*cx
, HandleObject enclosingScope
, Handle
<StaticBlockObject
*> srcBlock
)
812 /* NB: Keep this in sync with XDRStaticBlockObject. */
814 Rooted
<StaticBlockObject
*> clone(cx
, StaticBlockObject::create(cx
));
818 clone
->initEnclosingStaticScope(enclosingScope
);
819 clone
->setStackDepth(srcBlock
->stackDepth());
821 /* Shape::Range is reverse order, so build a list in forward order. */
822 AutoShapeVector
shapes(cx
);
823 if (!shapes
.growBy(srcBlock
->slotCount()))
825 for (Shape::Range
<NoGC
> r(srcBlock
->lastProperty()); !r
.empty(); r
.popFront())
826 shapes
[r
.front().shortid()] = &r
.front();
828 for (Shape
**p
= shapes
.begin(); p
!= shapes
.end(); ++p
) {
829 RootedId
id(cx
, (*p
)->propid());
830 unsigned i
= (*p
)->shortid();
833 if (!StaticBlockObject::addVar(cx
, clone
, id
, i
, &redeclared
)) {
834 JS_ASSERT(!redeclared
);
838 clone
->setAliased(i
, srcBlock
->isAliased(i
));
844 /*****************************************************************************/
846 // Any name atom for a function which will be added as a DeclEnv object to the
847 // scope chain above call objects for fun.
848 static inline JSAtom
*
849 CallObjectLambdaName(JSFunction
&fun
)
851 return fun
.isNamedLambda() ? fun
.atom() : nullptr;
854 ScopeIter::ScopeIter(const ScopeIter
&si
, JSContext
*cx
855 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
)
859 block_(cx
, si
.block_
),
861 hasScopeObject_(si
.hasScopeObject_
)
863 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
866 ScopeIter::ScopeIter(JSObject
&enclosingScope
, JSContext
*cx
867 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
)
869 frame_(NullFramePtr()),
870 cur_(cx
, &enclosingScope
),
874 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
877 ScopeIter::ScopeIter(AbstractFramePtr frame
, JSContext
*cx
878 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
)
881 cur_(cx
, frame
.scopeChain()),
882 block_(cx
, frame
.maybeBlockChain())
884 assertSameCompartment(cx
, frame
);
886 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
889 ScopeIter::ScopeIter(const ScopeIter
&si
, AbstractFramePtr frame
, JSContext
*cx
890 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
)
894 block_(cx
, si
.block_
),
896 hasScopeObject_(si
.hasScopeObject_
)
898 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
901 ScopeIter::ScopeIter(AbstractFramePtr frame
, ScopeObject
&scope
, JSContext
*cx
902 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
)
909 * Find the appropriate static block for this iterator, given 'scope'. We
910 * know that 'scope' is a (non-optimized) scope on fp's scope chain. We do
911 * not, however, know whether fp->maybeScopeChain() encloses 'scope'. E.g.:
914 * g = function() { eval('debugger') };
918 * g will have x's block in its enclosing scope but not y's. However, at
919 * the debugger statement, both the x's and y's blocks will be on
920 * fp->blockChain. Fortunately, we can compare scope object stack depths to
921 * determine the block (if any) that encloses 'scope'.
923 if (cur_
->is
<NestedScopeObject
>()) {
924 block_
= frame
.maybeBlockChain();
926 if (block_
->stackDepth() <= cur_
->as
<NestedScopeObject
>().stackDepth())
928 block_
= block_
->enclosingBlock();
930 JS_ASSERT_IF(cur_
->is
<ClonedBlockObject
>(),
931 cur_
->as
<ClonedBlockObject
>().staticBlock() == *block_
);
936 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
940 ScopeIter::scope() const
942 JS_ASSERT(hasScopeObject());
943 return cur_
->as
<ScopeObject
>();
947 ScopeIter::operator++()
952 if (hasScopeObject_
) {
953 cur_
= &cur_
->as
<CallObject
>().enclosingScope();
954 if (CallObjectLambdaName(*frame_
.fun()))
955 cur_
= &cur_
->as
<DeclEnvObject
>().enclosingScope();
957 frame_
= NullFramePtr();
960 block_
= block_
->enclosingBlock();
962 cur_
= &cur_
->as
<ClonedBlockObject
>().enclosingScope();
966 JS_ASSERT(hasScopeObject_
);
967 cur_
= &cur_
->as
<WithObject
>().enclosingScope();
970 case StrictEvalScope
:
972 cur_
= &cur_
->as
<CallObject
>().enclosingScope();
973 frame_
= NullFramePtr();
983 * Given an iterator state (cur_, block_), figure out which (potentially
984 * optimized) scope the iterator should report. Thus, the result is a pair
985 * (type_, hasScopeObject_) where hasScopeObject_ indicates whether the
986 * scope object has been optimized away and does not exist on the scope
987 * chain. Beware: while ScopeIter iterates over the scopes of a single
988 * frame, the scope chain (pointed to by cur_) continues into the scopes of
989 * enclosing frames. Thus, it is important not to look at cur_ until it is
990 * certain that cur_ points to a scope object in the current frame. In
991 * particular, there are three tricky corner cases:
992 * - non-heavyweight functions;
993 * - non-strict direct eval.
994 * - heavyweight functions observed before the prologue has finished;
995 * In all cases, cur_ can already be pointing into an enclosing frame's
996 * scope chain. Furthermore, in the first two cases: even if cur_ points
997 * into an enclosing frame's scope chain, the current frame may still have
998 * uncloned blocks. In the last case, since we haven't entered the
999 * function, we simply return a ScopeIter where done() == true.
1001 * Note: DebugScopeObject falls nicely into this plan: since they are only
1002 * ever introduced as the *enclosing* scope of a frame, they should never
1003 * show up in scope iteration and fall into the final non-scope case.
1005 if (frame_
.isNonEvalFunctionFrame() && !frame_
.fun()->isHeavyweight()) {
1008 hasScopeObject_
= block_
->needsClone();
1011 hasScopeObject_
= false;
1013 } else if (frame_
.isNonStrictDirectEvalFrame() && cur_
== frame_
.evalPrevScopeChain(cx
)) {
1015 JS_ASSERT(!block_
->needsClone());
1017 hasScopeObject_
= false;
1019 frame_
= NullFramePtr();
1021 } else if (frame_
.isNonEvalFunctionFrame() && !frame_
.hasCallObj()) {
1022 JS_ASSERT(cur_
== frame_
.fun()->environment());
1023 frame_
= NullFramePtr();
1024 } else if (frame_
.isStrictEvalFrame() && !frame_
.hasCallObj()) {
1025 JS_ASSERT(cur_
== frame_
.evalPrevScopeChain(cx
));
1026 frame_
= NullFramePtr();
1027 } else if (cur_
->is
<WithObject
>()) {
1028 JS_ASSERT_IF(frame_
.isFunctionFrame(), frame_
.fun()->isHeavyweight());
1029 JS_ASSERT_IF(block_
, block_
->needsClone());
1030 JS_ASSERT_IF(block_
, block_
->stackDepth() < cur_
->as
<WithObject
>().stackDepth());
1032 hasScopeObject_
= true;
1033 } else if (block_
) {
1035 hasScopeObject_
= block_
->needsClone();
1036 JS_ASSERT_IF(hasScopeObject_
, cur_
->as
<ClonedBlockObject
>().staticBlock() == *block_
);
1037 } else if (cur_
->is
<CallObject
>()) {
1038 CallObject
&callobj
= cur_
->as
<CallObject
>();
1039 type_
= callobj
.isForEval() ? StrictEvalScope
: Call
;
1040 hasScopeObject_
= true;
1041 JS_ASSERT_IF(type_
== Call
, callobj
.callee().nonLazyScript() == frame_
.script());
1043 JS_ASSERT(!cur_
->is
<ScopeObject
>());
1044 JS_ASSERT(frame_
.isGlobalFrame() || frame_
.isDebuggerFrame());
1045 frame_
= NullFramePtr();
1049 /* static */ HashNumber
1050 ScopeIterKey::hash(ScopeIterKey si
)
1052 /* hasScopeObject_ is determined by the other fields. */
1053 return size_t(si
.frame_
.raw()) ^ size_t(si
.cur_
) ^ size_t(si
.block_
) ^ si
.type_
;
1057 ScopeIterKey::match(ScopeIterKey si1
, ScopeIterKey si2
)
1059 /* hasScopeObject_ is determined by the other fields. */
1060 return si1
.frame_
== si2
.frame_
&&
1062 (si1
.cur_
== si2
.cur_
&&
1063 si1
.block_
== si2
.block_
&&
1064 si1
.type_
== si2
.type_
));
1067 /*****************************************************************************/
1072 * DebugScopeProxy is the handler for DebugScopeObject proxy objects. Having a
1073 * custom handler (rather than trying to reuse js::Wrapper) gives us several
1074 * important abilities:
1075 * - We want to pass the ScopeObject as the receiver to forwarded scope
1076 * property ops on aliased variables so that Call/Block/With ops do not all
1077 * require a 'normalization' step.
1078 * - The debug scope proxy can directly manipulate the stack frame to allow
1079 * the debugger to read/write args/locals that were otherwise unaliased.
1080 * - The debug scope proxy can store unaliased variables after the stack frame
1081 * is popped so that they may still be read/written by the debugger.
1082 * - The engine has made certain assumptions about the possible reads/writes
1083 * in a scope. DebugScopeProxy allows us to prevent the debugger from
1084 * breaking those assumptions.
1085 * - The engine makes optimizations that are observable to the debugger. The
1086 * proxy can either hide these optimizations or make the situation more
1087 * clear to the debugger. An example is 'arguments'.
1089 class DebugScopeProxy
: public BaseProxyHandler
1091 enum Action
{ SET
, GET
};
1094 * This function handles access to unaliased locals/formals. Since they are
1095 * unaliased, the values of these variables are not stored in the slots of
1096 * the normal Call/BlockObject scope objects and thus must be recovered
1097 * from somewhere else:
1098 * + if the invocation for which the scope was created is still executing,
1099 * there is a StackFrame (either live on the stack or floating in a
1100 * generator object) holding the values;
1101 * + if the invocation for which the scope was created finished executing:
1102 * - and there was a DebugScopeObject associated with scope, then the
1103 * DebugScopes::onPop(Call|Block) handler copied out the unaliased
1105 * . for block scopes, the unaliased values were copied directly
1106 * into the block object, since there is a slot allocated for every
1107 * block binding, regardless of whether it is aliased;
1108 * . for function scopes, a dense array is created in onPopCall to hold
1109 * the unaliased values and attached to the DebugScopeObject;
1110 * - and there was not a DebugScopeObject yet associated with the
1111 * scope, then the unaliased values are lost and not recoverable.
1113 * handleUnaliasedAccess returns 'true' if the access was unaliased and
1114 * completed by handleUnaliasedAccess.
1116 bool handleUnaliasedAccess(JSContext
*cx
, Handle
<DebugScopeObject
*> debugScope
, Handle
<ScopeObject
*> scope
,
1117 jsid id
, Action action
, MutableHandleValue vp
)
1119 JS_ASSERT(&debugScope
->scope() == scope
);
1120 AbstractFramePtr maybeframe
= DebugScopes::hasLiveFrame(*scope
);
1122 /* Handle unaliased formals, vars, and consts at function scope. */
1123 if (scope
->is
<CallObject
>() && !scope
->as
<CallObject
>().isForEval()) {
1124 CallObject
&callobj
= scope
->as
<CallObject
>();
1125 RootedScript
script(cx
, callobj
.callee().nonLazyScript());
1126 if (!script
->ensureHasTypes(cx
))
1129 Bindings
&bindings
= script
->bindings
;
1130 BindingIter
bi(script
);
1131 while (bi
&& NameToId(bi
->name()) != id
)
1136 if (bi
->kind() == VARIABLE
|| bi
->kind() == CONSTANT
) {
1137 unsigned i
= bi
.frameIndex();
1138 if (script
->varIsAliased(i
))
1143 vp
.set(maybeframe
.unaliasedVar(i
));
1145 maybeframe
.unaliasedVar(i
) = vp
;
1146 } else if (JSObject
*snapshot
= debugScope
->maybeSnapshot()) {
1148 vp
.set(snapshot
->getDenseElement(bindings
.numArgs() + i
));
1150 snapshot
->setDenseElement(bindings
.numArgs() + i
, vp
);
1152 /* The unaliased value has been lost to the debugger. */
1154 vp
.set(UndefinedValue());
1157 JS_ASSERT(bi
->kind() == ARGUMENT
);
1158 unsigned i
= bi
.frameIndex();
1159 if (script
->formalIsAliased(i
))
1163 if (script
->argsObjAliasesFormals() && maybeframe
.hasArgsObj()) {
1165 vp
.set(maybeframe
.argsObj().arg(i
));
1167 maybeframe
.argsObj().setArg(i
, vp
);
1170 vp
.set(maybeframe
.unaliasedFormal(i
, DONT_CHECK_ALIASING
));
1172 maybeframe
.unaliasedFormal(i
, DONT_CHECK_ALIASING
) = vp
;
1174 } else if (JSObject
*snapshot
= debugScope
->maybeSnapshot()) {
1176 vp
.set(snapshot
->getDenseElement(i
));
1178 snapshot
->setDenseElement(i
, vp
);
1180 /* The unaliased value has been lost to the debugger. */
1182 vp
.set(UndefinedValue());
1186 TypeScript::SetArgument(cx
, script
, i
, vp
);
1192 /* Handle unaliased let and catch bindings at block scope. */
1193 if (scope
->is
<ClonedBlockObject
>()) {
1194 Rooted
<ClonedBlockObject
*> block(cx
, &scope
->as
<ClonedBlockObject
>());
1195 Shape
*shape
= block
->lastProperty()->search(cx
, id
);
1199 unsigned i
= shape
->shortid();
1200 if (block
->staticBlock().isAliased(i
))
1204 JSScript
*script
= maybeframe
.script();
1205 unsigned local
= block
->slotToLocalIndex(script
->bindings
, shape
->slot());
1207 vp
.set(maybeframe
.unaliasedLocal(local
));
1209 maybeframe
.unaliasedLocal(local
) = vp
;
1210 JS_ASSERT(analyze::LocalSlot(script
, local
) >= analyze::TotalSlots(script
));
1213 vp
.set(block
->var(i
, DONT_CHECK_ALIASING
));
1215 block
->setVar(i
, vp
, DONT_CHECK_ALIASING
);
1221 /* The rest of the internal scopes do not have unaliased vars. */
1222 JS_ASSERT(scope
->is
<DeclEnvObject
>() || scope
->is
<WithObject
>() ||
1223 scope
->as
<CallObject
>().isForEval());
1227 static bool isArguments(JSContext
*cx
, jsid id
)
1229 return id
== NameToId(cx
->names().arguments
);
1232 static bool isFunctionScope(ScopeObject
&scope
)
1234 return scope
.is
<CallObject
>() && !scope
.as
<CallObject
>().isForEval();
1238 * In theory, every function scope contains an 'arguments' bindings.
1239 * However, the engine only adds a binding if 'arguments' is used in the
1240 * function body. Thus, from the debugger's perspective, 'arguments' may be
1241 * missing from the list of bindings.
1243 static bool isMissingArgumentsBinding(ScopeObject
&scope
)
1245 return isFunctionScope(scope
) &&
1246 !scope
.as
<CallObject
>().callee().nonLazyScript()->argumentsHasVarBinding();
1250 * This function creates an arguments object when the debugger requests
1251 * 'arguments' for a function scope where the arguments object has been
1252 * optimized away (either because the binding is missing altogether or
1253 * because !ScriptAnalysis::needsArgsObj).
1255 static bool checkForMissingArguments(JSContext
*cx
, jsid id
, ScopeObject
&scope
,
1256 ArgumentsObject
**maybeArgsObj
)
1258 *maybeArgsObj
= nullptr;
1260 if (!isArguments(cx
, id
) || !isFunctionScope(scope
))
1263 if (scope
.as
<CallObject
>().callee().nonLazyScript()->needsArgsObj())
1266 AbstractFramePtr maybeframe
= DebugScopes::hasLiveFrame(scope
);
1268 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_DEBUG_NOT_LIVE
,
1273 *maybeArgsObj
= ArgumentsObject::createUnexpected(cx
, maybeframe
);
1279 static DebugScopeProxy singleton
;
1281 DebugScopeProxy() : BaseProxyHandler(&family
) {}
1283 bool isExtensible(JSContext
*cx
, HandleObject proxy
, bool *extensible
) MOZ_OVERRIDE
1285 // always [[Extensible]], can't be made non-[[Extensible]], like most
1291 bool preventExtensions(JSContext
*cx
, HandleObject proxy
) MOZ_OVERRIDE
1294 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY
);
1298 bool getPropertyDescriptor(JSContext
*cx
, HandleObject proxy
, HandleId id
,
1299 MutableHandle
<PropertyDescriptor
> desc
,
1300 unsigned flags
) MOZ_OVERRIDE
1302 return getOwnPropertyDescriptor(cx
, proxy
, id
, desc
, flags
);
1305 bool getOwnPropertyDescriptor(JSContext
*cx
, HandleObject proxy
, HandleId id
,
1306 MutableHandle
<PropertyDescriptor
> desc
,
1307 unsigned flags
) MOZ_OVERRIDE
1309 Rooted
<DebugScopeObject
*> debugScope(cx
, &proxy
->as
<DebugScopeObject
>());
1310 Rooted
<ScopeObject
*> scope(cx
, &debugScope
->scope());
1312 RootedArgumentsObject
maybeArgsObj(cx
);
1313 if (!checkForMissingArguments(cx
, id
, *scope
, maybeArgsObj
.address()))
1317 desc
.object().set(debugScope
);
1318 desc
.setAttributes(JSPROP_READONLY
| JSPROP_ENUMERATE
| JSPROP_PERMANENT
);
1319 desc
.value().setObject(*maybeArgsObj
);
1321 desc
.setGetter(nullptr);
1322 desc
.setSetter(nullptr);
1327 if (handleUnaliasedAccess(cx
, debugScope
, scope
, id
, GET
, &v
)) {
1328 desc
.object().set(debugScope
);
1329 desc
.setAttributes(JSPROP_READONLY
| JSPROP_ENUMERATE
| JSPROP_PERMANENT
);
1330 desc
.value().set(v
);
1332 desc
.setGetter(nullptr);
1333 desc
.setSetter(nullptr);
1337 return JS_GetOwnPropertyDescriptorById(cx
, scope
, id
, flags
, desc
);
1340 bool get(JSContext
*cx
, HandleObject proxy
, HandleObject receiver
, HandleId id
,
1341 MutableHandleValue vp
) MOZ_OVERRIDE
1343 Rooted
<DebugScopeObject
*> debugScope(cx
, &proxy
->as
<DebugScopeObject
>());
1344 Rooted
<ScopeObject
*> scope(cx
, &proxy
->as
<DebugScopeObject
>().scope());
1346 RootedArgumentsObject
maybeArgsObj(cx
);
1347 if (!checkForMissingArguments(cx
, id
, *scope
, maybeArgsObj
.address()))
1351 vp
.set(ObjectValue(*maybeArgsObj
));
1355 if (handleUnaliasedAccess(cx
, debugScope
, scope
, id
, GET
, vp
))
1358 return JSObject::getGeneric(cx
, scope
, scope
, id
, vp
);
1361 bool set(JSContext
*cx
, HandleObject proxy
, HandleObject receiver
, HandleId id
, bool strict
,
1362 MutableHandleValue vp
) MOZ_OVERRIDE
1364 Rooted
<DebugScopeObject
*> debugScope(cx
, &proxy
->as
<DebugScopeObject
>());
1365 Rooted
<ScopeObject
*> scope(cx
, &proxy
->as
<DebugScopeObject
>().scope());
1366 if (handleUnaliasedAccess(cx
, debugScope
, scope
, id
, SET
, vp
))
1368 return JSObject::setGeneric(cx
, scope
, scope
, id
, vp
, strict
);
1371 bool defineProperty(JSContext
*cx
, HandleObject proxy
, HandleId id
,
1372 MutableHandle
<PropertyDescriptor
> desc
) MOZ_OVERRIDE
1374 Rooted
<ScopeObject
*> scope(cx
, &proxy
->as
<DebugScopeObject
>().scope());
1377 if (!has(cx
, proxy
, id
, &found
))
1380 return Throw(cx
, id
, JSMSG_CANT_REDEFINE_PROP
);
1382 return JS_DefinePropertyById(cx
, scope
, id
, desc
.value(), desc
.getter(), desc
.setter(),
1386 bool getScopePropertyNames(JSContext
*cx
, HandleObject proxy
, AutoIdVector
&props
,
1389 Rooted
<ScopeObject
*> scope(cx
, &proxy
->as
<DebugScopeObject
>().scope());
1391 if (isMissingArgumentsBinding(*scope
)) {
1392 if (!props
.append(NameToId(cx
->names().arguments
)))
1396 if (!GetPropertyNames(cx
, scope
, flags
, &props
))
1400 * Function scopes are optimized to not contain unaliased variables so
1401 * they must be manually appended here.
1403 if (scope
->is
<CallObject
>() && !scope
->as
<CallObject
>().isForEval()) {
1404 RootedScript
script(cx
, scope
->as
<CallObject
>().callee().nonLazyScript());
1405 for (BindingIter
bi(script
); bi
; bi
++) {
1406 if (!bi
->aliased() && !props
.append(NameToId(bi
->name())))
1414 bool getOwnPropertyNames(JSContext
*cx
, HandleObject proxy
, AutoIdVector
&props
) MOZ_OVERRIDE
1416 return getScopePropertyNames(cx
, proxy
, props
, JSITER_OWNONLY
);
1419 bool enumerate(JSContext
*cx
, HandleObject proxy
, AutoIdVector
&props
) MOZ_OVERRIDE
1421 return getScopePropertyNames(cx
, proxy
, props
, 0);
1424 bool has(JSContext
*cx
, HandleObject proxy
, HandleId id_
, bool *bp
) MOZ_OVERRIDE
1426 RootedId
id(cx
, id_
);
1427 ScopeObject
&scopeObj
= proxy
->as
<DebugScopeObject
>().scope();
1429 if (isArguments(cx
, id
) && isFunctionScope(scopeObj
)) {
1435 RootedObject
scope(cx
, &scopeObj
);
1436 if (!JS_HasPropertyById(cx
, scope
, id
, &found
))
1440 * Function scopes are optimized to not contain unaliased variables so
1441 * a manual search is necessary.
1443 if (!found
&& scope
->is
<CallObject
>() && !scope
->as
<CallObject
>().isForEval()) {
1444 RootedScript
script(cx
, scope
->as
<CallObject
>().callee().nonLazyScript());
1445 for (BindingIter
bi(script
); bi
; bi
++) {
1446 if (!bi
->aliased() && NameToId(bi
->name()) == id
) {
1457 bool delete_(JSContext
*cx
, HandleObject proxy
, HandleId id
, bool *bp
) MOZ_OVERRIDE
1459 RootedValue
idval(cx
, IdToValue(id
));
1460 return js_ReportValueErrorFlags(cx
, JSREPORT_ERROR
, JSMSG_CANT_DELETE
,
1461 JSDVG_IGNORE_STACK
, idval
, NullPtr(), nullptr, nullptr);
1465 } /* anonymous namespace */
1467 int DebugScopeProxy::family
= 0;
1468 DebugScopeProxy
DebugScopeProxy::singleton
;
1470 /* static */ DebugScopeObject
*
1471 DebugScopeObject::create(JSContext
*cx
, ScopeObject
&scope
, HandleObject enclosing
)
1473 JS_ASSERT(scope
.compartment() == cx
->compartment());
1474 RootedValue
priv(cx
, ObjectValue(scope
));
1475 JSObject
*obj
= NewProxyObject(cx
, &DebugScopeProxy::singleton
, priv
,
1476 nullptr /* proto */, &scope
.global());
1480 JS_ASSERT(!enclosing
->is
<ScopeObject
>());
1482 DebugScopeObject
*debugScope
= &obj
->as
<DebugScopeObject
>();
1483 debugScope
->setExtra(ENCLOSING_EXTRA
, ObjectValue(*enclosing
));
1484 debugScope
->setExtra(SNAPSHOT_EXTRA
, NullValue());
1490 DebugScopeObject::scope() const
1492 return target()->as
<ScopeObject
>();
1496 DebugScopeObject::enclosingScope() const
1498 return extra(ENCLOSING_EXTRA
).toObject();
1502 DebugScopeObject::maybeSnapshot() const
1504 JS_ASSERT(!scope().as
<CallObject
>().isForEval());
1505 return extra(SNAPSHOT_EXTRA
).toObjectOrNull();
1509 DebugScopeObject::initSnapshot(JSObject
&o
)
1511 JS_ASSERT(maybeSnapshot() == nullptr);
1512 setExtra(SNAPSHOT_EXTRA
, ObjectValue(o
));
1516 DebugScopeObject::isForDeclarative() const
1518 ScopeObject
&s
= scope();
1519 return s
.is
<CallObject
>() || s
.is
<BlockObject
>() || s
.is
<DeclEnvObject
>();
1523 js_IsDebugScopeSlow(ProxyObject
*proxy
)
1525 JS_ASSERT(proxy
->hasClass(&ProxyObject::uncallableClass_
));
1526 return proxy
->handler() == &DebugScopeProxy::singleton
;
1529 /*****************************************************************************/
1531 /* static */ JS_ALWAYS_INLINE
void
1532 DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime
*rt
, ObjectWeakMap
*map
,
1533 const EncapsulatedPtr
<JSObject
> &key
)
1535 #ifdef JSGC_GENERATIONAL
1537 * Strip the barriers from the type before inserting into the store buffer.
1538 * This will automatically ensure that barriers do not fire during GC.
1540 typedef WeakMap
<JSObject
*, JSObject
*> UnbarrieredMap
;
1541 typedef gc::HashKeyRef
<UnbarrieredMap
, JSObject
*> Ref
;
1542 if (key
&& IsInsideNursery(rt
, key
))
1543 rt
->gcStoreBuffer
.putGeneric(Ref(reinterpret_cast<UnbarrieredMap
*>(map
), key
.get()));
1547 /* static */ JS_ALWAYS_INLINE
void
1548 DebugScopes::liveScopesPostWriteBarrier(JSRuntime
*rt
, LiveScopeMap
*map
, ScopeObject
*key
)
1550 #ifdef JSGC_GENERATIONAL
1551 if (key
&& IsInsideNursery(rt
, key
))
1552 rt
->gcStoreBuffer
.putGeneric(gc::HashKeyRef
<LiveScopeMap
, ScopeObject
*>(map
, key
));
1556 DebugScopes::DebugScopes(JSContext
*cx
)
1557 : proxiedScopes(cx
),
1558 missingScopes(cx
->runtime()),
1559 liveScopes(cx
->runtime())
1562 DebugScopes::~DebugScopes()
1564 JS_ASSERT(missingScopes
.empty());
1565 WeakMapBase::removeWeakMapFromList(&proxiedScopes
);
1571 if (!liveScopes
.init() ||
1572 !proxiedScopes
.init() ||
1573 !missingScopes
.init())
1581 DebugScopes::mark(JSTracer
*trc
)
1583 proxiedScopes
.trace(trc
);
1587 DebugScopes::sweep(JSRuntime
*rt
)
1590 * Note: missingScopes points to debug scopes weakly not just so that debug
1591 * scopes can be released more eagerly, but, more importantly, to avoid
1592 * creating an uncollectable cycle with suspended generator frames.
1594 for (MissingScopeMap::Enum
e(missingScopes
); !e
.empty(); e
.popFront()) {
1595 if (IsObjectAboutToBeFinalized(e
.front().value().unsafeGet()))
1599 for (LiveScopeMap::Enum
e(liveScopes
); !e
.empty(); e
.popFront()) {
1600 ScopeObject
*scope
= e
.front().key();
1601 AbstractFramePtr frame
= e
.front().value();
1604 * Scopes can be finalized when a debugger-synthesized ScopeObject is
1605 * no longer reachable via its DebugScopeObject.
1607 if (IsObjectAboutToBeFinalized(&scope
)) {
1613 * As explained in onGeneratorFrameChange, liveScopes includes
1614 * suspended generator frames. Since a generator can be finalized while
1615 * its scope is live, we must explicitly detect finalized generators.
1617 if (JSGenerator
*gen
= frame
.maybeSuspendedGenerator(rt
)) {
1618 JS_ASSERT(gen
->state
== JSGEN_NEWBORN
|| gen
->state
== JSGEN_OPEN
);
1619 if (IsObjectAboutToBeFinalized(&gen
->obj
)) {
1628 * Unfortunately, GetDebugScopeForFrame needs to work even outside debug mode
1629 * (in particular, JS_GetFrameScopeChain does not require debug mode). Since
1630 * DebugScopes::onPop* are only called in debug mode, this means we cannot
1631 * use any of the maps in DebugScopes. This will produce debug scope chains
1632 * that do not obey the debugger invariants but that is just fine.
1635 CanUseDebugScopeMaps(JSContext
*cx
)
1637 return cx
->compartment()->debugMode();
1641 DebugScopes::ensureCompartmentData(JSContext
*cx
)
1643 JSCompartment
*c
= cx
->compartment();
1645 return c
->debugScopes
;
1647 c
->debugScopes
= cx
->runtime()->new_
<DebugScopes
>(cx
);
1648 if (c
->debugScopes
&& c
->debugScopes
->init())
1649 return c
->debugScopes
;
1651 js_ReportOutOfMemory(cx
);
1656 DebugScopes::hasDebugScope(JSContext
*cx
, ScopeObject
&scope
)
1658 DebugScopes
*scopes
= scope
.compartment()->debugScopes
;
1662 if (ObjectWeakMap::Ptr p
= scopes
->proxiedScopes
.lookup(&scope
)) {
1663 JS_ASSERT(CanUseDebugScopeMaps(cx
));
1664 return &p
->value()->as
<DebugScopeObject
>();
1671 DebugScopes::addDebugScope(JSContext
*cx
, ScopeObject
&scope
, DebugScopeObject
&debugScope
)
1673 JS_ASSERT(cx
->compartment() == scope
.compartment());
1674 JS_ASSERT(cx
->compartment() == debugScope
.compartment());
1676 if (!CanUseDebugScopeMaps(cx
))
1679 DebugScopes
*scopes
= ensureCompartmentData(cx
);
1683 JS_ASSERT(!scopes
->proxiedScopes
.has(&scope
));
1684 if (!scopes
->proxiedScopes
.put(&scope
, &debugScope
)) {
1685 js_ReportOutOfMemory(cx
);
1689 proxiedScopesPostWriteBarrier(cx
->runtime(), &scopes
->proxiedScopes
, &scope
);
1694 DebugScopes::hasDebugScope(JSContext
*cx
, const ScopeIter
&si
)
1696 JS_ASSERT(!si
.hasScopeObject());
1698 DebugScopes
*scopes
= cx
->compartment()->debugScopes
;
1702 if (MissingScopeMap::Ptr p
= scopes
->missingScopes
.lookup(si
)) {
1703 JS_ASSERT(CanUseDebugScopeMaps(cx
));
1710 DebugScopes::addDebugScope(JSContext
*cx
, const ScopeIter
&si
, DebugScopeObject
&debugScope
)
1712 JS_ASSERT(!si
.hasScopeObject());
1713 JS_ASSERT(cx
->compartment() == debugScope
.compartment());
1715 if (!CanUseDebugScopeMaps(cx
))
1718 DebugScopes
*scopes
= ensureCompartmentData(cx
);
1722 JS_ASSERT(!scopes
->missingScopes
.has(si
));
1723 if (!scopes
->missingScopes
.put(si
, &debugScope
)) {
1724 js_ReportOutOfMemory(cx
);
1728 JS_ASSERT(!scopes
->liveScopes
.has(&debugScope
.scope()));
1729 if (!scopes
->liveScopes
.put(&debugScope
.scope(), si
.frame())) {
1730 js_ReportOutOfMemory(cx
);
1733 liveScopesPostWriteBarrier(cx
->runtime(), &scopes
->liveScopes
, &debugScope
.scope());
1739 DebugScopes::onPopCall(AbstractFramePtr frame
, JSContext
*cx
)
1741 JS_ASSERT(!frame
.isYielding());
1742 assertSameCompartment(cx
, frame
);
1744 DebugScopes
*scopes
= cx
->compartment()->debugScopes
;
1748 Rooted
<DebugScopeObject
*> debugScope(cx
, nullptr);
1750 if (frame
.fun()->isHeavyweight()) {
1752 * The StackFrame may be observed before the prologue has created the
1753 * CallObject. See ScopeIter::settle.
1755 if (!frame
.hasCallObj())
1758 CallObject
&callobj
= frame
.scopeChain()->as
<CallObject
>();
1759 scopes
->liveScopes
.remove(&callobj
);
1760 if (ObjectWeakMap::Ptr p
= scopes
->proxiedScopes
.lookup(&callobj
))
1761 debugScope
= &p
->value()->as
<DebugScopeObject
>();
1763 ScopeIter
si(frame
, cx
);
1764 if (MissingScopeMap::Ptr p
= scopes
->missingScopes
.lookup(si
)) {
1765 debugScope
= p
->value();
1766 scopes
->liveScopes
.remove(&debugScope
->scope().as
<CallObject
>());
1767 scopes
->missingScopes
.remove(p
);
1772 * When the StackFrame is popped, the values of unaliased variables
1773 * are lost. If there is any debug scope referring to this scope, save a
1774 * copy of the unaliased variables' values in an array for later debugger
1775 * access via DebugScopeProxy::handleUnaliasedAccess.
1777 * Note: since it is simplest for this function to be infallible, failure
1778 * in this code will be silently ignored. This does not break any
1779 * invariants since DebugScopeObject::maybeSnapshot can already be nullptr.
1783 * Copy all frame values into the snapshot, regardless of
1784 * aliasing. This unnecessarily includes aliased variables
1785 * but it simplifies later indexing logic.
1787 AutoValueVector
vec(cx
);
1788 if (!frame
.copyRawFrameSlots(&vec
) || vec
.length() == 0)
1792 * Copy in formals that are not aliased via the scope chain
1793 * but are aliased via the arguments object.
1795 RootedScript
script(cx
, frame
.script());
1796 if (script
->analyzedArgsUsage() && script
->needsArgsObj() && frame
.hasArgsObj()) {
1797 for (unsigned i
= 0; i
< frame
.numFormalArgs(); ++i
) {
1798 if (script
->formalLivesInArgumentsObject(i
))
1799 vec
[i
] = frame
.argsObj().arg(i
);
1804 * Use a dense array as storage (since proxies do not have trace
1805 * hooks). This array must not escape into the wild.
1807 RootedObject
snapshot(cx
, NewDenseCopiedArray(cx
, vec
.length(), vec
.begin()));
1809 cx
->clearPendingException();
1813 debugScope
->initSnapshot(*snapshot
);
1818 DebugScopes::onPopBlock(JSContext
*cx
, AbstractFramePtr frame
)
1820 assertSameCompartment(cx
, frame
);
1822 DebugScopes
*scopes
= cx
->compartment()->debugScopes
;
1826 StaticBlockObject
&staticBlock
= *frame
.maybeBlockChain();
1827 if (staticBlock
.needsClone()) {
1828 ClonedBlockObject
&clone
= frame
.scopeChain()->as
<ClonedBlockObject
>();
1829 clone
.copyUnaliasedValues(frame
);
1830 scopes
->liveScopes
.remove(&clone
);
1832 ScopeIter
si(frame
, cx
);
1833 if (MissingScopeMap::Ptr p
= scopes
->missingScopes
.lookup(si
)) {
1834 ClonedBlockObject
&clone
= p
->value()->scope().as
<ClonedBlockObject
>();
1835 clone
.copyUnaliasedValues(frame
);
1836 scopes
->liveScopes
.remove(&clone
);
1837 scopes
->missingScopes
.remove(p
);
1843 DebugScopes::onPopWith(AbstractFramePtr frame
)
1845 DebugScopes
*scopes
= frame
.compartment()->debugScopes
;
1847 scopes
->liveScopes
.remove(&frame
.scopeChain()->as
<WithObject
>());
1851 DebugScopes::onPopStrictEvalScope(AbstractFramePtr frame
)
1853 DebugScopes
*scopes
= frame
.compartment()->debugScopes
;
1858 * The StackFrame may be observed before the prologue has created the
1859 * CallObject. See ScopeIter::settle.
1861 if (frame
.hasCallObj())
1862 scopes
->liveScopes
.remove(&frame
.scopeChain()->as
<CallObject
>());
1866 DebugScopes::onGeneratorFrameChange(AbstractFramePtr from
, AbstractFramePtr to
, JSContext
*cx
)
1868 for (ScopeIter
toIter(to
, cx
); !toIter
.done(); ++toIter
) {
1869 DebugScopes
*scopes
= ensureCompartmentData(cx
);
1873 if (toIter
.hasScopeObject()) {
1875 * Not only must we correctly replace mappings [scope -> from] with
1876 * mappings [scope -> to], but we must add [scope -> to] if it
1877 * doesn't already exist so that if we need to proxy a generator's
1878 * scope while it is suspended, we can find its frame (which would
1879 * otherwise not be found by AllFramesIter).
1881 JS_ASSERT(toIter
.scope().compartment() == cx
->compartment());
1882 LiveScopeMap::AddPtr livePtr
= scopes
->liveScopes
.lookupForAdd(&toIter
.scope());
1884 livePtr
->value() = to
;
1886 scopes
->liveScopes
.add(livePtr
, &toIter
.scope(), to
); // OOM here?
1887 liveScopesPostWriteBarrier(cx
->runtime(), &scopes
->liveScopes
, &toIter
.scope());
1890 ScopeIter
si(toIter
, from
, cx
);
1891 JS_ASSERT(si
.frame().scopeChain()->compartment() == cx
->compartment());
1892 if (MissingScopeMap::Ptr p
= scopes
->missingScopes
.lookup(si
)) {
1893 DebugScopeObject
&debugScope
= *p
->value();
1894 scopes
->liveScopes
.lookup(&debugScope
.scope())->value() = to
;
1895 scopes
->missingScopes
.remove(p
);
1896 scopes
->missingScopes
.put(toIter
, &debugScope
); // OOM here?
1903 DebugScopes::onCompartmentLeaveDebugMode(JSCompartment
*c
)
1905 DebugScopes
*scopes
= c
->debugScopes
;
1907 scopes
->proxiedScopes
.clear();
1908 scopes
->missingScopes
.clear();
1909 scopes
->liveScopes
.clear();
1914 DebugScopes::updateLiveScopes(JSContext
*cx
)
1916 JS_CHECK_RECURSION(cx
, return false);
1919 * Note that we must always update the top frame's scope objects' entries
1920 * in liveScopes because we can't be sure code hasn't run in that frame to
1921 * change the scope chain since we were last called. The fp->prevUpToDate()
1922 * flag indicates whether the scopes of frames older than fp are already
1923 * included in liveScopes. It might seem simpler to have fp instead carry a
1924 * flag indicating whether fp itself is accurately described, but then we
1925 * would need to clear that flag whenever fp ran code. By storing the 'up
1926 * to date' bit for fp->prev() in fp, simply popping fp effectively clears
1927 * the flag for us, at exactly the time when execution resumes fp->prev().
1929 for (AllFramesIter
i(cx
); !i
.done(); ++i
) {
1931 * Debug-mode currently disables Ion compilation in the compartment of
1937 AbstractFramePtr frame
= i
.abstractFramePtr();
1938 if (frame
.scopeChain()->compartment() != cx
->compartment())
1941 for (ScopeIter
si(frame
, cx
); !si
.done(); ++si
) {
1942 if (si
.hasScopeObject()) {
1943 JS_ASSERT(si
.scope().compartment() == cx
->compartment());
1944 DebugScopes
*scopes
= ensureCompartmentData(cx
);
1947 if (!scopes
->liveScopes
.put(&si
.scope(), frame
))
1949 liveScopesPostWriteBarrier(cx
->runtime(), &scopes
->liveScopes
, &si
.scope());
1953 if (frame
.prevUpToDate())
1955 JS_ASSERT(frame
.scopeChain()->compartment()->debugMode());
1956 frame
.setPrevUpToDate();
1963 DebugScopes::hasLiveFrame(ScopeObject
&scope
)
1965 DebugScopes
*scopes
= scope
.compartment()->debugScopes
;
1967 return NullFramePtr();
1969 if (LiveScopeMap::Ptr p
= scopes
->liveScopes
.lookup(&scope
)) {
1970 AbstractFramePtr frame
= p
->value();
1973 * Since liveScopes is effectively a weak pointer, we need a read
1974 * barrier. The scenario where this is necessary is:
1975 * 1. GC starts, a suspended generator is not live
1976 * 2. hasLiveFrame returns a StackFrame* to the (soon to be dead)
1977 * suspended generator
1978 * 3. stack frame values (which will neve be marked) are read from the
1980 * 4. GC completes, live objects may now point to values that weren't
1981 * marked and thus may point to swept GC things
1983 if (JSGenerator
*gen
= frame
.maybeSuspendedGenerator(scope
.compartment()->runtimeFromMainThread()))
1984 JSObject::readBarrier(gen
->obj
);
1988 return NullFramePtr();
1991 /*****************************************************************************/
1994 GetDebugScope(JSContext
*cx
, const ScopeIter
&si
);
1996 static DebugScopeObject
*
1997 GetDebugScopeForScope(JSContext
*cx
, Handle
<ScopeObject
*> scope
, const ScopeIter
&enclosing
)
1999 if (DebugScopeObject
*debugScope
= DebugScopes::hasDebugScope(cx
, *scope
))
2002 RootedObject
enclosingDebug(cx
, GetDebugScope(cx
, enclosing
));
2003 if (!enclosingDebug
)
2006 JSObject
&maybeDecl
= scope
->enclosingScope();
2007 if (maybeDecl
.is
<DeclEnvObject
>()) {
2008 JS_ASSERT(CallObjectLambdaName(scope
->as
<CallObject
>().callee()));
2009 enclosingDebug
= DebugScopeObject::create(cx
, maybeDecl
.as
<DeclEnvObject
>(), enclosingDebug
);
2010 if (!enclosingDebug
)
2014 DebugScopeObject
*debugScope
= DebugScopeObject::create(cx
, *scope
, enclosingDebug
);
2018 if (!DebugScopes::addDebugScope(cx
, *scope
, *debugScope
))
2024 static DebugScopeObject
*
2025 GetDebugScopeForMissing(JSContext
*cx
, const ScopeIter
&si
)
2027 if (DebugScopeObject
*debugScope
= DebugScopes::hasDebugScope(cx
, si
))
2030 ScopeIter
copy(si
, cx
);
2031 RootedObject
enclosingDebug(cx
, GetDebugScope(cx
, ++copy
));
2032 if (!enclosingDebug
)
2036 * Create the missing scope object. For block objects, this takes care of
2037 * storing variable values after the StackFrame has been popped. For call
2038 * objects, we only use the pretend call object to access callee, bindings
2039 * and to receive dynamically added properties. Together, this provides the
2040 * nice invariant that every DebugScopeObject has a ScopeObject.
2042 * Note: to preserve scopeChain depth invariants, these lazily-reified
2043 * scopes must not be put on the frame's scope chain; instead, they are
2044 * maintained via DebugScopes hooks.
2046 DebugScopeObject
*debugScope
= nullptr;
2047 switch (si
.type()) {
2048 case ScopeIter::Call
: {
2049 Rooted
<CallObject
*> callobj(cx
, CallObject::createForFunction(cx
, si
.frame()));
2053 if (callobj
->enclosingScope().is
<DeclEnvObject
>()) {
2054 JS_ASSERT(CallObjectLambdaName(callobj
->callee()));
2055 DeclEnvObject
&declenv
= callobj
->enclosingScope().as
<DeclEnvObject
>();
2056 enclosingDebug
= DebugScopeObject::create(cx
, declenv
, enclosingDebug
);
2057 if (!enclosingDebug
)
2061 debugScope
= DebugScopeObject::create(cx
, *callobj
, enclosingDebug
);
2064 case ScopeIter::Block
: {
2065 Rooted
<StaticBlockObject
*> staticBlock(cx
, &si
.staticBlock());
2066 ClonedBlockObject
*block
= ClonedBlockObject::create(cx
, staticBlock
, si
.frame());
2070 debugScope
= DebugScopeObject::create(cx
, *block
, enclosingDebug
);
2073 case ScopeIter::With
:
2074 case ScopeIter::StrictEvalScope
:
2075 MOZ_ASSUME_UNREACHABLE("should already have a scope");
2080 if (!DebugScopes::addDebugScope(cx
, si
, *debugScope
))
2087 GetDebugScope(JSContext
*cx
, JSObject
&obj
)
2090 * As an engine invariant (maintained internally and asserted by Execute),
2091 * ScopeObjects and non-ScopeObjects cannot be interleaved on the scope
2092 * chain; every scope chain must start with zero or more ScopeObjects and
2093 * terminate with one or more non-ScopeObjects (viz., GlobalObject).
2095 if (!obj
.is
<ScopeObject
>()) {
2098 while ((o
= o
->enclosingScope()))
2099 JS_ASSERT(!o
->is
<ScopeObject
>());
2104 Rooted
<ScopeObject
*> scope(cx
, &obj
.as
<ScopeObject
>());
2105 if (AbstractFramePtr frame
= DebugScopes::hasLiveFrame(*scope
)) {
2106 ScopeIter
si(frame
, *scope
, cx
);
2107 return GetDebugScope(cx
, si
);
2109 ScopeIter
si(scope
->enclosingScope(), cx
);
2110 return GetDebugScopeForScope(cx
, scope
, si
);
2114 GetDebugScope(JSContext
*cx
, const ScopeIter
&si
)
2116 JS_CHECK_RECURSION(cx
, return nullptr);
2119 return GetDebugScope(cx
, si
.enclosingScope());
2121 if (!si
.hasScopeObject())
2122 return GetDebugScopeForMissing(cx
, si
);
2124 Rooted
<ScopeObject
*> scope(cx
, &si
.scope());
2126 ScopeIter
copy(si
, cx
);
2127 return GetDebugScopeForScope(cx
, scope
, ++copy
);
2131 js::GetDebugScopeForFunction(JSContext
*cx
, HandleFunction fun
)
2133 assertSameCompartment(cx
, fun
);
2134 JS_ASSERT(cx
->compartment()->debugMode());
2135 if (!DebugScopes::updateLiveScopes(cx
))
2137 return GetDebugScope(cx
, *fun
->environment());
2141 js::GetDebugScopeForFrame(JSContext
*cx
, AbstractFramePtr frame
)
2143 assertSameCompartment(cx
, frame
);
2144 if (CanUseDebugScopeMaps(cx
) && !DebugScopes::updateLiveScopes(cx
))
2146 ScopeIter
si(frame
, cx
);
2147 return GetDebugScope(cx
, si
);
2152 typedef HashSet
<PropertyName
*> PropertyNameSet
;
2155 RemoveReferencedNames(JSContext
*cx
, HandleScript script
, PropertyNameSet
&remainingNames
)
2157 // Remove from remainingNames --- the closure variables in some outer
2158 // script --- any free variables in this script. This analysis isn't perfect:
2160 // - It will not account for free variables in an inner script which are
2161 // actually accessing some name in an intermediate script between the
2162 // inner and outer scripts. This can cause remainingNames to be an
2163 // underapproximation.
2165 // - It will not account for new names introduced via eval. This can cause
2166 // remainingNames to be an overapproximation. This would be easy to fix
2167 // but is nice to have as the eval will probably not access these
2168 // these names and putting eval in an inner script is bad news if you
2169 // care about entraining variables unnecessarily.
2171 for (jsbytecode
*pc
= script
->code(); pc
!= script
->codeEnd(); pc
+= GetBytecodeLength(pc
)) {
2174 switch (JSOp(*pc
)) {
2178 name
= script
->getName(pc
);
2181 case JSOP_GETALIASEDVAR
:
2182 case JSOP_CALLALIASEDVAR
:
2183 case JSOP_SETALIASEDVAR
:
2184 name
= ScopeCoordinateName(cx
->runtime()->scopeCoordinateNameCache
, script
, pc
);
2193 remainingNames
.remove(name
);
2196 if (script
->hasObjects()) {
2197 ObjectArray
*objects
= script
->objects();
2198 for (size_t i
= 0; i
< objects
->length
; i
++) {
2199 JSObject
*obj
= objects
->vector
[i
];
2200 if (obj
->is
<JSFunction
>() && obj
->as
<JSFunction
>().isInterpreted()) {
2201 JSFunction
*fun
= &obj
->as
<JSFunction
>();
2202 RootedScript
innerScript(cx
, fun
->getOrCreateScript(cx
));
2206 if (!RemoveReferencedNames(cx
, innerScript
, remainingNames
))
2216 AnalyzeEntrainedVariablesInScript(JSContext
*cx
, HandleScript script
, HandleScript innerScript
)
2218 PropertyNameSet
remainingNames(cx
);
2219 if (!remainingNames
.init())
2222 for (BindingIter
bi(script
); bi
; bi
++) {
2223 if (bi
->aliased()) {
2224 PropertyNameSet::AddPtr p
= remainingNames
.lookupForAdd(bi
->name());
2225 if (!p
&& !remainingNames
.add(p
, bi
->name()))
2230 if (!RemoveReferencedNames(cx
, innerScript
, remainingNames
))
2233 if (!remainingNames
.empty()) {
2238 buf
.printf("Script ");
2240 if (JSAtom
*name
= script
->function()->displayAtom()) {
2241 buf
.putString(name
);
2245 buf
.printf("(%s:%d) has variables entrained by ", script
->filename(), script
->lineno
);
2247 if (JSAtom
*name
= innerScript
->function()->displayAtom()) {
2248 buf
.putString(name
);
2252 buf
.printf("(%s:%d) ::", innerScript
->filename(), innerScript
->lineno
);
2254 for (PropertyNameSet::Range r
= remainingNames
.all(); !r
.empty(); r
.popFront()) {
2256 buf
.putString(r
.front());
2259 printf("%s\n", buf
.string());
2262 if (innerScript
->hasObjects()) {
2263 ObjectArray
*objects
= innerScript
->objects();
2264 for (size_t i
= 0; i
< objects
->length
; i
++) {
2265 JSObject
*obj
= objects
->vector
[i
];
2266 if (obj
->is
<JSFunction
>() && obj
->as
<JSFunction
>().isInterpreted()) {
2267 JSFunction
*fun
= &obj
->as
<JSFunction
>();
2268 RootedScript
innerInnerScript(cx
, fun
->nonLazyScript());
2269 if (!AnalyzeEntrainedVariablesInScript(cx
, script
, innerInnerScript
))
2278 // Look for local variables in script or any other script inner to it, which are
2279 // part of the script's call object and are unnecessarily entrained by their own
2280 // inner scripts which do not refer to those variables. An example is:
2284 // function bar() { return a; }
2285 // function baz() { return b; }
2288 // |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|.
2290 js::AnalyzeEntrainedVariables(JSContext
*cx
, HandleScript script
)
2292 if (!script
->hasObjects())
2295 ObjectArray
*objects
= script
->objects();
2296 for (size_t i
= 0; i
< objects
->length
; i
++) {
2297 JSObject
*obj
= objects
->vector
[i
];
2298 if (obj
->is
<JSFunction
>() && obj
->as
<JSFunction
>().isInterpreted()) {
2299 JSFunction
*fun
= &obj
->as
<JSFunction
>();
2300 RootedScript
innerScript(cx
, fun
->getOrCreateScript(cx
));
2304 if (script
->function() && script
->function()->isHeavyweight()) {
2305 if (!AnalyzeEntrainedVariablesInScript(cx
, script
, innerScript
))
2309 if (!AnalyzeEntrainedVariables(cx
, innerScript
))