Backed out changesets 22770b30545b, 0809370fabdb, 4ef4764c1b55, and bfe768b87464...
[gecko.git] / js / src / vm / ScopeObject.cpp
blob8e0aa3365a2df5702bdc2ff8c6812871e24a6e2d
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"
12 #include "jsiter.h"
14 #include "vm/ArgumentsObject.h"
15 #include "vm/GlobalObject.h"
16 #include "vm/ProxyObject.h"
17 #include "vm/Shape.h"
18 #include "vm/Xdr.h"
20 #include "jsatominlines.h"
21 #include "jsobjinlines.h"
22 #include "jsscriptinlines.h"
24 #include "vm/Stack-inl.h"
26 using namespace js;
27 using namespace js::types;
29 using mozilla::PodZero;
31 typedef Rooted<ArgumentsObject *> RootedArgumentsObject;
33 /*****************************************************************************/
35 static JSObject *
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>();
48 Shape *
49 js::ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc)
51 StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
52 ScopeCoordinate sc(pc);
53 while (true) {
54 if (ssi.hasDynamicScopeObject()) {
55 if (!sc.hops)
56 break;
57 sc.hops--;
59 ssi++;
61 return ssi.scopeShape();
64 static const uint32_t SCOPE_COORDINATE_NAME_THRESHOLD = 20;
66 void
67 ScopeCoordinateNameCache::purge()
69 shape = nullptr;
70 if (map.initialized())
71 map.finish();
74 PropertyName *
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) {
79 cache.purge();
80 if (cache.map.init(shape->slot())) {
81 cache.shape = shape;
82 Shape::Range<NoGC> r(shape);
83 while (!r.empty()) {
84 if (!cache.map.putNew(r.front().slot(), r.front().propid())) {
85 cache.purge();
86 break;
88 r.popFront();
93 jsid id;
94 ScopeCoordinate sc(pc);
95 if (shape == cache.shape) {
96 ScopeCoordinateNameCache::Map::Ptr p = cache.map.lookup(sc.slot);
97 id = p->value();
98 } else {
99 Shape::Range<NoGC> r(shape);
100 while (r.front().slot() != sc.slot)
101 r.popFront();
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();
111 JSScript *
112 js::ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc)
114 StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
115 ScopeCoordinate sc(pc);
116 while (true) {
117 if (ssi.hasDynamicScopeObject()) {
118 if (!sc.hops)
119 break;
120 sc.hops--;
122 ssi++;
124 if (ssi.type() != StaticScopeIter<NoGC>::FUNCTION)
125 return nullptr;
126 return ssi.funScript();
129 /*****************************************************************************/
131 void
132 ScopeObject::setEnclosingScope(HandleObject obj)
134 JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
135 obj->isDelegate());
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.
143 CallObject *
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);
152 if (!obj)
153 return nullptr;
155 if (script->treatAsRunOnce) {
156 RootedObject nobj(cx, obj);
157 if (!JSObject::setSingletonType(cx, nobj))
158 return nullptr;
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.
170 CallObject *
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));
177 if (!type)
178 return 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);
185 if (!obj)
186 return nullptr;
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
195 * must be null.
197 CallObject *
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);
202 if (!callobj)
203 return nullptr;
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))
211 return nullptr;
212 return ncallobj;
215 return callobj;
218 CallObject *
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);
230 if (!scopeChain)
231 return nullptr;
234 RootedScript script(cx, callee->nonLazyScript());
235 return create(cx, script, scopeChain, callee);
238 CallObject *
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);
248 if (!callobj)
249 return nullptr;
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));
257 return callobj;
260 CallObject *
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_ = {
274 "Call",
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 */
280 JS_EnumerateStub,
281 JS_ResolveStub,
282 nullptr /* convert: Leave it nullptr so we notice if calls ever escape */
285 const Class DeclEnvObject::class_ = {
286 js_Object_str,
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 */
293 JS_EnumerateStub,
294 JS_ResolveStub,
295 JS_ConvertStub
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.
303 DeclEnvObject *
304 DeclEnvObject::createTemplateObject(JSContext *cx, HandleFunction fun, gc::InitialHeap heap)
306 JS_ASSERT(IsNurseryAllocable(FINALIZE_KIND));
308 RootedTypeObject type(cx, cx->getNewType(&class_, nullptr));
309 if (!type)
310 return nullptr;
312 RootedShape emptyDeclEnvShape(cx);
313 emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &class_, nullptr,
314 cx->global(), nullptr, FINALIZE_KIND,
315 BaseShape::DELEGATE);
316 if (!emptyDeclEnvShape)
317 return nullptr;
319 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, heap, emptyDeclEnvShape, type));
320 if (!obj)
321 return nullptr;
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))
330 return nullptr;
333 JS_ASSERT(!obj->hasDynamicSlots());
334 return &obj->as<DeclEnvObject>();
337 DeclEnvObject *
338 DeclEnvObject::create(JSContext *cx, HandleObject enclosing, HandleFunction callee)
340 RootedObject obj(cx, createTemplateObject(cx, callee, gc::DefaultHeap));
341 if (!obj)
342 return nullptr;
344 obj->as<ScopeObject>().setEnclosingScope(enclosing);
345 obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
346 return &obj->as<DeclEnvObject>();
349 WithObject *
350 WithObject::create(JSContext *cx, HandleObject proto, HandleObject enclosing, uint32_t depth)
352 RootedTypeObject type(cx, cx->getNewType(&class_, proto.get()));
353 if (!type)
354 return nullptr;
356 RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(proto),
357 &enclosing->global(), nullptr,
358 FINALIZE_KIND));
359 if (!shape)
360 return nullptr;
362 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, type));
363 if (!obj)
364 return nullptr;
366 obj->as<ScopeObject>().setEnclosingScope(enclosing);
367 obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
369 JSObject *thisp = JSObject::thisObject(cx, proto);
370 if (!thisp)
371 return nullptr;
373 obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
375 return &obj->as<WithObject>();
378 static bool
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);
386 static bool
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);
394 static bool
395 with_LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
396 MutableHandleObject objp, MutableHandleShape propp)
398 RootedId id(cx);
399 if (!IndexToId(cx, index, id.address()))
400 return false;
401 return with_LookupGeneric(cx, obj, id, objp, propp);
404 static bool
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);
412 static bool
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);
420 static bool
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);
428 static bool
429 with_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
430 MutableHandleValue vp)
432 RootedId id(cx);
433 if (!IndexToId(cx, index, id.address()))
434 return false;
435 return with_GetGeneric(cx, obj, receiver, id, vp);
438 static bool
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);
446 static bool
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);
454 static bool
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);
462 static bool
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);
470 static bool
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);
478 static bool
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);
485 static bool
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);
492 static bool
493 with_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
494 bool *succeeded)
496 RootedObject actual(cx, &obj->as<WithObject>().object());
497 return JSObject::deleteProperty(cx, actual, name, succeeded);
500 static bool
501 with_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index,
502 bool *succeeded)
504 RootedObject actual(cx, &obj->as<WithObject>().object());
505 return JSObject::deleteElement(cx, actual, index, succeeded);
508 static bool
509 with_DeleteSpecial(JSContext *cx, HandleObject obj, HandleSpecialId sid,
510 bool *succeeded)
512 RootedObject actual(cx, &obj->as<WithObject>().object());
513 return JSObject::deleteSpecial(cx, actual, sid, succeeded);
516 static bool
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);
524 static JSObject *
525 with_ThisObject(JSContext *cx, HandleObject obj)
527 return &obj->as<WithObject>().withThis();
530 const Class WithObject::class_ = {
531 "With",
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 */
538 JS_EnumerateStub,
539 JS_ResolveStub,
540 JS_ConvertStub,
541 nullptr, /* finalize */
542 nullptr, /* checkAccess */
543 nullptr, /* call */
544 nullptr, /* hasInstance */
545 nullptr, /* construct */
546 nullptr, /* trace */
547 JS_NULL_CLASS_EXT,
549 with_LookupGeneric,
550 with_LookupProperty,
551 with_LookupElement,
552 with_LookupSpecial,
553 nullptr, /* defineGeneric */
554 nullptr, /* defineProperty */
555 nullptr, /* defineElement */
556 nullptr, /* defineSpecial */
557 with_GetGeneric,
558 with_GetProperty,
559 with_GetElement,
560 nullptr, /* getElementIfPresent */
561 with_GetSpecial,
562 with_SetGeneric,
563 with_SetProperty,
564 with_SetElement,
565 with_SetSpecial,
566 with_GetGenericAttributes,
567 with_SetGenericAttributes,
568 with_DeleteProperty,
569 with_DeleteElement,
570 with_DeleteSpecial,
571 nullptr, nullptr, /* watch/unwatch */
572 with_Enumerate,
573 with_ThisObject,
577 /*****************************************************************************/
579 ClonedBlockObject *
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()));
586 if (!type)
587 return nullptr;
589 RootedShape shape(cx, block->lastProperty());
591 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, type));
592 if (!obj)
593 return nullptr;
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))
600 return nullptr;
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>();
625 void
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);
636 StaticBlockObject *
637 StaticBlockObject::create(ExclusiveContext *cx)
639 RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, nullptr));
640 if (!type)
641 return nullptr;
643 RootedShape emptyBlockShape(cx);
644 emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockObject::class_, nullptr, nullptr,
645 nullptr, FINALIZE_KIND, BaseShape::DELEGATE);
646 if (!emptyBlockShape)
647 return nullptr;
649 JSObject *obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, type);
650 if (!obj)
651 return nullptr;
653 return &obj->as<StaticBlockObject>();
656 /* static */ Shape *
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));
662 *redeclared = false;
664 /* Inline JSObject::addProperty in order to trap the redefinition case. */
665 Shape **spp;
666 if (Shape::search(cx, block->lastProperty(), id, &spp, true)) {
667 *redeclared = true;
668 return nullptr;
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>(
677 cx, block, id,
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_ = {
685 "Block",
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 */
693 JS_EnumerateStub,
694 JS_ResolveStub,
695 JS_ConvertStub
698 template<XDRMode mode>
699 bool
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);
708 uint32_t count = 0;
709 uint32_t depthAndCount = 0;
711 if (mode == XDR_ENCODE) {
712 obj = *objp;
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);
722 if (!obj)
723 return false;
724 obj->initEnclosingStaticScope(enclosingScope);
725 *objp = obj;
728 if (!xdr->codeUint32(&depthAndCount))
729 return false;
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++) {
741 RootedAtom atom(cx);
742 if (!XDRAtom(xdr, &atom))
743 return false;
745 /* The empty string indicates an int id. */
746 RootedId id(cx, atom != cx->runtime()->emptyString
747 ? AtomToId(atom)
748 : INT_TO_JSID(i));
750 bool redeclared;
751 if (!StaticBlockObject::addVar(cx, obj, id, i, &redeclared)) {
752 JS_ASSERT(!redeclared);
753 return false;
756 uint32_t aliased;
757 if (!xdr->codeUint32(&aliased))
758 return false;
760 JS_ASSERT(aliased == 0 || aliased == 1);
761 obj->setAliased(i, !!aliased);
763 } else {
764 AutoShapeVector shapes(cx);
765 if (!shapes.growBy(count))
766 return false;
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);
778 RootedId propid(cx);
779 RootedAtom atom(cx);
780 for (unsigned i = 0; i < count; i++) {
781 shape = shapes[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))
793 return false;
795 uint32_t aliased = obj->isAliased(i);
796 if (!xdr->codeUint32(&aliased))
797 return false;
800 return true;
803 template bool
804 js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, StaticBlockObject **);
806 template bool
807 js::XDRStaticBlockObject(XDRState<XDR_DECODE> *, HandleObject, StaticBlockObject **);
809 JSObject *
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));
815 if (!clone)
816 return nullptr;
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()))
824 return nullptr;
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();
832 bool redeclared;
833 if (!StaticBlockObject::addVar(cx, clone, id, i, &redeclared)) {
834 JS_ASSERT(!redeclared);
835 return nullptr;
838 clone->setAliased(i, srcBlock->isAliased(i));
841 return clone;
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)
856 : cx(cx),
857 frame_(si.frame_),
858 cur_(cx, si.cur_),
859 block_(cx, si.block_),
860 type_(si.type_),
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)
868 : cx(cx),
869 frame_(NullFramePtr()),
870 cur_(cx, &enclosingScope),
871 block_(cx, nullptr),
872 type_(Type(-1))
874 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
877 ScopeIter::ScopeIter(AbstractFramePtr frame, JSContext *cx
878 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
879 : cx(cx),
880 frame_(frame),
881 cur_(cx, frame.scopeChain()),
882 block_(cx, frame.maybeBlockChain())
884 assertSameCompartment(cx, frame);
885 settle();
886 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
889 ScopeIter::ScopeIter(const ScopeIter &si, AbstractFramePtr frame, JSContext *cx
890 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
891 : cx(si.cx),
892 frame_(frame),
893 cur_(cx, si.cur_),
894 block_(cx, si.block_),
895 type_(si.type_),
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)
903 : cx(cx),
904 frame_(frame),
905 cur_(cx, &scope),
906 block_(cx)
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.:
913 * let (x = 1) {
914 * g = function() { eval('debugger') };
915 * let (y = 1) g();
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();
925 while (block_) {
926 if (block_->stackDepth() <= cur_->as<NestedScopeObject>().stackDepth())
927 break;
928 block_ = block_->enclosingBlock();
930 JS_ASSERT_IF(cur_->is<ClonedBlockObject>(),
931 cur_->as<ClonedBlockObject>().staticBlock() == *block_);
932 } else {
933 block_ = nullptr;
935 settle();
936 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
939 ScopeObject &
940 ScopeIter::scope() const
942 JS_ASSERT(hasScopeObject());
943 return cur_->as<ScopeObject>();
946 ScopeIter &
947 ScopeIter::operator++()
949 JS_ASSERT(!done());
950 switch (type_) {
951 case Call:
952 if (hasScopeObject_) {
953 cur_ = &cur_->as<CallObject>().enclosingScope();
954 if (CallObjectLambdaName(*frame_.fun()))
955 cur_ = &cur_->as<DeclEnvObject>().enclosingScope();
957 frame_ = NullFramePtr();
958 break;
959 case Block:
960 block_ = block_->enclosingBlock();
961 if (hasScopeObject_)
962 cur_ = &cur_->as<ClonedBlockObject>().enclosingScope();
963 settle();
964 break;
965 case With:
966 JS_ASSERT(hasScopeObject_);
967 cur_ = &cur_->as<WithObject>().enclosingScope();
968 settle();
969 break;
970 case StrictEvalScope:
971 if (hasScopeObject_)
972 cur_ = &cur_->as<CallObject>().enclosingScope();
973 frame_ = NullFramePtr();
974 break;
976 return *this;
979 void
980 ScopeIter::settle()
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()) {
1006 if (block_) {
1007 type_ = Block;
1008 hasScopeObject_ = block_->needsClone();
1009 } else {
1010 type_ = Call;
1011 hasScopeObject_ = false;
1013 } else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrevScopeChain(cx)) {
1014 if (block_) {
1015 JS_ASSERT(!block_->needsClone());
1016 type_ = Block;
1017 hasScopeObject_ = false;
1018 } else {
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());
1031 type_ = With;
1032 hasScopeObject_ = true;
1033 } else if (block_) {
1034 type_ = 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());
1042 } else {
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_;
1056 /* static */ bool
1057 ScopeIterKey::match(ScopeIterKey si1, ScopeIterKey si2)
1059 /* hasScopeObject_ is determined by the other fields. */
1060 return si1.frame_ == si2.frame_ &&
1061 (!si1.frame_ ||
1062 (si1.cur_ == si2.cur_ &&
1063 si1.block_ == si2.block_ &&
1064 si1.type_ == si2.type_));
1067 /*****************************************************************************/
1069 namespace {
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
1104 * variables:
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))
1127 return false;
1129 Bindings &bindings = script->bindings;
1130 BindingIter bi(script);
1131 while (bi && NameToId(bi->name()) != id)
1132 bi++;
1133 if (!bi)
1134 return false;
1136 if (bi->kind() == VARIABLE || bi->kind() == CONSTANT) {
1137 unsigned i = bi.frameIndex();
1138 if (script->varIsAliased(i))
1139 return false;
1141 if (maybeframe) {
1142 if (action == GET)
1143 vp.set(maybeframe.unaliasedVar(i));
1144 else
1145 maybeframe.unaliasedVar(i) = vp;
1146 } else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
1147 if (action == GET)
1148 vp.set(snapshot->getDenseElement(bindings.numArgs() + i));
1149 else
1150 snapshot->setDenseElement(bindings.numArgs() + i, vp);
1151 } else {
1152 /* The unaliased value has been lost to the debugger. */
1153 if (action == GET)
1154 vp.set(UndefinedValue());
1156 } else {
1157 JS_ASSERT(bi->kind() == ARGUMENT);
1158 unsigned i = bi.frameIndex();
1159 if (script->formalIsAliased(i))
1160 return false;
1162 if (maybeframe) {
1163 if (script->argsObjAliasesFormals() && maybeframe.hasArgsObj()) {
1164 if (action == GET)
1165 vp.set(maybeframe.argsObj().arg(i));
1166 else
1167 maybeframe.argsObj().setArg(i, vp);
1168 } else {
1169 if (action == GET)
1170 vp.set(maybeframe.unaliasedFormal(i, DONT_CHECK_ALIASING));
1171 else
1172 maybeframe.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
1174 } else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
1175 if (action == GET)
1176 vp.set(snapshot->getDenseElement(i));
1177 else
1178 snapshot->setDenseElement(i, vp);
1179 } else {
1180 /* The unaliased value has been lost to the debugger. */
1181 if (action == GET)
1182 vp.set(UndefinedValue());
1185 if (action == SET)
1186 TypeScript::SetArgument(cx, script, i, vp);
1189 return true;
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);
1196 if (!shape)
1197 return false;
1199 unsigned i = shape->shortid();
1200 if (block->staticBlock().isAliased(i))
1201 return false;
1203 if (maybeframe) {
1204 JSScript *script = maybeframe.script();
1205 unsigned local = block->slotToLocalIndex(script->bindings, shape->slot());
1206 if (action == GET)
1207 vp.set(maybeframe.unaliasedLocal(local));
1208 else
1209 maybeframe.unaliasedLocal(local) = vp;
1210 JS_ASSERT(analyze::LocalSlot(script, local) >= analyze::TotalSlots(script));
1211 } else {
1212 if (action == GET)
1213 vp.set(block->var(i, DONT_CHECK_ALIASING));
1214 else
1215 block->setVar(i, vp, DONT_CHECK_ALIASING);
1218 return true;
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());
1224 return false;
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))
1261 return true;
1263 if (scope.as<CallObject>().callee().nonLazyScript()->needsArgsObj())
1264 return true;
1266 AbstractFramePtr maybeframe = DebugScopes::hasLiveFrame(scope);
1267 if (!maybeframe) {
1268 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
1269 "Debugger scope");
1270 return false;
1273 *maybeArgsObj = ArgumentsObject::createUnexpected(cx, maybeframe);
1274 return true;
1277 public:
1278 static int family;
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
1286 // proxies
1287 *extensible = true;
1288 return true;
1291 bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE
1293 // See above.
1294 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
1295 return false;
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()))
1314 return false;
1316 if (maybeArgsObj) {
1317 desc.object().set(debugScope);
1318 desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
1319 desc.value().setObject(*maybeArgsObj);
1320 desc.setShortId(0);
1321 desc.setGetter(nullptr);
1322 desc.setSetter(nullptr);
1323 return true;
1326 RootedValue v(cx);
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);
1331 desc.setShortId(0);
1332 desc.setGetter(nullptr);
1333 desc.setSetter(nullptr);
1334 return true;
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()))
1348 return false;
1350 if (maybeArgsObj) {
1351 vp.set(ObjectValue(*maybeArgsObj));
1352 return true;
1355 if (handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp))
1356 return true;
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))
1367 return true;
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());
1376 bool found;
1377 if (!has(cx, proxy, id, &found))
1378 return false;
1379 if (found)
1380 return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
1382 return JS_DefinePropertyById(cx, scope, id, desc.value(), desc.getter(), desc.setter(),
1383 desc.attributes());
1386 bool getScopePropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props,
1387 unsigned flags)
1389 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
1391 if (isMissingArgumentsBinding(*scope)) {
1392 if (!props.append(NameToId(cx->names().arguments)))
1393 return false;
1396 if (!GetPropertyNames(cx, scope, flags, &props))
1397 return false;
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())))
1407 return false;
1411 return true;
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)) {
1430 *bp = true;
1431 return true;
1434 bool found;
1435 RootedObject scope(cx, &scopeObj);
1436 if (!JS_HasPropertyById(cx, scope, id, &found))
1437 return false;
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) {
1447 found = true;
1448 break;
1453 *bp = found;
1454 return true;
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());
1477 if (!obj)
1478 return nullptr;
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());
1486 return debugScope;
1489 ScopeObject &
1490 DebugScopeObject::scope() const
1492 return target()->as<ScopeObject>();
1495 JSObject &
1496 DebugScopeObject::enclosingScope() const
1498 return extra(ENCLOSING_EXTRA).toObject();
1501 JSObject *
1502 DebugScopeObject::maybeSnapshot() const
1504 JS_ASSERT(!scope().as<CallObject>().isForEval());
1505 return extra(SNAPSHOT_EXTRA).toObjectOrNull();
1508 void
1509 DebugScopeObject::initSnapshot(JSObject &o)
1511 JS_ASSERT(maybeSnapshot() == nullptr);
1512 setExtra(SNAPSHOT_EXTRA, ObjectValue(o));
1515 bool
1516 DebugScopeObject::isForDeclarative() const
1518 ScopeObject &s = scope();
1519 return s.is<CallObject>() || s.is<BlockObject>() || s.is<DeclEnvObject>();
1522 bool
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()));
1544 #endif
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));
1553 #endif
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);
1568 bool
1569 DebugScopes::init()
1571 if (!liveScopes.init() ||
1572 !proxiedScopes.init() ||
1573 !missingScopes.init())
1575 return false;
1577 return true;
1580 void
1581 DebugScopes::mark(JSTracer *trc)
1583 proxiedScopes.trace(trc);
1586 void
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()))
1596 e.removeFront();
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)) {
1608 e.removeFront();
1609 continue;
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)) {
1620 e.removeFront();
1621 continue;
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.
1634 static bool
1635 CanUseDebugScopeMaps(JSContext *cx)
1637 return cx->compartment()->debugMode();
1640 DebugScopes *
1641 DebugScopes::ensureCompartmentData(JSContext *cx)
1643 JSCompartment *c = cx->compartment();
1644 if (c->debugScopes)
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);
1652 return nullptr;
1655 DebugScopeObject *
1656 DebugScopes::hasDebugScope(JSContext *cx, ScopeObject &scope)
1658 DebugScopes *scopes = scope.compartment()->debugScopes;
1659 if (!scopes)
1660 return nullptr;
1662 if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&scope)) {
1663 JS_ASSERT(CanUseDebugScopeMaps(cx));
1664 return &p->value()->as<DebugScopeObject>();
1667 return nullptr;
1670 bool
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))
1677 return true;
1679 DebugScopes *scopes = ensureCompartmentData(cx);
1680 if (!scopes)
1681 return false;
1683 JS_ASSERT(!scopes->proxiedScopes.has(&scope));
1684 if (!scopes->proxiedScopes.put(&scope, &debugScope)) {
1685 js_ReportOutOfMemory(cx);
1686 return false;
1689 proxiedScopesPostWriteBarrier(cx->runtime(), &scopes->proxiedScopes, &scope);
1690 return true;
1693 DebugScopeObject *
1694 DebugScopes::hasDebugScope(JSContext *cx, const ScopeIter &si)
1696 JS_ASSERT(!si.hasScopeObject());
1698 DebugScopes *scopes = cx->compartment()->debugScopes;
1699 if (!scopes)
1700 return nullptr;
1702 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
1703 JS_ASSERT(CanUseDebugScopeMaps(cx));
1704 return p->value();
1706 return nullptr;
1709 bool
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))
1716 return true;
1718 DebugScopes *scopes = ensureCompartmentData(cx);
1719 if (!scopes)
1720 return false;
1722 JS_ASSERT(!scopes->missingScopes.has(si));
1723 if (!scopes->missingScopes.put(si, &debugScope)) {
1724 js_ReportOutOfMemory(cx);
1725 return false;
1728 JS_ASSERT(!scopes->liveScopes.has(&debugScope.scope()));
1729 if (!scopes->liveScopes.put(&debugScope.scope(), si.frame())) {
1730 js_ReportOutOfMemory(cx);
1731 return false;
1733 liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &debugScope.scope());
1735 return true;
1738 void
1739 DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
1741 JS_ASSERT(!frame.isYielding());
1742 assertSameCompartment(cx, frame);
1744 DebugScopes *scopes = cx->compartment()->debugScopes;
1745 if (!scopes)
1746 return;
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())
1756 return;
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>();
1762 } else {
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.
1781 if (debugScope) {
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)
1789 return;
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()));
1808 if (!snapshot) {
1809 cx->clearPendingException();
1810 return;
1813 debugScope->initSnapshot(*snapshot);
1817 void
1818 DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame)
1820 assertSameCompartment(cx, frame);
1822 DebugScopes *scopes = cx->compartment()->debugScopes;
1823 if (!scopes)
1824 return;
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);
1831 } else {
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);
1842 void
1843 DebugScopes::onPopWith(AbstractFramePtr frame)
1845 DebugScopes *scopes = frame.compartment()->debugScopes;
1846 if (scopes)
1847 scopes->liveScopes.remove(&frame.scopeChain()->as<WithObject>());
1850 void
1851 DebugScopes::onPopStrictEvalScope(AbstractFramePtr frame)
1853 DebugScopes *scopes = frame.compartment()->debugScopes;
1854 if (!scopes)
1855 return;
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>());
1865 void
1866 DebugScopes::onGeneratorFrameChange(AbstractFramePtr from, AbstractFramePtr to, JSContext *cx)
1868 for (ScopeIter toIter(to, cx); !toIter.done(); ++toIter) {
1869 DebugScopes *scopes = ensureCompartmentData(cx);
1870 if (!scopes)
1871 return;
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());
1883 if (livePtr) {
1884 livePtr->value() = to;
1885 } else {
1886 scopes->liveScopes.add(livePtr, &toIter.scope(), to); // OOM here?
1887 liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &toIter.scope());
1889 } else {
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?
1902 void
1903 DebugScopes::onCompartmentLeaveDebugMode(JSCompartment *c)
1905 DebugScopes *scopes = c->debugScopes;
1906 if (scopes) {
1907 scopes->proxiedScopes.clear();
1908 scopes->missingScopes.clear();
1909 scopes->liveScopes.clear();
1913 bool
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
1932 * the debuggee.
1934 if (i.isIon())
1935 continue;
1937 AbstractFramePtr frame = i.abstractFramePtr();
1938 if (frame.scopeChain()->compartment() != cx->compartment())
1939 continue;
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);
1945 if (!scopes)
1946 return false;
1947 if (!scopes->liveScopes.put(&si.scope(), frame))
1948 return false;
1949 liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &si.scope());
1953 if (frame.prevUpToDate())
1954 return true;
1955 JS_ASSERT(frame.scopeChain()->compartment()->debugMode());
1956 frame.setPrevUpToDate();
1959 return true;
1962 AbstractFramePtr
1963 DebugScopes::hasLiveFrame(ScopeObject &scope)
1965 DebugScopes *scopes = scope.compartment()->debugScopes;
1966 if (!scopes)
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
1979 * StackFrame
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);
1986 return frame;
1988 return NullFramePtr();
1991 /*****************************************************************************/
1993 static JSObject *
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))
2000 return debugScope;
2002 RootedObject enclosingDebug(cx, GetDebugScope(cx, enclosing));
2003 if (!enclosingDebug)
2004 return nullptr;
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)
2011 return nullptr;
2014 DebugScopeObject *debugScope = DebugScopeObject::create(cx, *scope, enclosingDebug);
2015 if (!debugScope)
2016 return nullptr;
2018 if (!DebugScopes::addDebugScope(cx, *scope, *debugScope))
2019 return nullptr;
2021 return debugScope;
2024 static DebugScopeObject *
2025 GetDebugScopeForMissing(JSContext *cx, const ScopeIter &si)
2027 if (DebugScopeObject *debugScope = DebugScopes::hasDebugScope(cx, si))
2028 return debugScope;
2030 ScopeIter copy(si, cx);
2031 RootedObject enclosingDebug(cx, GetDebugScope(cx, ++copy));
2032 if (!enclosingDebug)
2033 return nullptr;
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()));
2050 if (!callobj)
2051 return nullptr;
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)
2058 return nullptr;
2061 debugScope = DebugScopeObject::create(cx, *callobj, enclosingDebug);
2062 break;
2064 case ScopeIter::Block: {
2065 Rooted<StaticBlockObject *> staticBlock(cx, &si.staticBlock());
2066 ClonedBlockObject *block = ClonedBlockObject::create(cx, staticBlock, si.frame());
2067 if (!block)
2068 return nullptr;
2070 debugScope = DebugScopeObject::create(cx, *block, enclosingDebug);
2071 break;
2073 case ScopeIter::With:
2074 case ScopeIter::StrictEvalScope:
2075 MOZ_ASSUME_UNREACHABLE("should already have a scope");
2077 if (!debugScope)
2078 return nullptr;
2080 if (!DebugScopes::addDebugScope(cx, si, *debugScope))
2081 return nullptr;
2083 return debugScope;
2086 static JSObject *
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>()) {
2096 #ifdef DEBUG
2097 JSObject *o = &obj;
2098 while ((o = o->enclosingScope()))
2099 JS_ASSERT(!o->is<ScopeObject>());
2100 #endif
2101 return &obj;
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);
2113 static JSObject *
2114 GetDebugScope(JSContext *cx, const ScopeIter &si)
2116 JS_CHECK_RECURSION(cx, return nullptr);
2118 if (si.done())
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);
2130 JSObject *
2131 js::GetDebugScopeForFunction(JSContext *cx, HandleFunction fun)
2133 assertSameCompartment(cx, fun);
2134 JS_ASSERT(cx->compartment()->debugMode());
2135 if (!DebugScopes::updateLiveScopes(cx))
2136 return nullptr;
2137 return GetDebugScope(cx, *fun->environment());
2140 JSObject *
2141 js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame)
2143 assertSameCompartment(cx, frame);
2144 if (CanUseDebugScopeMaps(cx) && !DebugScopes::updateLiveScopes(cx))
2145 return nullptr;
2146 ScopeIter si(frame, cx);
2147 return GetDebugScope(cx, si);
2150 #ifdef DEBUG
2152 typedef HashSet<PropertyName *> PropertyNameSet;
2154 static bool
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)) {
2172 PropertyName *name;
2174 switch (JSOp(*pc)) {
2175 case JSOP_NAME:
2176 case JSOP_CALLNAME:
2177 case JSOP_SETNAME:
2178 name = script->getName(pc);
2179 break;
2181 case JSOP_GETALIASEDVAR:
2182 case JSOP_CALLALIASEDVAR:
2183 case JSOP_SETALIASEDVAR:
2184 name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
2185 break;
2187 default:
2188 name = nullptr;
2189 break;
2192 if (name)
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));
2203 if (!innerScript)
2204 return false;
2206 if (!RemoveReferencedNames(cx, innerScript, remainingNames))
2207 return false;
2212 return true;
2215 static bool
2216 AnalyzeEntrainedVariablesInScript(JSContext *cx, HandleScript script, HandleScript innerScript)
2218 PropertyNameSet remainingNames(cx);
2219 if (!remainingNames.init())
2220 return false;
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()))
2226 return false;
2230 if (!RemoveReferencedNames(cx, innerScript, remainingNames))
2231 return false;
2233 if (!remainingNames.empty()) {
2234 Sprinter buf(cx);
2235 if (!buf.init())
2236 return false;
2238 buf.printf("Script ");
2240 if (JSAtom *name = script->function()->displayAtom()) {
2241 buf.putString(name);
2242 buf.printf(" ");
2245 buf.printf("(%s:%d) has variables entrained by ", script->filename(), script->lineno);
2247 if (JSAtom *name = innerScript->function()->displayAtom()) {
2248 buf.putString(name);
2249 buf.printf(" ");
2252 buf.printf("(%s:%d) ::", innerScript->filename(), innerScript->lineno);
2254 for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) {
2255 buf.printf(" ");
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))
2270 return false;
2275 return true;
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:
2282 // function foo() {
2283 // var a, b;
2284 // function bar() { return a; }
2285 // function baz() { return b; }
2286 // }
2288 // |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|.
2289 bool
2290 js::AnalyzeEntrainedVariables(JSContext *cx, HandleScript script)
2292 if (!script->hasObjects())
2293 return true;
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));
2301 if (!innerScript)
2302 return false;
2304 if (script->function() && script->function()->isHeavyweight()) {
2305 if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript))
2306 return false;
2309 if (!AnalyzeEntrainedVariables(cx, innerScript))
2310 return false;
2314 return true;
2317 #endif