Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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;
32 typedef MutableHandle<ArgumentsObject *> MutableHandleArgumentsObject;
34 /*****************************************************************************/
36 static JSObject *
37 InnermostStaticScope(JSScript *script, jsbytecode *pc)
38 {
39 JS_ASSERT(script->containsPC(pc));
40 JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
42 NestedScopeObject *scope = script->getStaticScope(pc);
43 if (scope)
44 return scope;
45 return script->functionNonDelazifying();
46 }
48 Shape *
49 js::ScopeCoordinateToStaticScopeShape(JSScript *script, jsbytecode *pc)
50 {
51 StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
52 uint32_t hops = ScopeCoordinate(pc).hops();
53 while (true) {
54 JS_ASSERT(!ssi.done());
55 if (ssi.hasDynamicScopeObject()) {
56 if (!hops)
57 break;
58 hops--;
59 }
60 ssi++;
61 }
62 return ssi.scopeShape();
63 }
65 static const uint32_t SCOPE_COORDINATE_NAME_THRESHOLD = 20;
67 void
68 ScopeCoordinateNameCache::purge()
69 {
70 shape = nullptr;
71 if (map.initialized())
72 map.finish();
73 }
75 PropertyName *
76 js::ScopeCoordinateName(ScopeCoordinateNameCache &cache, JSScript *script, jsbytecode *pc)
77 {
78 Shape *shape = ScopeCoordinateToStaticScopeShape(script, pc);
79 if (shape != cache.shape && shape->slot() >= SCOPE_COORDINATE_NAME_THRESHOLD) {
80 cache.purge();
81 if (cache.map.init(shape->slot())) {
82 cache.shape = shape;
83 Shape::Range<NoGC> r(shape);
84 while (!r.empty()) {
85 if (!cache.map.putNew(r.front().slot(), r.front().propid())) {
86 cache.purge();
87 break;
88 }
89 r.popFront();
90 }
91 }
92 }
94 jsid id;
95 ScopeCoordinate sc(pc);
96 if (shape == cache.shape) {
97 ScopeCoordinateNameCache::Map::Ptr p = cache.map.lookup(sc.slot());
98 id = p->value();
99 } else {
100 Shape::Range<NoGC> r(shape);
101 while (r.front().slot() != sc.slot())
102 r.popFront();
103 id = r.front().propidRaw();
104 }
106 /* Beware nameless destructuring formal. */
107 if (!JSID_IS_ATOM(id))
108 return script->runtimeFromAnyThread()->commonNames->empty;
109 return JSID_TO_ATOM(id)->asPropertyName();
110 }
112 JSScript *
113 js::ScopeCoordinateFunctionScript(JSScript *script, jsbytecode *pc)
114 {
115 StaticScopeIter<NoGC> ssi(InnermostStaticScope(script, pc));
116 uint32_t hops = ScopeCoordinate(pc).hops();
117 while (true) {
118 if (ssi.hasDynamicScopeObject()) {
119 if (!hops)
120 break;
121 hops--;
122 }
123 ssi++;
124 }
125 if (ssi.type() != StaticScopeIter<NoGC>::FUNCTION)
126 return nullptr;
127 return ssi.funScript();
128 }
130 /*****************************************************************************/
132 void
133 ScopeObject::setEnclosingScope(HandleObject obj)
134 {
135 JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
136 obj->isDelegate());
137 setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
138 }
140 CallObject *
141 CallObject::create(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots)
142 {
143 MOZ_ASSERT(!type->singleton(),
144 "passed a singleton type to create() (use createSingleton() "
145 "instead)");
146 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
147 MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
148 kind = gc::GetBackgroundAllocKind(kind);
150 JSObject *obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, type, slots);
151 if (!obj)
152 return nullptr;
154 return &obj->as<CallObject>();
155 }
157 CallObject *
158 CallObject::createSingleton(JSContext *cx, HandleShape shape, HeapSlot *slots)
159 {
160 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
161 MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
162 kind = gc::GetBackgroundAllocKind(kind);
164 RootedTypeObject type(cx, cx->getSingletonType(&class_, nullptr));
165 if (!type)
166 return nullptr;
167 RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, type, slots));
168 if (!obj)
169 return nullptr;
171 MOZ_ASSERT(obj->hasSingletonType(),
172 "type created inline above must be a singleton");
174 return &obj->as<CallObject>();
175 }
177 /*
178 * Create a CallObject for a JSScript that is not initialized to any particular
179 * callsite. This object can either be initialized (with an enclosing scope and
180 * callee) or used as a template for jit compilation.
181 */
182 CallObject *
183 CallObject::createTemplateObject(JSContext *cx, HandleScript script, gc::InitialHeap heap)
184 {
185 RootedShape shape(cx, script->bindings.callObjShape());
186 JS_ASSERT(shape->getObjectClass() == &class_);
188 RootedTypeObject type(cx, cx->getNewType(&class_, nullptr));
189 if (!type)
190 return nullptr;
192 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
193 JS_ASSERT(CanBeFinalizedInBackground(kind, &class_));
194 kind = gc::GetBackgroundAllocKind(kind);
196 JSObject *obj = JSObject::create(cx, kind, heap, shape, type);
197 if (!obj)
198 return nullptr;
200 return &obj->as<CallObject>();
201 }
203 /*
204 * Construct a call object for the given bindings. If this is a call object
205 * for a function invocation, callee should be the function being called.
206 * Otherwise it must be a call object for eval of strict mode code, and callee
207 * must be null.
208 */
209 CallObject *
210 CallObject::create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee)
211 {
212 gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap;
213 CallObject *callobj = CallObject::createTemplateObject(cx, script, heap);
214 if (!callobj)
215 return nullptr;
217 callobj->as<ScopeObject>().setEnclosingScope(enclosing);
218 callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
220 if (script->treatAsRunOnce()) {
221 Rooted<CallObject*> ncallobj(cx, callobj);
222 if (!JSObject::setSingletonType(cx, ncallobj))
223 return nullptr;
224 return ncallobj;
225 }
227 return callobj;
228 }
230 CallObject *
231 CallObject::createForFunction(JSContext *cx, HandleObject enclosing, HandleFunction callee)
232 {
233 RootedObject scopeChain(cx, enclosing);
234 JS_ASSERT(scopeChain);
236 /*
237 * For a named function expression Call's parent points to an environment
238 * object holding function's name.
239 */
240 if (callee->isNamedLambda()) {
241 scopeChain = DeclEnvObject::create(cx, scopeChain, callee);
242 if (!scopeChain)
243 return nullptr;
244 }
246 RootedScript script(cx, callee->nonLazyScript());
247 return create(cx, script, scopeChain, callee);
248 }
250 CallObject *
251 CallObject::createForFunction(JSContext *cx, AbstractFramePtr frame)
252 {
253 JS_ASSERT(frame.isNonEvalFunctionFrame());
254 assertSameCompartment(cx, frame);
256 RootedObject scopeChain(cx, frame.scopeChain());
257 RootedFunction callee(cx, frame.callee());
259 CallObject *callobj = createForFunction(cx, scopeChain, callee);
260 if (!callobj)
261 return nullptr;
263 /* Copy in the closed-over formal arguments. */
264 for (AliasedFormalIter i(frame.script()); i; i++) {
265 callobj->setAliasedVar(cx, i, i->name(),
266 frame.unaliasedFormal(i.frameIndex(), DONT_CHECK_ALIASING));
267 }
269 return callobj;
270 }
272 CallObject *
273 CallObject::createForStrictEval(JSContext *cx, AbstractFramePtr frame)
274 {
275 JS_ASSERT(frame.isStrictEvalFrame());
276 JS_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterFrame() == frame.asInterpreterFrame());
277 JS_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterRegs().pc == frame.script()->code());
279 RootedFunction callee(cx);
280 RootedScript script(cx, frame.script());
281 RootedObject scopeChain(cx, frame.scopeChain());
282 return create(cx, script, scopeChain, callee);
283 }
285 const Class CallObject::class_ = {
286 "Call",
287 JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS),
288 JS_PropertyStub, /* addProperty */
289 JS_DeletePropertyStub, /* delProperty */
290 JS_PropertyStub, /* getProperty */
291 JS_StrictPropertyStub, /* setProperty */
292 JS_EnumerateStub,
293 JS_ResolveStub,
294 nullptr /* convert: Leave it nullptr so we notice if calls ever escape */
295 };
297 const Class DeclEnvObject::class_ = {
298 js_Object_str,
299 JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
300 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
301 JS_PropertyStub, /* addProperty */
302 JS_DeletePropertyStub, /* delProperty */
303 JS_PropertyStub, /* getProperty */
304 JS_StrictPropertyStub, /* setProperty */
305 JS_EnumerateStub,
306 JS_ResolveStub,
307 JS_ConvertStub
308 };
310 /*
311 * Create a DeclEnvObject for a JSScript that is not initialized to any
312 * particular callsite. This object can either be initialized (with an enclosing
313 * scope and callee) or used as a template for jit compilation.
314 */
315 DeclEnvObject *
316 DeclEnvObject::createTemplateObject(JSContext *cx, HandleFunction fun, gc::InitialHeap heap)
317 {
318 JS_ASSERT(IsNurseryAllocable(FINALIZE_KIND));
320 RootedTypeObject type(cx, cx->getNewType(&class_, nullptr));
321 if (!type)
322 return nullptr;
324 RootedShape emptyDeclEnvShape(cx);
325 emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &class_, nullptr,
326 cx->global(), nullptr, FINALIZE_KIND,
327 BaseShape::DELEGATE);
328 if (!emptyDeclEnvShape)
329 return nullptr;
331 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, heap, emptyDeclEnvShape, type));
332 if (!obj)
333 return nullptr;
335 // Assign a fixed slot to a property with the same name as the lambda.
336 Rooted<jsid> id(cx, AtomToId(fun->atom()));
337 const Class *clasp = obj->getClass();
338 unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
339 if (!JSObject::putProperty<SequentialExecution>(cx, obj, id, clasp->getProperty,
340 clasp->setProperty, lambdaSlot(), attrs, 0)) {
341 return nullptr;
342 }
344 JS_ASSERT(!obj->hasDynamicSlots());
345 return &obj->as<DeclEnvObject>();
346 }
348 DeclEnvObject *
349 DeclEnvObject::create(JSContext *cx, HandleObject enclosing, HandleFunction callee)
350 {
351 RootedObject obj(cx, createTemplateObject(cx, callee, gc::DefaultHeap));
352 if (!obj)
353 return nullptr;
355 obj->as<ScopeObject>().setEnclosingScope(enclosing);
356 obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
357 return &obj->as<DeclEnvObject>();
358 }
360 template<XDRMode mode>
361 bool
362 js::XDRStaticWithObject(XDRState<mode> *xdr, HandleObject enclosingScope, StaticWithObject **objp)
363 {
364 if (mode == XDR_DECODE) {
365 JSContext *cx = xdr->cx();
366 Rooted<StaticWithObject*> obj(cx, StaticWithObject::create(cx));
367 if (!obj)
368 return false;
369 obj->initEnclosingNestedScope(enclosingScope);
370 *objp = obj;
371 }
372 // For encoding, there is nothing to do. The only information that is
373 // encoded by a StaticWithObject is its presence on the scope chain, and the
374 // script XDR handler already takes care of that.
376 return true;
377 }
379 template bool
380 js::XDRStaticWithObject(XDRState<XDR_ENCODE> *, HandleObject, StaticWithObject **);
382 template bool
383 js::XDRStaticWithObject(XDRState<XDR_DECODE> *, HandleObject, StaticWithObject **);
385 StaticWithObject *
386 StaticWithObject::create(ExclusiveContext *cx)
387 {
388 RootedTypeObject type(cx, cx->getNewType(&class_, nullptr));
389 if (!type)
390 return nullptr;
392 RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(nullptr),
393 nullptr, nullptr, FINALIZE_KIND));
394 if (!shape)
395 return nullptr;
397 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, type));
398 if (!obj)
399 return nullptr;
401 return &obj->as<StaticWithObject>();
402 }
404 static JSObject *
405 CloneStaticWithObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticWithObject*> srcWith)
406 {
407 Rooted<StaticWithObject*> clone(cx, StaticWithObject::create(cx));
408 if (!clone)
409 return nullptr;
411 clone->initEnclosingNestedScope(enclosingScope);
413 return clone;
414 }
416 DynamicWithObject *
417 DynamicWithObject::create(JSContext *cx, HandleObject object, HandleObject enclosing,
418 HandleObject staticWith)
419 {
420 JS_ASSERT(staticWith->is<StaticWithObject>());
421 RootedTypeObject type(cx, cx->getNewType(&class_, staticWith.get()));
422 if (!type)
423 return nullptr;
425 RootedShape shape(cx, EmptyShape::getInitialShape(cx, &class_, TaggedProto(staticWith),
426 &enclosing->global(), nullptr,
427 FINALIZE_KIND));
428 if (!shape)
429 return nullptr;
431 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, type));
432 if (!obj)
433 return nullptr;
435 JSObject *thisp = JSObject::thisObject(cx, object);
436 if (!thisp)
437 return nullptr;
439 obj->as<ScopeObject>().setEnclosingScope(enclosing);
440 obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
441 obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
443 return &obj->as<DynamicWithObject>();
444 }
446 static bool
447 with_LookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
448 MutableHandleObject objp, MutableHandleShape propp)
449 {
450 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
451 return JSObject::lookupGeneric(cx, actual, id, objp, propp);
452 }
454 static bool
455 with_LookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
456 MutableHandleObject objp, MutableHandleShape propp)
457 {
458 Rooted<jsid> id(cx, NameToId(name));
459 return with_LookupGeneric(cx, obj, id, objp, propp);
460 }
462 static bool
463 with_LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
464 MutableHandleObject objp, MutableHandleShape propp)
465 {
466 RootedId id(cx);
467 if (!IndexToId(cx, index, &id))
468 return false;
469 return with_LookupGeneric(cx, obj, id, objp, propp);
470 }
472 static bool
473 with_GetGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id,
474 MutableHandleValue vp)
475 {
476 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
477 return JSObject::getGeneric(cx, actual, actual, id, vp);
478 }
480 static bool
481 with_GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name,
482 MutableHandleValue vp)
483 {
484 RootedId id(cx, NameToId(name));
485 return with_GetGeneric(cx, obj, receiver, id, vp);
486 }
488 static bool
489 with_GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
490 MutableHandleValue vp)
491 {
492 RootedId id(cx);
493 if (!IndexToId(cx, index, &id))
494 return false;
495 return with_GetGeneric(cx, obj, receiver, id, vp);
496 }
498 static bool
499 with_SetGeneric(JSContext *cx, HandleObject obj, HandleId id,
500 MutableHandleValue vp, bool strict)
501 {
502 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
503 return JSObject::setGeneric(cx, actual, actual, id, vp, strict);
504 }
506 static bool
507 with_SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
508 MutableHandleValue vp, bool strict)
509 {
510 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
511 return JSObject::setProperty(cx, actual, actual, name, vp, strict);
512 }
514 static bool
515 with_SetElement(JSContext *cx, HandleObject obj, uint32_t index,
516 MutableHandleValue vp, bool strict)
517 {
518 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
519 return JSObject::setElement(cx, actual, actual, index, vp, strict);
520 }
522 static bool
523 with_GetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
524 {
525 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
526 return JSObject::getGenericAttributes(cx, actual, id, attrsp);
527 }
529 static bool
530 with_SetGenericAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
531 {
532 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
533 return JSObject::setGenericAttributes(cx, actual, id, attrsp);
534 }
536 static bool
537 with_DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
538 bool *succeeded)
539 {
540 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
541 return JSObject::deleteProperty(cx, actual, name, succeeded);
542 }
544 static bool
545 with_DeleteElement(JSContext *cx, HandleObject obj, uint32_t index,
546 bool *succeeded)
547 {
548 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
549 return JSObject::deleteElement(cx, actual, index, succeeded);
550 }
552 static JSObject *
553 with_ThisObject(JSContext *cx, HandleObject obj)
554 {
555 return &obj->as<DynamicWithObject>().withThis();
556 }
558 const Class StaticWithObject::class_ = {
559 "WithTemplate",
560 JSCLASS_IMPLEMENTS_BARRIERS |
561 JSCLASS_HAS_RESERVED_SLOTS(StaticWithObject::RESERVED_SLOTS) |
562 JSCLASS_IS_ANONYMOUS,
563 JS_PropertyStub, /* addProperty */
564 JS_DeletePropertyStub, /* delProperty */
565 JS_PropertyStub, /* getProperty */
566 JS_StrictPropertyStub, /* setProperty */
567 JS_EnumerateStub,
568 JS_ResolveStub,
569 JS_ConvertStub
570 };
572 const Class DynamicWithObject::class_ = {
573 "With",
574 JSCLASS_HAS_RESERVED_SLOTS(DynamicWithObject::RESERVED_SLOTS) |
575 JSCLASS_IS_ANONYMOUS,
576 JS_PropertyStub, /* addProperty */
577 JS_DeletePropertyStub, /* delProperty */
578 JS_PropertyStub, /* getProperty */
579 JS_StrictPropertyStub, /* setProperty */
580 JS_EnumerateStub,
581 JS_ResolveStub,
582 JS_ConvertStub,
583 nullptr, /* finalize */
584 nullptr, /* call */
585 nullptr, /* hasInstance */
586 nullptr, /* construct */
587 nullptr, /* trace */
588 JS_NULL_CLASS_SPEC,
589 JS_NULL_CLASS_EXT,
590 {
591 with_LookupGeneric,
592 with_LookupProperty,
593 with_LookupElement,
594 nullptr, /* defineGeneric */
595 nullptr, /* defineProperty */
596 nullptr, /* defineElement */
597 with_GetGeneric,
598 with_GetProperty,
599 with_GetElement,
600 with_SetGeneric,
601 with_SetProperty,
602 with_SetElement,
603 with_GetGenericAttributes,
604 with_SetGenericAttributes,
605 with_DeleteProperty,
606 with_DeleteElement,
607 nullptr, nullptr, /* watch/unwatch */
608 nullptr, /* slice */
609 nullptr, /* enumerate (native enumeration of target doesn't work) */
610 with_ThisObject,
611 }
612 };
614 /*****************************************************************************/
616 ClonedBlockObject *
617 ClonedBlockObject::create(JSContext *cx, Handle<StaticBlockObject *> block, AbstractFramePtr frame)
618 {
619 assertSameCompartment(cx, frame);
620 JS_ASSERT(block->getClass() == &BlockObject::class_);
622 RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, block.get()));
623 if (!type)
624 return nullptr;
626 RootedShape shape(cx, block->lastProperty());
628 RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, type));
629 if (!obj)
630 return nullptr;
632 /* Set the parent if necessary, as for call objects. */
633 if (&frame.scopeChain()->global() != obj->getParent()) {
634 JS_ASSERT(obj->getParent() == nullptr);
635 Rooted<GlobalObject*> global(cx, &frame.scopeChain()->global());
636 if (!JSObject::setParent(cx, obj, global))
637 return nullptr;
638 }
640 JS_ASSERT(!obj->inDictionaryMode());
641 JS_ASSERT(obj->slotSpan() >= block->numVariables() + RESERVED_SLOTS);
643 obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*frame.scopeChain()));
645 /*
646 * Copy in the closed-over locals. Closed-over locals don't need
647 * any fixup since the initial value is 'undefined'.
648 */
649 unsigned nvars = block->numVariables();
650 for (unsigned i = 0; i < nvars; ++i) {
651 if (block->isAliased(i)) {
652 Value &val = frame.unaliasedLocal(block->blockIndexToLocalIndex(i));
653 obj->as<ClonedBlockObject>().setVar(i, val);
654 }
655 }
657 JS_ASSERT(obj->isDelegate());
659 return &obj->as<ClonedBlockObject>();
660 }
662 void
663 ClonedBlockObject::copyUnaliasedValues(AbstractFramePtr frame)
664 {
665 StaticBlockObject &block = staticBlock();
666 for (unsigned i = 0; i < numVariables(); ++i) {
667 if (!block.isAliased(i)) {
668 Value &val = frame.unaliasedLocal(block.blockIndexToLocalIndex(i));
669 setVar(i, val, DONT_CHECK_ALIASING);
670 }
671 }
672 }
674 StaticBlockObject *
675 StaticBlockObject::create(ExclusiveContext *cx)
676 {
677 RootedTypeObject type(cx, cx->getNewType(&BlockObject::class_, nullptr));
678 if (!type)
679 return nullptr;
681 RootedShape emptyBlockShape(cx);
682 emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockObject::class_, nullptr, nullptr,
683 nullptr, FINALIZE_KIND, BaseShape::DELEGATE);
684 if (!emptyBlockShape)
685 return nullptr;
687 JSObject *obj = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, emptyBlockShape, type);
688 if (!obj)
689 return nullptr;
691 return &obj->as<StaticBlockObject>();
692 }
694 /* static */ Shape *
695 StaticBlockObject::addVar(ExclusiveContext *cx, Handle<StaticBlockObject*> block, HandleId id,
696 unsigned index, bool *redeclared)
697 {
698 JS_ASSERT(JSID_IS_ATOM(id));
699 JS_ASSERT(index < LOCAL_INDEX_LIMIT);
701 *redeclared = false;
703 /* Inline JSObject::addProperty in order to trap the redefinition case. */
704 Shape **spp;
705 if (Shape::search(cx, block->lastProperty(), id, &spp, true)) {
706 *redeclared = true;
707 return nullptr;
708 }
710 /*
711 * Don't convert this object to dictionary mode so that we can clone the
712 * block's shape later.
713 */
714 uint32_t slot = JSSLOT_FREE(&BlockObject::class_) + index;
715 return JSObject::addPropertyInternal<SequentialExecution>(cx, block, id,
716 /* getter = */ nullptr,
717 /* setter = */ nullptr,
718 slot,
719 JSPROP_ENUMERATE | JSPROP_PERMANENT,
720 /* attrs = */ 0,
721 spp,
722 /* allowDictionary = */ false);
723 }
725 const Class BlockObject::class_ = {
726 "Block",
727 JSCLASS_IMPLEMENTS_BARRIERS |
728 JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) |
729 JSCLASS_IS_ANONYMOUS,
730 JS_PropertyStub, /* addProperty */
731 JS_DeletePropertyStub, /* delProperty */
732 JS_PropertyStub, /* getProperty */
733 JS_StrictPropertyStub, /* setProperty */
734 JS_EnumerateStub,
735 JS_ResolveStub,
736 JS_ConvertStub
737 };
739 template<XDRMode mode>
740 bool
741 js::XDRStaticBlockObject(XDRState<mode> *xdr, HandleObject enclosingScope,
742 StaticBlockObject **objp)
743 {
744 /* NB: Keep this in sync with CloneStaticBlockObject. */
746 JSContext *cx = xdr->cx();
748 Rooted<StaticBlockObject*> obj(cx);
749 uint32_t count = 0, offset = 0;
751 if (mode == XDR_ENCODE) {
752 obj = *objp;
753 count = obj->numVariables();
754 offset = obj->localOffset();
755 }
757 if (mode == XDR_DECODE) {
758 obj = StaticBlockObject::create(cx);
759 if (!obj)
760 return false;
761 obj->initEnclosingNestedScope(enclosingScope);
762 *objp = obj;
763 }
765 if (!xdr->codeUint32(&count))
766 return false;
767 if (!xdr->codeUint32(&offset))
768 return false;
770 /*
771 * XDR the block object's properties. We know that there are 'count'
772 * properties to XDR, stored as id/aliased pairs. (The empty string as
773 * id indicates an int id.)
774 */
775 if (mode == XDR_DECODE) {
776 obj->setLocalOffset(offset);
778 for (unsigned i = 0; i < count; i++) {
779 RootedAtom atom(cx);
780 if (!XDRAtom(xdr, &atom))
781 return false;
783 RootedId id(cx, atom != cx->runtime()->emptyString
784 ? AtomToId(atom)
785 : INT_TO_JSID(i));
787 bool redeclared;
788 if (!StaticBlockObject::addVar(cx, obj, id, i, &redeclared)) {
789 JS_ASSERT(!redeclared);
790 return false;
791 }
793 uint32_t aliased;
794 if (!xdr->codeUint32(&aliased))
795 return false;
797 JS_ASSERT(aliased == 0 || aliased == 1);
798 obj->setAliased(i, !!aliased);
799 }
800 } else {
801 AutoShapeVector shapes(cx);
802 if (!shapes.growBy(count))
803 return false;
805 for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
806 shapes[obj->shapeToIndex(r.front())] = &r.front();
808 RootedShape shape(cx);
809 RootedId propid(cx);
810 RootedAtom atom(cx);
811 for (unsigned i = 0; i < count; i++) {
812 shape = shapes[i];
813 JS_ASSERT(shape->hasDefaultGetter());
814 JS_ASSERT(obj->shapeToIndex(*shape) == i);
816 propid = shape->propid();
817 JS_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid));
819 atom = JSID_IS_ATOM(propid)
820 ? JSID_TO_ATOM(propid)
821 : cx->runtime()->emptyString;
822 if (!XDRAtom(xdr, &atom))
823 return false;
825 uint32_t aliased = obj->isAliased(i);
826 if (!xdr->codeUint32(&aliased))
827 return false;
828 }
829 }
830 return true;
831 }
833 template bool
834 js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *, HandleObject, StaticBlockObject **);
836 template bool
837 js::XDRStaticBlockObject(XDRState<XDR_DECODE> *, HandleObject, StaticBlockObject **);
839 static JSObject *
840 CloneStaticBlockObject(JSContext *cx, HandleObject enclosingScope, Handle<StaticBlockObject*> srcBlock)
841 {
842 /* NB: Keep this in sync with XDRStaticBlockObject. */
844 Rooted<StaticBlockObject*> clone(cx, StaticBlockObject::create(cx));
845 if (!clone)
846 return nullptr;
848 clone->initEnclosingNestedScope(enclosingScope);
849 clone->setLocalOffset(srcBlock->localOffset());
851 /* Shape::Range is reverse order, so build a list in forward order. */
852 AutoShapeVector shapes(cx);
853 if (!shapes.growBy(srcBlock->numVariables()))
854 return nullptr;
856 for (Shape::Range<NoGC> r(srcBlock->lastProperty()); !r.empty(); r.popFront())
857 shapes[srcBlock->shapeToIndex(r.front())] = &r.front();
859 for (Shape **p = shapes.begin(); p != shapes.end(); ++p) {
860 RootedId id(cx, (*p)->propid());
861 unsigned i = srcBlock->shapeToIndex(**p);
863 bool redeclared;
864 if (!StaticBlockObject::addVar(cx, clone, id, i, &redeclared)) {
865 JS_ASSERT(!redeclared);
866 return nullptr;
867 }
869 clone->setAliased(i, srcBlock->isAliased(i));
870 }
872 return clone;
873 }
875 JSObject *
876 js::CloneNestedScopeObject(JSContext *cx, HandleObject enclosingScope, Handle<NestedScopeObject*> srcBlock)
877 {
878 if (srcBlock->is<StaticBlockObject>()) {
879 Rooted<StaticBlockObject *> blockObj(cx, &srcBlock->as<StaticBlockObject>());
880 return CloneStaticBlockObject(cx, enclosingScope, blockObj);
881 } else {
882 Rooted<StaticWithObject *> withObj(cx, &srcBlock->as<StaticWithObject>());
883 return CloneStaticWithObject(cx, enclosingScope, withObj);
884 }
885 }
887 /*****************************************************************************/
889 // Any name atom for a function which will be added as a DeclEnv object to the
890 // scope chain above call objects for fun.
891 static inline JSAtom *
892 CallObjectLambdaName(JSFunction &fun)
893 {
894 return fun.isNamedLambda() ? fun.atom() : nullptr;
895 }
897 ScopeIter::ScopeIter(const ScopeIter &si, JSContext *cx
898 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
899 : cx(cx),
900 frame_(si.frame_),
901 cur_(cx, si.cur_),
902 staticScope_(cx, si.staticScope_),
903 type_(si.type_),
904 hasScopeObject_(si.hasScopeObject_)
905 {
906 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
907 }
909 ScopeIter::ScopeIter(JSObject &enclosingScope, JSContext *cx
910 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
911 : cx(cx),
912 frame_(NullFramePtr()),
913 cur_(cx, &enclosingScope),
914 staticScope_(cx, nullptr),
915 type_(Type(-1))
916 {
917 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
918 }
920 ScopeIter::ScopeIter(AbstractFramePtr frame, jsbytecode *pc, JSContext *cx
921 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
922 : cx(cx),
923 frame_(frame),
924 cur_(cx, frame.scopeChain()),
925 staticScope_(cx, frame.script()->getStaticScope(pc))
926 {
927 assertSameCompartment(cx, frame);
928 settle();
929 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
930 }
932 ScopeIter::ScopeIter(const ScopeIterVal &val, JSContext *cx
933 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
934 : cx(cx),
935 frame_(val.frame_),
936 cur_(cx, val.cur_),
937 staticScope_(cx, val.staticScope_),
938 type_(val.type_),
939 hasScopeObject_(val.hasScopeObject_)
940 {
941 assertSameCompartment(cx, val.frame_);
942 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
943 }
945 ScopeObject &
946 ScopeIter::scope() const
947 {
948 JS_ASSERT(hasScopeObject());
949 return cur_->as<ScopeObject>();
950 }
952 ScopeIter &
953 ScopeIter::operator++()
954 {
955 JS_ASSERT(!done());
956 switch (type_) {
957 case Call:
958 if (hasScopeObject_) {
959 cur_ = &cur_->as<CallObject>().enclosingScope();
960 if (CallObjectLambdaName(*frame_.fun()))
961 cur_ = &cur_->as<DeclEnvObject>().enclosingScope();
962 }
963 frame_ = NullFramePtr();
964 break;
965 case Block:
966 JS_ASSERT(staticScope_ && staticScope_->is<StaticBlockObject>());
967 staticScope_ = staticScope_->as<StaticBlockObject>().enclosingNestedScope();
968 if (hasScopeObject_)
969 cur_ = &cur_->as<ClonedBlockObject>().enclosingScope();
970 settle();
971 break;
972 case With:
973 JS_ASSERT(staticScope_ && staticScope_->is<StaticWithObject>());
974 JS_ASSERT(hasScopeObject_);
975 staticScope_ = staticScope_->as<StaticWithObject>().enclosingNestedScope();
976 cur_ = &cur_->as<DynamicWithObject>().enclosingScope();
977 settle();
978 break;
979 case StrictEvalScope:
980 if (hasScopeObject_)
981 cur_ = &cur_->as<CallObject>().enclosingScope();
982 frame_ = NullFramePtr();
983 break;
984 }
985 return *this;
986 }
988 void
989 ScopeIter::settle()
990 {
991 /*
992 * Given an iterator state (cur_, staticScope_), figure out which (potentially
993 * optimized) scope the iterator should report. Thus, the result is a pair
994 * (type_, hasScopeObject_) where hasScopeObject_ indicates whether the
995 * scope object has been optimized away and does not exist on the scope
996 * chain. Beware: while ScopeIter iterates over the scopes of a single
997 * frame, the scope chain (pointed to by cur_) continues into the scopes of
998 * enclosing frames. Thus, it is important not to look at cur_ until it is
999 * certain that cur_ points to a scope object in the current frame. In
1000 * particular, there are three tricky corner cases:
1001 * - non-heavyweight functions;
1002 * - non-strict direct eval.
1003 * - heavyweight functions observed before the prologue has finished;
1004 * In all cases, cur_ can already be pointing into an enclosing frame's
1005 * scope chain. Furthermore, in the first two cases: even if cur_ points
1006 * into an enclosing frame's scope chain, the current frame may still have
1007 * uncloned blocks. In the last case, since we haven't entered the
1008 * function, we simply return a ScopeIter where done() == true.
1009 *
1010 * Note: DebugScopeObject falls nicely into this plan: since they are only
1011 * ever introduced as the *enclosing* scope of a frame, they should never
1012 * show up in scope iteration and fall into the final non-scope case.
1013 */
1014 if (frame_.isNonEvalFunctionFrame() && !frame_.fun()->isHeavyweight()) {
1015 if (staticScope_) {
1016 // If staticScope_ were a StaticWithObject, the function would be
1017 // heavyweight.
1018 JS_ASSERT(staticScope_->is<StaticBlockObject>());
1019 type_ = Block;
1020 hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone();
1021 } else {
1022 type_ = Call;
1023 hasScopeObject_ = false;
1024 }
1025 } else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrevScopeChain(cx)) {
1026 if (staticScope_) {
1027 JS_ASSERT(staticScope_->is<StaticBlockObject>());
1028 JS_ASSERT(!staticScope_->as<StaticBlockObject>().needsClone());
1029 type_ = Block;
1030 hasScopeObject_ = false;
1031 } else {
1032 frame_ = NullFramePtr();
1033 }
1034 } else if (frame_.isNonEvalFunctionFrame() && !frame_.hasCallObj()) {
1035 JS_ASSERT(cur_ == frame_.fun()->environment());
1036 frame_ = NullFramePtr();
1037 } else if (frame_.isStrictEvalFrame() && !frame_.hasCallObj()) {
1038 JS_ASSERT(cur_ == frame_.evalPrevScopeChain(cx));
1039 frame_ = NullFramePtr();
1040 } else if (staticScope_) {
1041 if (staticScope_->is<StaticWithObject>()) {
1042 JS_ASSERT(cur_);
1043 JS_ASSERT(cur_->as<DynamicWithObject>().staticScope() == staticScope_);
1044 type_ = With;
1045 hasScopeObject_ = true;
1046 } else {
1047 type_ = Block;
1048 hasScopeObject_ = staticScope_->as<StaticBlockObject>().needsClone();
1049 JS_ASSERT_IF(hasScopeObject_,
1050 cur_->as<ClonedBlockObject>().staticBlock() == *staticScope_);
1051 }
1052 } else if (cur_->is<CallObject>()) {
1053 CallObject &callobj = cur_->as<CallObject>();
1054 type_ = callobj.isForEval() ? StrictEvalScope : Call;
1055 hasScopeObject_ = true;
1056 JS_ASSERT_IF(type_ == Call, callobj.callee().nonLazyScript() == frame_.script());
1057 } else {
1058 JS_ASSERT(!cur_->is<ScopeObject>());
1059 JS_ASSERT(frame_.isGlobalFrame() || frame_.isDebuggerFrame());
1060 frame_ = NullFramePtr();
1061 }
1062 }
1064 /* static */ HashNumber
1065 ScopeIterKey::hash(ScopeIterKey si)
1066 {
1067 /* hasScopeObject_ is determined by the other fields. */
1068 return size_t(si.frame_.raw()) ^ size_t(si.cur_) ^ size_t(si.staticScope_) ^ si.type_;
1069 }
1071 /* static */ bool
1072 ScopeIterKey::match(ScopeIterKey si1, ScopeIterKey si2)
1073 {
1074 /* hasScopeObject_ is determined by the other fields. */
1075 return si1.frame_ == si2.frame_ &&
1076 (!si1.frame_ ||
1077 (si1.cur_ == si2.cur_ &&
1078 si1.staticScope_ == si2.staticScope_ &&
1079 si1.type_ == si2.type_));
1080 }
1082 // Live ScopeIter values may be added to DebugScopes::liveScopes, as
1083 // ScopeIterVal instances. They need to have write barriers when they are added
1084 // to the hash table, but no barriers when rehashing inside GC. It's a nasty
1085 // hack, but the important thing is that ScopeIterKey and ScopeIterVal need to
1086 // alias each other.
1087 void ScopeIterVal::staticAsserts() {
1088 static_assert(sizeof(ScopeIterVal) == sizeof(ScopeIterKey),
1089 "ScopeIterVal must be same size of ScopeIterKey");
1090 static_assert(offsetof(ScopeIterVal, cur_) == offsetof(ScopeIterKey, cur_),
1091 "ScopeIterVal.cur_ must alias ScopeIterKey.cur_");
1092 static_assert(offsetof(ScopeIterVal, staticScope_) == offsetof(ScopeIterKey, staticScope_),
1093 "ScopeIterVal.staticScope_ must alias ScopeIterKey.staticScope_");
1094 }
1096 /*****************************************************************************/
1098 namespace {
1100 /*
1101 * DebugScopeProxy is the handler for DebugScopeObject proxy objects. Having a
1102 * custom handler (rather than trying to reuse js::Wrapper) gives us several
1103 * important abilities:
1104 * - We want to pass the ScopeObject as the receiver to forwarded scope
1105 * property ops on aliased variables so that Call/Block/With ops do not all
1106 * require a 'normalization' step.
1107 * - The debug scope proxy can directly manipulate the stack frame to allow
1108 * the debugger to read/write args/locals that were otherwise unaliased.
1109 * - The debug scope proxy can store unaliased variables after the stack frame
1110 * is popped so that they may still be read/written by the debugger.
1111 * - The engine has made certain assumptions about the possible reads/writes
1112 * in a scope. DebugScopeProxy allows us to prevent the debugger from
1113 * breaking those assumptions.
1114 * - The engine makes optimizations that are observable to the debugger. The
1115 * proxy can either hide these optimizations or make the situation more
1116 * clear to the debugger. An example is 'arguments'.
1117 */
1118 class DebugScopeProxy : public BaseProxyHandler
1119 {
1120 enum Action { SET, GET };
1122 enum AccessResult {
1123 ACCESS_UNALIASED,
1124 ACCESS_GENERIC,
1125 ACCESS_LOST
1126 };
1128 /*
1129 * This function handles access to unaliased locals/formals. Since they are
1130 * unaliased, the values of these variables are not stored in the slots of
1131 * the normal Call/BlockObject scope objects and thus must be recovered
1132 * from somewhere else:
1133 * + if the invocation for which the scope was created is still executing,
1134 * there is a JS frame live on the stack holding the values;
1135 * + if the invocation for which the scope was created finished executing:
1136 * - and there was a DebugScopeObject associated with scope, then the
1137 * DebugScopes::onPop(Call|Block) handler copied out the unaliased
1138 * variables:
1139 * . for block scopes, the unaliased values were copied directly
1140 * into the block object, since there is a slot allocated for every
1141 * block binding, regardless of whether it is aliased;
1142 * . for function scopes, a dense array is created in onPopCall to hold
1143 * the unaliased values and attached to the DebugScopeObject;
1144 * - and there was not a DebugScopeObject yet associated with the
1145 * scope, then the unaliased values are lost and not recoverable.
1146 *
1147 * Callers should check accessResult for non-failure results:
1148 * - ACCESS_UNALIASED if the access was unaliased and completed
1149 * - ACCESS_GENERIC if the access was aliased or the property not found
1150 * - ACCESS_LOST if the value has been lost to the debugger
1151 */
1152 bool handleUnaliasedAccess(JSContext *cx, Handle<DebugScopeObject*> debugScope,
1153 Handle<ScopeObject*> scope, jsid id, Action action,
1154 MutableHandleValue vp, AccessResult *accessResult)
1155 {
1156 JS_ASSERT(&debugScope->scope() == scope);
1157 *accessResult = ACCESS_GENERIC;
1158 ScopeIterVal *maybeLiveScope = DebugScopes::hasLiveScope(*scope);
1160 /* Handle unaliased formals, vars, and consts at function scope. */
1161 if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
1162 CallObject &callobj = scope->as<CallObject>();
1163 RootedScript script(cx, callobj.callee().nonLazyScript());
1164 if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
1165 return false;
1167 Bindings &bindings = script->bindings;
1168 BindingIter bi(script);
1169 while (bi && NameToId(bi->name()) != id)
1170 bi++;
1171 if (!bi)
1172 return true;
1174 if (bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT) {
1175 uint32_t i = bi.frameIndex();
1176 if (script->varIsAliased(i))
1177 return true;
1179 if (maybeLiveScope) {
1180 AbstractFramePtr frame = maybeLiveScope->frame();
1181 if (action == GET)
1182 vp.set(frame.unaliasedVar(i));
1183 else
1184 frame.unaliasedVar(i) = vp;
1185 } else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
1186 if (action == GET)
1187 vp.set(snapshot->getDenseElement(bindings.numArgs() + i));
1188 else
1189 snapshot->setDenseElement(bindings.numArgs() + i, vp);
1190 } else {
1191 /* The unaliased value has been lost to the debugger. */
1192 if (action == GET) {
1193 *accessResult = ACCESS_LOST;
1194 return true;
1195 }
1196 }
1197 } else {
1198 JS_ASSERT(bi->kind() == Binding::ARGUMENT);
1199 unsigned i = bi.frameIndex();
1200 if (script->formalIsAliased(i))
1201 return true;
1203 if (maybeLiveScope) {
1204 AbstractFramePtr frame = maybeLiveScope->frame();
1205 if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
1206 if (action == GET)
1207 vp.set(frame.argsObj().arg(i));
1208 else
1209 frame.argsObj().setArg(i, vp);
1210 } else {
1211 if (action == GET)
1212 vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING));
1213 else
1214 frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
1215 }
1216 } else if (JSObject *snapshot = debugScope->maybeSnapshot()) {
1217 if (action == GET)
1218 vp.set(snapshot->getDenseElement(i));
1219 else
1220 snapshot->setDenseElement(i, vp);
1221 } else {
1222 /* The unaliased value has been lost to the debugger. */
1223 if (action == GET) {
1224 *accessResult = ACCESS_LOST;
1225 return true;
1226 }
1227 }
1229 if (action == SET)
1230 TypeScript::SetArgument(cx, script, i, vp);
1231 }
1233 *accessResult = ACCESS_UNALIASED;
1234 return true;
1235 }
1237 /* Handle unaliased let and catch bindings at block scope. */
1238 if (scope->is<ClonedBlockObject>()) {
1239 Rooted<ClonedBlockObject *> block(cx, &scope->as<ClonedBlockObject>());
1240 Shape *shape = block->lastProperty()->search(cx, id);
1241 if (!shape)
1242 return true;
1244 unsigned i = block->staticBlock().shapeToIndex(*shape);
1245 if (block->staticBlock().isAliased(i))
1246 return true;
1248 if (maybeLiveScope) {
1249 AbstractFramePtr frame = maybeLiveScope->frame();
1250 uint32_t local = block->staticBlock().blockIndexToLocalIndex(i);
1251 JS_ASSERT(local < frame.script()->nfixed());
1252 if (action == GET)
1253 vp.set(frame.unaliasedLocal(local));
1254 else
1255 frame.unaliasedLocal(local) = vp;
1256 } else {
1257 if (action == GET)
1258 vp.set(block->var(i, DONT_CHECK_ALIASING));
1259 else
1260 block->setVar(i, vp, DONT_CHECK_ALIASING);
1261 }
1263 *accessResult = ACCESS_UNALIASED;
1264 return true;
1265 }
1267 /* The rest of the internal scopes do not have unaliased vars. */
1268 JS_ASSERT(scope->is<DeclEnvObject>() || scope->is<DynamicWithObject>() ||
1269 scope->as<CallObject>().isForEval());
1270 return true;
1271 }
1273 static bool isArguments(JSContext *cx, jsid id)
1274 {
1275 return id == NameToId(cx->names().arguments);
1276 }
1278 static bool isFunctionScope(ScopeObject &scope)
1279 {
1280 return scope.is<CallObject>() && !scope.as<CallObject>().isForEval();
1281 }
1283 /*
1284 * In theory, every function scope contains an 'arguments' bindings.
1285 * However, the engine only adds a binding if 'arguments' is used in the
1286 * function body. Thus, from the debugger's perspective, 'arguments' may be
1287 * missing from the list of bindings.
1288 */
1289 static bool isMissingArgumentsBinding(ScopeObject &scope)
1290 {
1291 return isFunctionScope(scope) &&
1292 !scope.as<CallObject>().callee().nonLazyScript()->argumentsHasVarBinding();
1293 }
1295 /*
1296 * This function checks if an arguments object needs to be created when
1297 * the debugger requests 'arguments' for a function scope where the
1298 * arguments object has been optimized away (either because the binding is
1299 * missing altogether or because !ScriptAnalysis::needsArgsObj).
1300 */
1301 static bool isMissingArguments(JSContext *cx, jsid id, ScopeObject &scope)
1302 {
1303 return isArguments(cx, id) && isFunctionScope(scope) &&
1304 !scope.as<CallObject>().callee().nonLazyScript()->needsArgsObj();
1305 }
1307 /*
1308 * Create a missing arguments object. If the function returns true but
1309 * argsObj is null, it means the scope is dead.
1310 */
1311 static bool createMissingArguments(JSContext *cx, jsid id, ScopeObject &scope,
1312 MutableHandleArgumentsObject argsObj)
1313 {
1314 MOZ_ASSERT(isMissingArguments(cx, id, scope));
1315 argsObj.set(nullptr);
1317 ScopeIterVal *maybeScope = DebugScopes::hasLiveScope(scope);
1318 if (!maybeScope)
1319 return true;
1321 argsObj.set(ArgumentsObject::createUnexpected(cx, maybeScope->frame()));
1322 return !!argsObj;
1323 }
1325 public:
1326 static int family;
1327 static DebugScopeProxy singleton;
1329 DebugScopeProxy() : BaseProxyHandler(&family) {}
1331 bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE
1332 {
1333 // always [[Extensible]], can't be made non-[[Extensible]], like most
1334 // proxies
1335 *extensible = true;
1336 return true;
1337 }
1339 bool preventExtensions(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE
1340 {
1341 // See above.
1342 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY);
1343 return false;
1344 }
1346 bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
1347 MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE
1348 {
1349 return getOwnPropertyDescriptor(cx, proxy, id, desc);
1350 }
1352 bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
1353 MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE
1354 {
1355 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
1356 Rooted<ScopeObject*> scope(cx, &debugScope->scope());
1358 if (isMissingArguments(cx, id, *scope)) {
1359 RootedArgumentsObject argsObj(cx);
1360 if (!createMissingArguments(cx, id, *scope, &argsObj))
1361 return false;
1363 if (!argsObj) {
1364 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
1365 "Debugger scope");
1366 return false;
1367 }
1369 desc.object().set(debugScope);
1370 desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
1371 desc.value().setObject(*argsObj);
1372 desc.setGetter(nullptr);
1373 desc.setSetter(nullptr);
1374 return true;
1375 }
1377 RootedValue v(cx);
1378 AccessResult access;
1379 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, &v, &access))
1380 return false;
1382 switch (access) {
1383 case ACCESS_UNALIASED:
1384 desc.object().set(debugScope);
1385 desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
1386 desc.value().set(v);
1387 desc.setGetter(nullptr);
1388 desc.setSetter(nullptr);
1389 return true;
1390 case ACCESS_GENERIC:
1391 return JS_GetOwnPropertyDescriptorById(cx, scope, id, desc);
1392 case ACCESS_LOST:
1393 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT);
1394 return false;
1395 default:
1396 MOZ_ASSUME_UNREACHABLE("bad AccessResult");
1397 }
1398 }
1400 bool get(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
1401 MutableHandleValue vp) MOZ_OVERRIDE
1402 {
1403 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
1404 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
1406 if (isMissingArguments(cx, id, *scope)) {
1407 RootedArgumentsObject argsObj(cx);
1408 if (!createMissingArguments(cx, id, *scope, &argsObj))
1409 return false;
1411 if (!argsObj) {
1412 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
1413 "Debugger scope");
1414 return false;
1415 }
1417 vp.setObject(*argsObj);
1418 return true;
1419 }
1421 AccessResult access;
1422 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access))
1423 return false;
1425 switch (access) {
1426 case ACCESS_UNALIASED:
1427 return true;
1428 case ACCESS_GENERIC:
1429 return JSObject::getGeneric(cx, scope, scope, id, vp);
1430 case ACCESS_LOST:
1431 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT);
1432 return false;
1433 default:
1434 MOZ_ASSUME_UNREACHABLE("bad AccessResult");
1435 }
1436 }
1438 /*
1439 * Like 'get', but returns sentinel values instead of throwing on
1440 * exceptional cases.
1441 */
1442 bool getMaybeSentinelValue(JSContext *cx, Handle<DebugScopeObject *> debugScope, HandleId id,
1443 MutableHandleValue vp)
1444 {
1445 Rooted<ScopeObject*> scope(cx, &debugScope->scope());
1447 if (isMissingArguments(cx, id, *scope)) {
1448 RootedArgumentsObject argsObj(cx);
1449 if (!createMissingArguments(cx, id, *scope, &argsObj))
1450 return false;
1451 vp.set(argsObj ? ObjectValue(*argsObj) : MagicValue(JS_OPTIMIZED_ARGUMENTS));
1452 return true;
1453 }
1455 AccessResult access;
1456 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access))
1457 return false;
1459 switch (access) {
1460 case ACCESS_UNALIASED:
1461 return true;
1462 case ACCESS_GENERIC:
1463 return JSObject::getGeneric(cx, scope, scope, id, vp);
1464 case ACCESS_LOST:
1465 vp.setMagic(JS_OPTIMIZED_OUT);
1466 return true;
1467 default:
1468 MOZ_ASSUME_UNREACHABLE("bad AccessResult");
1469 }
1470 }
1472 bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict,
1473 MutableHandleValue vp) MOZ_OVERRIDE
1474 {
1475 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
1476 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
1478 AccessResult access;
1479 if (!handleUnaliasedAccess(cx, debugScope, scope, id, SET, vp, &access))
1480 return false;
1482 switch (access) {
1483 case ACCESS_UNALIASED:
1484 return true;
1485 case ACCESS_GENERIC:
1486 return JSObject::setGeneric(cx, scope, scope, id, vp, strict);
1487 default:
1488 MOZ_ASSUME_UNREACHABLE("bad AccessResult");
1489 }
1490 }
1492 bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
1493 MutableHandle<PropertyDescriptor> desc) MOZ_OVERRIDE
1494 {
1495 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
1497 bool found;
1498 if (!has(cx, proxy, id, &found))
1499 return false;
1500 if (found)
1501 return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
1503 return JS_DefinePropertyById(cx, scope, id, desc.value(), desc.getter(), desc.setter(),
1504 desc.attributes());
1505 }
1507 bool getScopePropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props,
1508 unsigned flags)
1509 {
1510 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
1512 if (isMissingArgumentsBinding(*scope)) {
1513 if (!props.append(NameToId(cx->names().arguments)))
1514 return false;
1515 }
1517 // DynamicWithObject isn't a very good proxy. It doesn't have a
1518 // JSNewEnumerateOp implementation, because if it just delegated to the
1519 // target object, the object would indicate that native enumeration is
1520 // the thing to do, but native enumeration over the DynamicWithObject
1521 // wrapper yields no properties. So instead here we hack around the
1522 // issue, and punch a hole through to the with object target.
1523 Rooted<JSObject*> target(cx, (scope->is<DynamicWithObject>()
1524 ? &scope->as<DynamicWithObject>().object() : scope));
1525 if (!GetPropertyNames(cx, target, flags, &props))
1526 return false;
1528 /*
1529 * Function scopes are optimized to not contain unaliased variables so
1530 * they must be manually appended here.
1531 */
1532 if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
1533 RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript());
1534 for (BindingIter bi(script); bi; bi++) {
1535 if (!bi->aliased() && !props.append(NameToId(bi->name())))
1536 return false;
1537 }
1538 }
1540 return true;
1541 }
1543 bool getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE
1544 {
1545 return getScopePropertyNames(cx, proxy, props, JSITER_OWNONLY);
1546 }
1548 bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE
1549 {
1550 return getScopePropertyNames(cx, proxy, props, 0);
1551 }
1553 bool has(JSContext *cx, HandleObject proxy, HandleId id_, bool *bp) MOZ_OVERRIDE
1554 {
1555 RootedId id(cx, id_);
1556 ScopeObject &scopeObj = proxy->as<DebugScopeObject>().scope();
1558 if (isArguments(cx, id) && isFunctionScope(scopeObj)) {
1559 *bp = true;
1560 return true;
1561 }
1563 bool found;
1564 RootedObject scope(cx, &scopeObj);
1565 if (!JS_HasPropertyById(cx, scope, id, &found))
1566 return false;
1568 /*
1569 * Function scopes are optimized to not contain unaliased variables so
1570 * a manual search is necessary.
1571 */
1572 if (!found && scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
1573 RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript());
1574 for (BindingIter bi(script); bi; bi++) {
1575 if (!bi->aliased() && NameToId(bi->name()) == id) {
1576 found = true;
1577 break;
1578 }
1579 }
1580 }
1582 *bp = found;
1583 return true;
1584 }
1586 bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp) MOZ_OVERRIDE
1587 {
1588 RootedValue idval(cx, IdToValue(id));
1589 return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_CANT_DELETE,
1590 JSDVG_IGNORE_STACK, idval, NullPtr(), nullptr, nullptr);
1591 }
1592 };
1594 } /* anonymous namespace */
1596 int DebugScopeProxy::family = 0;
1597 DebugScopeProxy DebugScopeProxy::singleton;
1599 /* static */ DebugScopeObject *
1600 DebugScopeObject::create(JSContext *cx, ScopeObject &scope, HandleObject enclosing)
1601 {
1602 JS_ASSERT(scope.compartment() == cx->compartment());
1603 RootedValue priv(cx, ObjectValue(scope));
1604 JSObject *obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv,
1605 nullptr /* proto */, &scope.global());
1606 if (!obj)
1607 return nullptr;
1609 JS_ASSERT(!enclosing->is<ScopeObject>());
1611 DebugScopeObject *debugScope = &obj->as<DebugScopeObject>();
1612 debugScope->setExtra(ENCLOSING_EXTRA, ObjectValue(*enclosing));
1613 debugScope->setExtra(SNAPSHOT_EXTRA, NullValue());
1615 return debugScope;
1616 }
1618 ScopeObject &
1619 DebugScopeObject::scope() const
1620 {
1621 return target()->as<ScopeObject>();
1622 }
1624 JSObject &
1625 DebugScopeObject::enclosingScope() const
1626 {
1627 return extra(ENCLOSING_EXTRA).toObject();
1628 }
1630 JSObject *
1631 DebugScopeObject::maybeSnapshot() const
1632 {
1633 JS_ASSERT(!scope().as<CallObject>().isForEval());
1634 return extra(SNAPSHOT_EXTRA).toObjectOrNull();
1635 }
1637 void
1638 DebugScopeObject::initSnapshot(JSObject &o)
1639 {
1640 JS_ASSERT(maybeSnapshot() == nullptr);
1641 setExtra(SNAPSHOT_EXTRA, ObjectValue(o));
1642 }
1644 bool
1645 DebugScopeObject::isForDeclarative() const
1646 {
1647 ScopeObject &s = scope();
1648 return s.is<CallObject>() || s.is<BlockObject>() || s.is<DeclEnvObject>();
1649 }
1651 bool
1652 DebugScopeObject::getMaybeSentinelValue(JSContext *cx, HandleId id, MutableHandleValue vp)
1653 {
1654 Rooted<DebugScopeObject *> self(cx, this);
1655 return DebugScopeProxy::singleton.getMaybeSentinelValue(cx, self, id, vp);
1656 }
1658 bool
1659 js_IsDebugScopeSlow(ProxyObject *proxy)
1660 {
1661 JS_ASSERT(proxy->hasClass(&ProxyObject::uncallableClass_));
1662 return proxy->handler() == &DebugScopeProxy::singleton;
1663 }
1665 /*****************************************************************************/
1667 /* static */ MOZ_ALWAYS_INLINE void
1668 DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
1669 const EncapsulatedPtr<JSObject> &key)
1670 {
1671 #ifdef JSGC_GENERATIONAL
1672 /*
1673 * Strip the barriers from the type before inserting into the store buffer.
1674 * This will automatically ensure that barriers do not fire during GC.
1675 *
1676 * Some compilers complain about instantiating the WeakMap class for
1677 * unbarriered type arguments, so we cast to a HashMap instead. Because of
1678 * WeakMap's multiple inheritace, We need to do this in two stages, first to
1679 * the HashMap base class and then to the unbarriered version.
1680 */
1681 ObjectWeakMap::Base *baseHashMap = static_cast<ObjectWeakMap::Base *>(map);
1683 typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
1684 UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
1686 typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
1687 if (key && IsInsideNursery(rt, key))
1688 rt->gcStoreBuffer.putGeneric(Ref(unbarrieredMap, key.get()));
1689 #endif
1690 }
1692 #ifdef JSGC_GENERATIONAL
1693 class DebugScopes::MissingScopesRef : public gc::BufferableRef
1694 {
1695 MissingScopeMap *map;
1696 ScopeIterKey key;
1698 public:
1699 MissingScopesRef(MissingScopeMap *m, const ScopeIterKey &k) : map(m), key(k) {}
1701 void mark(JSTracer *trc) {
1702 ScopeIterKey prior = key;
1703 MissingScopeMap::Ptr p = map->lookup(key);
1704 if (!p)
1705 return;
1706 trc->setTracingLocation(&const_cast<ScopeIterKey &>(p->key()).enclosingScope());
1707 Mark(trc, &key.enclosingScope(), "MissingScopesRef");
1708 map->rekeyIfMoved(prior, key);
1709 }
1710 };
1711 #endif
1713 /* static */ MOZ_ALWAYS_INLINE void
1714 DebugScopes::missingScopesPostWriteBarrier(JSRuntime *rt, MissingScopeMap *map,
1715 const ScopeIterKey &key)
1716 {
1717 #ifdef JSGC_GENERATIONAL
1718 if (key.enclosingScope() && IsInsideNursery(rt, key.enclosingScope()))
1719 rt->gcStoreBuffer.putGeneric(MissingScopesRef(map, key));
1720 #endif
1721 }
1723 /* static */ MOZ_ALWAYS_INLINE void
1724 DebugScopes::liveScopesPostWriteBarrier(JSRuntime *rt, LiveScopeMap *map, ScopeObject *key)
1725 {
1726 #ifdef JSGC_GENERATIONAL
1727 // As above. Otherwise, barriers could fire during GC when moving the
1728 // value.
1729 typedef HashMap<ScopeObject *,
1730 ScopeIterKey,
1731 DefaultHasher<ScopeObject *>,
1732 RuntimeAllocPolicy> UnbarrieredLiveScopeMap;
1733 typedef gc::HashKeyRef<UnbarrieredLiveScopeMap, ScopeObject *> Ref;
1734 if (key && IsInsideNursery(rt, key))
1735 rt->gcStoreBuffer.putGeneric(Ref(reinterpret_cast<UnbarrieredLiveScopeMap *>(map), key));
1736 #endif
1737 }
1739 DebugScopes::DebugScopes(JSContext *cx)
1740 : proxiedScopes(cx),
1741 missingScopes(cx->runtime()),
1742 liveScopes(cx->runtime())
1743 {}
1745 DebugScopes::~DebugScopes()
1746 {
1747 JS_ASSERT(missingScopes.empty());
1748 WeakMapBase::removeWeakMapFromList(&proxiedScopes);
1749 }
1751 bool
1752 DebugScopes::init()
1753 {
1754 if (!liveScopes.init() ||
1755 !proxiedScopes.init() ||
1756 !missingScopes.init())
1757 {
1758 return false;
1759 }
1760 return true;
1761 }
1763 void
1764 DebugScopes::mark(JSTracer *trc)
1765 {
1766 proxiedScopes.trace(trc);
1767 }
1769 void
1770 DebugScopes::sweep(JSRuntime *rt)
1771 {
1772 /*
1773 * missingScopes points to debug scopes weakly so that debug scopes can be
1774 * released more eagerly.
1775 */
1776 for (MissingScopeMap::Enum e(missingScopes); !e.empty(); e.popFront()) {
1777 DebugScopeObject **debugScope = e.front().value().unsafeGet();
1778 if (IsObjectAboutToBeFinalized(debugScope)) {
1779 /*
1780 * Note that onPopCall and onPopBlock rely on missingScopes to find
1781 * scope objects that we synthesized for the debugger's sake, and
1782 * clean up the synthetic scope objects' entries in liveScopes. So
1783 * if we remove an entry frcom missingScopes here, we must also
1784 * remove the corresponding liveScopes entry.
1785 *
1786 * Since the DebugScopeObject is the only thing using its scope
1787 * object, and the DSO is about to be finalized, you might assume
1788 * that the synthetic SO is also about to be finalized too, and thus
1789 * the loop below will take care of things. But complex GC behavior
1790 * means that marks are only conservative approximations of
1791 * liveness; we should assume that anything could be marked.
1792 *
1793 * Thus, we must explicitly remove the entries from both liveScopes
1794 * and missingScopes here.
1795 */
1796 liveScopes.remove(&(*debugScope)->scope());
1797 e.removeFront();
1798 }
1799 }
1801 for (LiveScopeMap::Enum e(liveScopes); !e.empty(); e.popFront()) {
1802 ScopeObject *scope = e.front().key();
1804 /*
1805 * Scopes can be finalized when a debugger-synthesized ScopeObject is
1806 * no longer reachable via its DebugScopeObject.
1807 */
1808 if (IsObjectAboutToBeFinalized(&scope)) {
1809 e.removeFront();
1810 continue;
1811 }
1812 }
1813 }
1815 #if defined(JSGC_GENERATIONAL) && defined(JS_GC_ZEAL)
1816 void
1817 DebugScopes::checkHashTablesAfterMovingGC(JSRuntime *runtime)
1818 {
1819 /*
1820 * This is called at the end of StoreBuffer::mark() to check that our
1821 * postbarriers have worked and that no hashtable keys (or values) are left
1822 * pointing into the nursery.
1823 */
1824 JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtime);
1825 for (ObjectWeakMap::Range r = proxiedScopes.all(); !r.empty(); r.popFront()) {
1826 JS_ASSERT(!IsInsideNursery(rt, r.front().key().get()));
1827 JS_ASSERT(!IsInsideNursery(rt, r.front().value().get()));
1828 }
1829 for (MissingScopeMap::Range r = missingScopes.all(); !r.empty(); r.popFront()) {
1830 JS_ASSERT(!IsInsideNursery(rt, r.front().key().cur()));
1831 JS_ASSERT(!IsInsideNursery(rt, r.front().key().staticScope()));
1832 JS_ASSERT(!IsInsideNursery(rt, r.front().value().get()));
1833 }
1834 for (LiveScopeMap::Range r = liveScopes.all(); !r.empty(); r.popFront()) {
1835 JS_ASSERT(!IsInsideNursery(rt, r.front().key()));
1836 JS_ASSERT(!IsInsideNursery(rt, r.front().value().cur_.get()));
1837 JS_ASSERT(!IsInsideNursery(rt, r.front().value().staticScope_.get()));
1838 }
1839 }
1840 #endif
1842 /*
1843 * Unfortunately, GetDebugScopeForFrame needs to work even outside debug mode
1844 * (in particular, JS_GetFrameScopeChain does not require debug mode). Since
1845 * DebugScopes::onPop* are only called in debug mode, this means we cannot
1846 * use any of the maps in DebugScopes. This will produce debug scope chains
1847 * that do not obey the debugger invariants but that is just fine.
1848 */
1849 static bool
1850 CanUseDebugScopeMaps(JSContext *cx)
1851 {
1852 return cx->compartment()->debugMode();
1853 }
1855 DebugScopes *
1856 DebugScopes::ensureCompartmentData(JSContext *cx)
1857 {
1858 JSCompartment *c = cx->compartment();
1859 if (c->debugScopes)
1860 return c->debugScopes;
1862 c->debugScopes = cx->runtime()->new_<DebugScopes>(cx);
1863 if (c->debugScopes && c->debugScopes->init())
1864 return c->debugScopes;
1866 js_ReportOutOfMemory(cx);
1867 return nullptr;
1868 }
1870 DebugScopeObject *
1871 DebugScopes::hasDebugScope(JSContext *cx, ScopeObject &scope)
1872 {
1873 DebugScopes *scopes = scope.compartment()->debugScopes;
1874 if (!scopes)
1875 return nullptr;
1877 if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&scope)) {
1878 JS_ASSERT(CanUseDebugScopeMaps(cx));
1879 return &p->value()->as<DebugScopeObject>();
1880 }
1882 return nullptr;
1883 }
1885 bool
1886 DebugScopes::addDebugScope(JSContext *cx, ScopeObject &scope, DebugScopeObject &debugScope)
1887 {
1888 JS_ASSERT(cx->compartment() == scope.compartment());
1889 JS_ASSERT(cx->compartment() == debugScope.compartment());
1891 if (!CanUseDebugScopeMaps(cx))
1892 return true;
1894 DebugScopes *scopes = ensureCompartmentData(cx);
1895 if (!scopes)
1896 return false;
1898 JS_ASSERT(!scopes->proxiedScopes.has(&scope));
1899 if (!scopes->proxiedScopes.put(&scope, &debugScope)) {
1900 js_ReportOutOfMemory(cx);
1901 return false;
1902 }
1904 proxiedScopesPostWriteBarrier(cx->runtime(), &scopes->proxiedScopes, &scope);
1905 return true;
1906 }
1908 DebugScopeObject *
1909 DebugScopes::hasDebugScope(JSContext *cx, const ScopeIter &si)
1910 {
1911 JS_ASSERT(!si.hasScopeObject());
1913 DebugScopes *scopes = cx->compartment()->debugScopes;
1914 if (!scopes)
1915 return nullptr;
1917 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
1918 JS_ASSERT(CanUseDebugScopeMaps(cx));
1919 return p->value();
1920 }
1921 return nullptr;
1922 }
1924 bool
1925 DebugScopes::addDebugScope(JSContext *cx, const ScopeIter &si, DebugScopeObject &debugScope)
1926 {
1927 JS_ASSERT(!si.hasScopeObject());
1928 JS_ASSERT(cx->compartment() == debugScope.compartment());
1929 JS_ASSERT_IF(si.frame().isFunctionFrame(), !si.frame().callee()->isGenerator());
1931 if (!CanUseDebugScopeMaps(cx))
1932 return true;
1934 DebugScopes *scopes = ensureCompartmentData(cx);
1935 if (!scopes)
1936 return false;
1938 JS_ASSERT(!scopes->missingScopes.has(si));
1939 if (!scopes->missingScopes.put(si, &debugScope)) {
1940 js_ReportOutOfMemory(cx);
1941 return false;
1942 }
1943 missingScopesPostWriteBarrier(cx->runtime(), &scopes->missingScopes, si);
1945 JS_ASSERT(!scopes->liveScopes.has(&debugScope.scope()));
1946 if (!scopes->liveScopes.put(&debugScope.scope(), si)) {
1947 js_ReportOutOfMemory(cx);
1948 return false;
1949 }
1950 liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &debugScope.scope());
1952 return true;
1953 }
1955 void
1956 DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
1957 {
1958 JS_ASSERT(!frame.isYielding());
1959 assertSameCompartment(cx, frame);
1961 DebugScopes *scopes = cx->compartment()->debugScopes;
1962 if (!scopes)
1963 return;
1965 Rooted<DebugScopeObject*> debugScope(cx, nullptr);
1967 if (frame.fun()->isHeavyweight()) {
1968 /*
1969 * The frame may be observed before the prologue has created the
1970 * CallObject. See ScopeIter::settle.
1971 */
1972 if (!frame.hasCallObj())
1973 return;
1975 CallObject &callobj = frame.scopeChain()->as<CallObject>();
1976 scopes->liveScopes.remove(&callobj);
1977 if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj))
1978 debugScope = &p->value()->as<DebugScopeObject>();
1979 } else {
1980 ScopeIter si(frame, frame.script()->main(), cx);
1981 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
1982 debugScope = p->value();
1983 scopes->liveScopes.remove(&debugScope->scope().as<CallObject>());
1984 scopes->missingScopes.remove(p);
1985 }
1986 }
1988 /*
1989 * When the JS stack frame is popped, the values of unaliased variables
1990 * are lost. If there is any debug scope referring to this scope, save a
1991 * copy of the unaliased variables' values in an array for later debugger
1992 * access via DebugScopeProxy::handleUnaliasedAccess.
1993 *
1994 * Note: since it is simplest for this function to be infallible, failure
1995 * in this code will be silently ignored. This does not break any
1996 * invariants since DebugScopeObject::maybeSnapshot can already be nullptr.
1997 */
1998 if (debugScope) {
1999 /*
2000 * Copy all frame values into the snapshot, regardless of
2001 * aliasing. This unnecessarily includes aliased variables
2002 * but it simplifies later indexing logic.
2003 */
2004 AutoValueVector vec(cx);
2005 if (!frame.copyRawFrameSlots(&vec) || vec.length() == 0)
2006 return;
2008 /*
2009 * Copy in formals that are not aliased via the scope chain
2010 * but are aliased via the arguments object.
2011 */
2012 RootedScript script(cx, frame.script());
2013 if (script->analyzedArgsUsage() && script->needsArgsObj() && frame.hasArgsObj()) {
2014 for (unsigned i = 0; i < frame.numFormalArgs(); ++i) {
2015 if (script->formalLivesInArgumentsObject(i))
2016 vec[i] = frame.argsObj().arg(i);
2017 }
2018 }
2020 /*
2021 * Use a dense array as storage (since proxies do not have trace
2022 * hooks). This array must not escape into the wild.
2023 */
2024 RootedObject snapshot(cx, NewDenseCopiedArray(cx, vec.length(), vec.begin()));
2025 if (!snapshot) {
2026 cx->clearPendingException();
2027 return;
2028 }
2030 debugScope->initSnapshot(*snapshot);
2031 }
2032 }
2034 void
2035 DebugScopes::onPopBlock(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
2036 {
2037 assertSameCompartment(cx, frame);
2039 DebugScopes *scopes = cx->compartment()->debugScopes;
2040 if (!scopes)
2041 return;
2043 ScopeIter si(frame, pc, cx);
2044 onPopBlock(cx, si);
2045 }
2047 void
2048 DebugScopes::onPopBlock(JSContext *cx, const ScopeIter &si)
2049 {
2050 DebugScopes *scopes = cx->compartment()->debugScopes;
2051 if (!scopes)
2052 return;
2054 JS_ASSERT(si.type() == ScopeIter::Block);
2056 if (si.staticBlock().needsClone()) {
2057 ClonedBlockObject &clone = si.scope().as<ClonedBlockObject>();
2058 clone.copyUnaliasedValues(si.frame());
2059 scopes->liveScopes.remove(&clone);
2060 } else {
2061 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
2062 ClonedBlockObject &clone = p->value()->scope().as<ClonedBlockObject>();
2063 clone.copyUnaliasedValues(si.frame());
2064 scopes->liveScopes.remove(&clone);
2065 scopes->missingScopes.remove(p);
2066 }
2067 }
2068 }
2070 void
2071 DebugScopes::onPopWith(AbstractFramePtr frame)
2072 {
2073 DebugScopes *scopes = frame.compartment()->debugScopes;
2074 if (scopes)
2075 scopes->liveScopes.remove(&frame.scopeChain()->as<DynamicWithObject>());
2076 }
2078 void
2079 DebugScopes::onPopStrictEvalScope(AbstractFramePtr frame)
2080 {
2081 DebugScopes *scopes = frame.compartment()->debugScopes;
2082 if (!scopes)
2083 return;
2085 /*
2086 * The stack frame may be observed before the prologue has created the
2087 * CallObject. See ScopeIter::settle.
2088 */
2089 if (frame.hasCallObj())
2090 scopes->liveScopes.remove(&frame.scopeChain()->as<CallObject>());
2091 }
2093 void
2094 DebugScopes::onCompartmentLeaveDebugMode(JSCompartment *c)
2095 {
2096 DebugScopes *scopes = c->debugScopes;
2097 if (scopes) {
2098 scopes->proxiedScopes.clear();
2099 scopes->missingScopes.clear();
2100 scopes->liveScopes.clear();
2101 }
2102 }
2104 bool
2105 DebugScopes::updateLiveScopes(JSContext *cx)
2106 {
2107 JS_CHECK_RECURSION(cx, return false);
2109 /*
2110 * Note that we must always update the top frame's scope objects' entries
2111 * in liveScopes because we can't be sure code hasn't run in that frame to
2112 * change the scope chain since we were last called. The fp->prevUpToDate()
2113 * flag indicates whether the scopes of frames older than fp are already
2114 * included in liveScopes. It might seem simpler to have fp instead carry a
2115 * flag indicating whether fp itself is accurately described, but then we
2116 * would need to clear that flag whenever fp ran code. By storing the 'up
2117 * to date' bit for fp->prev() in fp, simply popping fp effectively clears
2118 * the flag for us, at exactly the time when execution resumes fp->prev().
2119 */
2120 for (AllFramesIter i(cx); !i.done(); ++i) {
2121 if (!i.hasUsableAbstractFramePtr())
2122 continue;
2124 AbstractFramePtr frame = i.abstractFramePtr();
2125 if (frame.scopeChain()->compartment() != cx->compartment())
2126 continue;
2128 if (frame.isFunctionFrame() && frame.callee()->isGenerator())
2129 continue;
2131 for (ScopeIter si(frame, i.pc(), cx); !si.done(); ++si) {
2132 if (si.hasScopeObject()) {
2133 JS_ASSERT(si.scope().compartment() == cx->compartment());
2134 DebugScopes *scopes = ensureCompartmentData(cx);
2135 if (!scopes)
2136 return false;
2137 if (!scopes->liveScopes.put(&si.scope(), si))
2138 return false;
2139 liveScopesPostWriteBarrier(cx->runtime(), &scopes->liveScopes, &si.scope());
2140 }
2141 }
2143 if (frame.prevUpToDate())
2144 return true;
2145 JS_ASSERT(frame.scopeChain()->compartment()->debugMode());
2146 frame.setPrevUpToDate();
2147 }
2149 return true;
2150 }
2152 ScopeIterVal*
2153 DebugScopes::hasLiveScope(ScopeObject &scope)
2154 {
2155 DebugScopes *scopes = scope.compartment()->debugScopes;
2156 if (!scopes)
2157 return nullptr;
2159 if (LiveScopeMap::Ptr p = scopes->liveScopes.lookup(&scope))
2160 return &p->value();
2162 return nullptr;
2163 }
2165 /*****************************************************************************/
2167 static JSObject *
2168 GetDebugScope(JSContext *cx, const ScopeIter &si);
2170 static DebugScopeObject *
2171 GetDebugScopeForScope(JSContext *cx, Handle<ScopeObject*> scope, const ScopeIter &enclosing)
2172 {
2173 if (DebugScopeObject *debugScope = DebugScopes::hasDebugScope(cx, *scope))
2174 return debugScope;
2176 RootedObject enclosingDebug(cx, GetDebugScope(cx, enclosing));
2177 if (!enclosingDebug)
2178 return nullptr;
2180 JSObject &maybeDecl = scope->enclosingScope();
2181 if (maybeDecl.is<DeclEnvObject>()) {
2182 JS_ASSERT(CallObjectLambdaName(scope->as<CallObject>().callee()));
2183 enclosingDebug = DebugScopeObject::create(cx, maybeDecl.as<DeclEnvObject>(), enclosingDebug);
2184 if (!enclosingDebug)
2185 return nullptr;
2186 }
2188 DebugScopeObject *debugScope = DebugScopeObject::create(cx, *scope, enclosingDebug);
2189 if (!debugScope)
2190 return nullptr;
2192 if (!DebugScopes::addDebugScope(cx, *scope, *debugScope))
2193 return nullptr;
2195 return debugScope;
2196 }
2198 static DebugScopeObject *
2199 GetDebugScopeForMissing(JSContext *cx, const ScopeIter &si)
2200 {
2201 if (DebugScopeObject *debugScope = DebugScopes::hasDebugScope(cx, si))
2202 return debugScope;
2204 ScopeIter copy(si, cx);
2205 RootedObject enclosingDebug(cx, GetDebugScope(cx, ++copy));
2206 if (!enclosingDebug)
2207 return nullptr;
2209 /*
2210 * Create the missing scope object. For block objects, this takes care of
2211 * storing variable values after the stack frame has been popped. For call
2212 * objects, we only use the pretend call object to access callee, bindings
2213 * and to receive dynamically added properties. Together, this provides the
2214 * nice invariant that every DebugScopeObject has a ScopeObject.
2215 *
2216 * Note: to preserve scopeChain depth invariants, these lazily-reified
2217 * scopes must not be put on the frame's scope chain; instead, they are
2218 * maintained via DebugScopes hooks.
2219 */
2220 DebugScopeObject *debugScope = nullptr;
2221 switch (si.type()) {
2222 case ScopeIter::Call: {
2223 // Generators should always reify their scopes.
2224 JS_ASSERT(!si.frame().callee()->isGenerator());
2225 Rooted<CallObject*> callobj(cx, CallObject::createForFunction(cx, si.frame()));
2226 if (!callobj)
2227 return nullptr;
2229 if (callobj->enclosingScope().is<DeclEnvObject>()) {
2230 JS_ASSERT(CallObjectLambdaName(callobj->callee()));
2231 DeclEnvObject &declenv = callobj->enclosingScope().as<DeclEnvObject>();
2232 enclosingDebug = DebugScopeObject::create(cx, declenv, enclosingDebug);
2233 if (!enclosingDebug)
2234 return nullptr;
2235 }
2237 debugScope = DebugScopeObject::create(cx, *callobj, enclosingDebug);
2238 break;
2239 }
2240 case ScopeIter::Block: {
2241 // Generators should always reify their scopes.
2242 JS_ASSERT_IF(si.frame().isFunctionFrame(), !si.frame().callee()->isGenerator());
2243 Rooted<StaticBlockObject *> staticBlock(cx, &si.staticBlock());
2244 ClonedBlockObject *block = ClonedBlockObject::create(cx, staticBlock, si.frame());
2245 if (!block)
2246 return nullptr;
2248 debugScope = DebugScopeObject::create(cx, *block, enclosingDebug);
2249 break;
2250 }
2251 case ScopeIter::With:
2252 case ScopeIter::StrictEvalScope:
2253 MOZ_ASSUME_UNREACHABLE("should already have a scope");
2254 }
2255 if (!debugScope)
2256 return nullptr;
2258 if (!DebugScopes::addDebugScope(cx, si, *debugScope))
2259 return nullptr;
2261 return debugScope;
2262 }
2264 static JSObject *
2265 GetDebugScope(JSContext *cx, JSObject &obj)
2266 {
2267 /*
2268 * As an engine invariant (maintained internally and asserted by Execute),
2269 * ScopeObjects and non-ScopeObjects cannot be interleaved on the scope
2270 * chain; every scope chain must start with zero or more ScopeObjects and
2271 * terminate with one or more non-ScopeObjects (viz., GlobalObject).
2272 */
2273 if (!obj.is<ScopeObject>()) {
2274 #ifdef DEBUG
2275 JSObject *o = &obj;
2276 while ((o = o->enclosingScope()))
2277 JS_ASSERT(!o->is<ScopeObject>());
2278 #endif
2279 return &obj;
2280 }
2282 Rooted<ScopeObject*> scope(cx, &obj.as<ScopeObject>());
2283 if (ScopeIterVal *maybeLiveScope = DebugScopes::hasLiveScope(*scope)) {
2284 ScopeIter si(*maybeLiveScope, cx);
2285 return GetDebugScope(cx, si);
2286 }
2287 ScopeIter si(scope->enclosingScope(), cx);
2288 return GetDebugScopeForScope(cx, scope, si);
2289 }
2291 static JSObject *
2292 GetDebugScope(JSContext *cx, const ScopeIter &si)
2293 {
2294 JS_CHECK_RECURSION(cx, return nullptr);
2296 if (si.done())
2297 return GetDebugScope(cx, si.enclosingScope());
2299 if (!si.hasScopeObject())
2300 return GetDebugScopeForMissing(cx, si);
2302 Rooted<ScopeObject*> scope(cx, &si.scope());
2304 ScopeIter copy(si, cx);
2305 return GetDebugScopeForScope(cx, scope, ++copy);
2306 }
2308 JSObject *
2309 js::GetDebugScopeForFunction(JSContext *cx, HandleFunction fun)
2310 {
2311 assertSameCompartment(cx, fun);
2312 JS_ASSERT(cx->compartment()->debugMode());
2313 if (!DebugScopes::updateLiveScopes(cx))
2314 return nullptr;
2315 return GetDebugScope(cx, *fun->environment());
2316 }
2318 JSObject *
2319 js::GetDebugScopeForFrame(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
2320 {
2321 assertSameCompartment(cx, frame);
2322 if (CanUseDebugScopeMaps(cx) && !DebugScopes::updateLiveScopes(cx))
2323 return nullptr;
2324 ScopeIter si(frame, pc, cx);
2325 return GetDebugScope(cx, si);
2326 }
2328 #ifdef DEBUG
2330 typedef HashSet<PropertyName *> PropertyNameSet;
2332 static bool
2333 RemoveReferencedNames(JSContext *cx, HandleScript script, PropertyNameSet &remainingNames)
2334 {
2335 // Remove from remainingNames --- the closure variables in some outer
2336 // script --- any free variables in this script. This analysis isn't perfect:
2337 //
2338 // - It will not account for free variables in an inner script which are
2339 // actually accessing some name in an intermediate script between the
2340 // inner and outer scripts. This can cause remainingNames to be an
2341 // underapproximation.
2342 //
2343 // - It will not account for new names introduced via eval. This can cause
2344 // remainingNames to be an overapproximation. This would be easy to fix
2345 // but is nice to have as the eval will probably not access these
2346 // these names and putting eval in an inner script is bad news if you
2347 // care about entraining variables unnecessarily.
2349 for (jsbytecode *pc = script->code(); pc != script->codeEnd(); pc += GetBytecodeLength(pc)) {
2350 PropertyName *name;
2352 switch (JSOp(*pc)) {
2353 case JSOP_NAME:
2354 case JSOP_SETNAME:
2355 name = script->getName(pc);
2356 break;
2358 case JSOP_GETALIASEDVAR:
2359 case JSOP_SETALIASEDVAR:
2360 name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
2361 break;
2363 default:
2364 name = nullptr;
2365 break;
2366 }
2368 if (name)
2369 remainingNames.remove(name);
2370 }
2372 if (script->hasObjects()) {
2373 ObjectArray *objects = script->objects();
2374 for (size_t i = 0; i < objects->length; i++) {
2375 JSObject *obj = objects->vector[i];
2376 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
2377 JSFunction *fun = &obj->as<JSFunction>();
2378 RootedScript innerScript(cx, fun->getOrCreateScript(cx));
2379 if (!innerScript)
2380 return false;
2382 if (!RemoveReferencedNames(cx, innerScript, remainingNames))
2383 return false;
2384 }
2385 }
2386 }
2388 return true;
2389 }
2391 static bool
2392 AnalyzeEntrainedVariablesInScript(JSContext *cx, HandleScript script, HandleScript innerScript)
2393 {
2394 PropertyNameSet remainingNames(cx);
2395 if (!remainingNames.init())
2396 return false;
2398 for (BindingIter bi(script); bi; bi++) {
2399 if (bi->aliased()) {
2400 PropertyNameSet::AddPtr p = remainingNames.lookupForAdd(bi->name());
2401 if (!p && !remainingNames.add(p, bi->name()))
2402 return false;
2403 }
2404 }
2406 if (!RemoveReferencedNames(cx, innerScript, remainingNames))
2407 return false;
2409 if (!remainingNames.empty()) {
2410 Sprinter buf(cx);
2411 if (!buf.init())
2412 return false;
2414 buf.printf("Script ");
2416 if (JSAtom *name = script->functionNonDelazifying()->displayAtom()) {
2417 buf.putString(name);
2418 buf.printf(" ");
2419 }
2421 buf.printf("(%s:%d) has variables entrained by ", script->filename(), script->lineno());
2423 if (JSAtom *name = innerScript->functionNonDelazifying()->displayAtom()) {
2424 buf.putString(name);
2425 buf.printf(" ");
2426 }
2428 buf.printf("(%s:%d) ::", innerScript->filename(), innerScript->lineno());
2430 for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) {
2431 buf.printf(" ");
2432 buf.putString(r.front());
2433 }
2435 printf("%s\n", buf.string());
2436 }
2438 if (innerScript->hasObjects()) {
2439 ObjectArray *objects = innerScript->objects();
2440 for (size_t i = 0; i < objects->length; i++) {
2441 JSObject *obj = objects->vector[i];
2442 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
2443 JSFunction *fun = &obj->as<JSFunction>();
2444 RootedScript innerInnerScript(cx, fun->getOrCreateScript(cx));
2445 if (!innerInnerScript ||
2446 !AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript))
2447 {
2448 return false;
2449 }
2450 }
2451 }
2452 }
2454 return true;
2455 }
2457 // Look for local variables in script or any other script inner to it, which are
2458 // part of the script's call object and are unnecessarily entrained by their own
2459 // inner scripts which do not refer to those variables. An example is:
2460 //
2461 // function foo() {
2462 // var a, b;
2463 // function bar() { return a; }
2464 // function baz() { return b; }
2465 // }
2466 //
2467 // |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|.
2468 bool
2469 js::AnalyzeEntrainedVariables(JSContext *cx, HandleScript script)
2470 {
2471 if (!script->hasObjects())
2472 return true;
2474 ObjectArray *objects = script->objects();
2475 for (size_t i = 0; i < objects->length; i++) {
2476 JSObject *obj = objects->vector[i];
2477 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
2478 JSFunction *fun = &obj->as<JSFunction>();
2479 RootedScript innerScript(cx, fun->getOrCreateScript(cx));
2480 if (!innerScript)
2481 return false;
2483 if (script->functionDelazifying() && script->functionDelazifying()->isHeavyweight()) {
2484 if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript))
2485 return false;
2486 }
2488 if (!AnalyzeEntrainedVariables(cx, innerScript))
2489 return false;
2490 }
2491 }
2493 return true;
2494 }
2496 #endif