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/. */
6
7 #include "vm/ScopeObject-inl.h"
8
9 #include "mozilla/PodOperations.h"
10 #include "mozilla/SizePrintfMacros.h"
11
12 #include "jscompartment.h"
13 #include "jsiter.h"
14
15 #include "builtin/ModuleObject.h"
16
17 #include "frontend/ParseNode.h"
18
19 #include "vm/ArgumentsObject.h"
20 #include "vm/GlobalObject.h"
21 #include "vm/ProxyObject.h"
22 #include "vm/Shape.h"
23 #include "vm/Xdr.h"
24
25 #include "jsatominlines.h"
26 #include "jsobjinlines.h"
27 #include "jsscriptinlines.h"
28
29 #include "vm/Stack-inl.h"
30
31 using namespace js;
32 using namespace js::gc;
33
34 using mozilla::PodZero;
35
36 typedef Rooted<ArgumentsObject*> RootedArgumentsObject;
37 typedef MutableHandle<ArgumentsObject*> MutableHandleArgumentsObject;
38
39 /*****************************************************************************/
40
41 Shape*
ScopeCoordinateToStaticScopeShape(JSScript * script,jsbytecode * pc)42 js::ScopeCoordinateToStaticScopeShape(JSScript* script, jsbytecode* pc)
43 {
44 MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
45 StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc));
46 uint32_t hops = ScopeCoordinate(pc).hops();
47 while (true) {
48 MOZ_ASSERT(!ssi.done());
49 if (ssi.hasSyntacticDynamicScopeObject()) {
50 if (!hops)
51 break;
52 hops--;
53 }
54 ssi++;
55 }
56 return ssi.scopeShape();
57 }
58
59 static const uint32_t SCOPE_COORDINATE_NAME_THRESHOLD = 20;
60
61 void
purge()62 ScopeCoordinateNameCache::purge()
63 {
64 shape = nullptr;
65 if (map.initialized())
66 map.finish();
67 }
68
69 PropertyName*
ScopeCoordinateName(ScopeCoordinateNameCache & cache,JSScript * script,jsbytecode * pc)70 js::ScopeCoordinateName(ScopeCoordinateNameCache& cache, JSScript* script, jsbytecode* pc)
71 {
72 Shape* shape = ScopeCoordinateToStaticScopeShape(script, pc);
73 if (shape != cache.shape && shape->slot() >= SCOPE_COORDINATE_NAME_THRESHOLD) {
74 cache.purge();
75 if (cache.map.init(shape->slot())) {
76 cache.shape = shape;
77 Shape::Range<NoGC> r(shape);
78 while (!r.empty()) {
79 if (!cache.map.putNew(r.front().slot(), r.front().propid())) {
80 cache.purge();
81 break;
82 }
83 r.popFront();
84 }
85 }
86 }
87
88 jsid id;
89 ScopeCoordinate sc(pc);
90 if (shape == cache.shape) {
91 ScopeCoordinateNameCache::Map::Ptr p = cache.map.lookup(sc.slot());
92 id = p->value();
93 } else {
94 Shape::Range<NoGC> r(shape);
95 while (r.front().slot() != sc.slot())
96 r.popFront();
97 id = r.front().propidRaw();
98 }
99
100 /* Beware nameless destructuring formal. */
101 if (!JSID_IS_ATOM(id))
102 return script->runtimeFromAnyThread()->commonNames->empty;
103 return JSID_TO_ATOM(id)->asPropertyName();
104 }
105
106 JSScript*
ScopeCoordinateFunctionScript(JSScript * script,jsbytecode * pc)107 js::ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc)
108 {
109 MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_SCOPECOORD);
110 StaticScopeIter<NoGC> ssi(script->innermostStaticScopeInScript(pc));
111 uint32_t hops = ScopeCoordinate(pc).hops();
112 while (true) {
113 if (ssi.hasSyntacticDynamicScopeObject()) {
114 if (!hops)
115 break;
116 hops--;
117 }
118 ssi++;
119 }
120 if (ssi.type() != StaticScopeIter<NoGC>::Function)
121 return nullptr;
122 return ssi.funScript();
123 }
124
125 /*****************************************************************************/
126
127 void
setEnclosingScope(HandleObject obj)128 ScopeObject::setEnclosingScope(HandleObject obj)
129 {
130 MOZ_ASSERT_IF(obj->is<LexicalScopeBase>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
131 obj->isDelegate());
132 setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
133 }
134
135 CallObject*
create(JSContext * cx,HandleShape shape,HandleObjectGroup group,uint32_t lexicalBegin)136 CallObject::create(JSContext* cx, HandleShape shape, HandleObjectGroup group, uint32_t lexicalBegin)
137 {
138 MOZ_ASSERT(!group->singleton(),
139 "passed a singleton group to create() (use createSingleton() "
140 "instead)");
141 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
142 MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
143 kind = gc::GetBackgroundAllocKind(kind);
144
145 JSObject* obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, group);
146 if (!obj)
147 return nullptr;
148
149 obj->as<CallObject>().initRemainingSlotsToUninitializedLexicals(lexicalBegin);
150 return &obj->as<CallObject>();
151 }
152
153 CallObject*
createSingleton(JSContext * cx,HandleShape shape,uint32_t lexicalBegin)154 CallObject::createSingleton(JSContext* cx, HandleShape shape, uint32_t lexicalBegin)
155 {
156 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
157 MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
158 kind = gc::GetBackgroundAllocKind(kind);
159
160 RootedObjectGroup group(cx, ObjectGroup::lazySingletonGroup(cx, &class_, TaggedProto(nullptr)));
161 if (!group)
162 return nullptr;
163 RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, group));
164 if (!obj)
165 return nullptr;
166
167 MOZ_ASSERT(obj->isSingleton(),
168 "group created inline above must be a singleton");
169
170 obj->as<CallObject>().initRemainingSlotsToUninitializedLexicals(lexicalBegin);
171 return &obj->as<CallObject>();
172 }
173
174 /*
175 * Create a CallObject for a JSScript that is not initialized to any particular
176 * callsite. This object can either be initialized (with an enclosing scope and
177 * callee) or used as a template for jit compilation.
178 */
179 CallObject*
createTemplateObject(JSContext * cx,HandleScript script,gc::InitialHeap heap)180 CallObject::createTemplateObject(JSContext* cx, HandleScript script, gc::InitialHeap heap)
181 {
182 RootedShape shape(cx, script->bindings.callObjShape());
183 MOZ_ASSERT(shape->getObjectClass() == &class_);
184
185 RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
186 if (!group)
187 return nullptr;
188
189 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
190 MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
191 kind = gc::GetBackgroundAllocKind(kind);
192
193 JSObject* obj = JSObject::create(cx, kind, heap, shape, group);
194 if (!obj)
195 return nullptr;
196
197 // Set uninitialized lexicals even on template objects, as Ion will copy
198 // over the template object's slot values in the fast path.
199 obj->as<CallObject>().initAliasedLexicalsToThrowOnTouch(script);
200
201 return &obj->as<CallObject>();
202 }
203
204 /*
205 * Construct a call object for the given bindings. If this is a call object
206 * for a function invocation, callee should be the function being called.
207 * Otherwise it must be a call object for eval of strict mode code, and callee
208 * must be null.
209 */
210 CallObject*
create(JSContext * cx,HandleScript script,HandleObject enclosing,HandleFunction callee)211 CallObject::create(JSContext* cx, HandleScript script, HandleObject enclosing, HandleFunction callee)
212 {
213 gc::InitialHeap heap = script->treatAsRunOnce() ? gc::TenuredHeap : gc::DefaultHeap;
214 CallObject* callobj = CallObject::createTemplateObject(cx, script, heap);
215 if (!callobj)
216 return nullptr;
217
218 callobj->setEnclosingScope(enclosing);
219 callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
220
221 if (script->treatAsRunOnce()) {
222 Rooted<CallObject*> ncallobj(cx, callobj);
223 if (!JSObject::setSingleton(cx, ncallobj))
224 return nullptr;
225 return ncallobj;
226 }
227
228 return callobj;
229 }
230
231 CallObject*
createForFunction(JSContext * cx,HandleObject enclosing,HandleFunction callee)232 CallObject::createForFunction(JSContext* cx, HandleObject enclosing, HandleFunction callee)
233 {
234 RootedObject scopeChain(cx, enclosing);
235 MOZ_ASSERT(scopeChain);
236
237 /*
238 * For a named function expression Call's parent points to an environment
239 * object holding function's name.
240 */
241 if (callee->isNamedLambda()) {
242 scopeChain = DeclEnvObject::create(cx, scopeChain, callee);
243 if (!scopeChain)
244 return nullptr;
245 }
246
247 RootedScript script(cx, callee->nonLazyScript());
248 return create(cx, script, scopeChain, callee);
249 }
250
251 CallObject*
createForFunction(JSContext * cx,AbstractFramePtr frame)252 CallObject::createForFunction(JSContext* cx, AbstractFramePtr frame)
253 {
254 MOZ_ASSERT(frame.isNonEvalFunctionFrame());
255 assertSameCompartment(cx, frame);
256
257 RootedObject scopeChain(cx, frame.scopeChain());
258 RootedFunction callee(cx, frame.callee());
259
260 CallObject* callobj = createForFunction(cx, scopeChain, callee);
261 if (!callobj)
262 return nullptr;
263
264 /* Copy in the closed-over formal arguments. */
265 for (AliasedFormalIter i(frame.script()); i; i++) {
266 callobj->setAliasedVar(cx, i, i->name(),
267 frame.unaliasedFormal(i.frameIndex(), DONT_CHECK_ALIASING));
268 }
269
270 return callobj;
271 }
272
273 CallObject*
createForStrictEval(JSContext * cx,AbstractFramePtr frame)274 CallObject::createForStrictEval(JSContext* cx, AbstractFramePtr frame)
275 {
276 MOZ_ASSERT(frame.isStrictEvalFrame());
277 MOZ_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterFrame() == frame.asInterpreterFrame());
278 MOZ_ASSERT_IF(frame.isInterpreterFrame(), cx->interpreterRegs().pc == frame.script()->code());
279
280 RootedFunction callee(cx);
281 RootedScript script(cx, frame.script());
282 RootedObject scopeChain(cx, frame.scopeChain());
283 return create(cx, script, scopeChain, callee);
284 }
285
286 CallObject*
createHollowForDebug(JSContext * cx,HandleFunction callee)287 CallObject::createHollowForDebug(JSContext* cx, HandleFunction callee)
288 {
289 MOZ_ASSERT(!callee->needsCallObject());
290
291 // This scope's parent link is never used: the DebugScopeObject that
292 // refers to this scope carries its own parent link, which is what
293 // Debugger uses to construct the tree of Debugger.Environment objects. So
294 // just parent this scope directly to the global lexical scope.
295 Rooted<GlobalObject*> global(cx, &callee->global());
296 RootedObject globalLexical(cx, &global->lexicalScope());
297 Rooted<CallObject*> callobj(cx, createForFunction(cx, globalLexical, callee));
298 if (!callobj)
299 return nullptr;
300
301 RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT));
302 RootedId id(cx);
303 RootedScript script(cx, callee->nonLazyScript());
304 for (BindingIter bi(script); !bi.done(); bi++) {
305 id = NameToId(bi->name());
306 if (!SetProperty(cx, callobj, id, optimizedOut))
307 return nullptr;
308 }
309
310 return callobj;
311 }
312
313 const Class CallObject::class_ = {
314 "Call",
315 JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS)
316 };
317
318 /*****************************************************************************/
319
320 const Class ModuleEnvironmentObject::class_ = {
321 "ModuleEnvironmentObject",
322 JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS) |
323 JSCLASS_IS_ANONYMOUS,
324 nullptr, /* addProperty */
325 nullptr, /* delProperty */
326 nullptr, /* getProperty */
327 nullptr, /* setProperty */
328 nullptr, /* enumerate */
329 nullptr, /* resolve */
330 nullptr, /* mayResolve */
331 nullptr, /* finalize */
332 nullptr, /* call */
333 nullptr, /* hasInstance */
334 nullptr, /* construct */
335 nullptr, /* trace */
336 JS_NULL_CLASS_SPEC,
337 JS_NULL_CLASS_EXT,
338 {
339 ModuleEnvironmentObject::lookupProperty,
340 nullptr, /* defineProperty */
341 ModuleEnvironmentObject::hasProperty,
342 ModuleEnvironmentObject::getProperty,
343 ModuleEnvironmentObject::setProperty,
344 ModuleEnvironmentObject::getOwnPropertyDescriptor,
345 ModuleEnvironmentObject::deleteProperty,
346 nullptr, nullptr, /* watch/unwatch */
347 nullptr, /* getElements */
348 ModuleEnvironmentObject::enumerate,
349 nullptr
350 }
351 };
352
353 /* static */ ModuleEnvironmentObject*
create(ExclusiveContext * cx,HandleModuleObject module)354 ModuleEnvironmentObject::create(ExclusiveContext* cx, HandleModuleObject module)
355 {
356 RootedScript script(cx, module->script());
357 RootedShape shape(cx, script->bindings.callObjShape());
358 MOZ_ASSERT(shape->getObjectClass() == &class_);
359
360 RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
361 if (!group)
362 return nullptr;
363
364 gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
365 MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
366 kind = gc::GetBackgroundAllocKind(kind);
367
368 JSObject* obj = JSObject::create(cx, kind, TenuredHeap, shape, group);
369 if (!obj)
370 return nullptr;
371
372 RootedModuleEnvironmentObject scope(cx, &obj->as<ModuleEnvironmentObject>());
373
374 // Set uninitialized lexicals even on template objects, as Ion will use
375 // copy over the template object's slot values in the fast path.
376 scope->initAliasedLexicalsToThrowOnTouch(script);
377
378 scope->initFixedSlot(MODULE_SLOT, ObjectValue(*module));
379 if (!JSObject::setSingleton(cx, scope))
380 return nullptr;
381
382 // Initialize this early so that we can manipulate the scope object without
383 // causing assertions.
384 RootedObject globalLexical(cx, &cx->global()->lexicalScope());
385 scope->setEnclosingScope(globalLexical);
386
387 // It is not be possible to add or remove bindings from a module environment
388 // after this point as module code is always strict.
389 #ifdef DEBUG
390 for (Shape::Range<NoGC> r(scope->lastProperty()); !r.empty(); r.popFront())
391 MOZ_ASSERT(!r.front().configurable());
392 MOZ_ASSERT(scope->lastProperty()->getObjectFlags() & BaseShape::NOT_EXTENSIBLE);
393 MOZ_ASSERT(!scope->inDictionaryMode());
394 #endif
395
396 return scope;
397 }
398
399 ModuleObject&
module()400 ModuleEnvironmentObject::module()
401 {
402 return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>();
403 }
404
405 IndirectBindingMap&
importBindings()406 ModuleEnvironmentObject::importBindings()
407 {
408 return module().importBindings();
409 }
410
411 bool
createImportBinding(JSContext * cx,HandleAtom importName,HandleModuleObject module,HandleAtom localName)412 ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importName,
413 HandleModuleObject module, HandleAtom localName)
414 {
415 RootedId importNameId(cx, AtomToId(importName));
416 RootedId localNameId(cx, AtomToId(localName));
417 RootedModuleEnvironmentObject env(cx, module->environment());
418 if (!importBindings().putNew(cx, importNameId, env, localNameId)) {
419 ReportOutOfMemory(cx);
420 return false;
421 }
422
423 return true;
424 }
425
426 bool
hasImportBinding(HandlePropertyName name)427 ModuleEnvironmentObject::hasImportBinding(HandlePropertyName name)
428 {
429 return importBindings().has(NameToId(name));
430 }
431
432 bool
lookupImport(jsid name,ModuleEnvironmentObject ** envOut,Shape ** shapeOut)433 ModuleEnvironmentObject::lookupImport(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut)
434 {
435 return importBindings().lookup(name, envOut, shapeOut);
436 }
437
438 /* static */ bool
lookupProperty(JSContext * cx,HandleObject obj,HandleId id,MutableHandleObject objp,MutableHandleShape propp)439 ModuleEnvironmentObject::lookupProperty(JSContext* cx, HandleObject obj, HandleId id,
440 MutableHandleObject objp, MutableHandleShape propp)
441 {
442 const IndirectBindingMap& bindings = obj->as<ModuleEnvironmentObject>().importBindings();
443 Shape* shape;
444 ModuleEnvironmentObject* env;
445 if (bindings.lookup(id, &env, &shape)) {
446 objp.set(env);
447 propp.set(shape);
448 return true;
449 }
450
451 RootedNativeObject target(cx, &obj->as<NativeObject>());
452 if (!NativeLookupOwnProperty<CanGC>(cx, target, id, propp))
453 return false;
454
455 objp.set(obj);
456 return true;
457 }
458
459 /* static */ bool
hasProperty(JSContext * cx,HandleObject obj,HandleId id,bool * foundp)460 ModuleEnvironmentObject::hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
461 {
462 if (obj->as<ModuleEnvironmentObject>().importBindings().has(id)) {
463 *foundp = true;
464 return true;
465 }
466
467 RootedNativeObject self(cx, &obj->as<NativeObject>());
468 return NativeHasProperty(cx, self, id, foundp);
469 }
470
471 /* static */ bool
getProperty(JSContext * cx,HandleObject obj,HandleValue receiver,HandleId id,MutableHandleValue vp)472 ModuleEnvironmentObject::getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
473 HandleId id, MutableHandleValue vp)
474 {
475 const IndirectBindingMap& bindings = obj->as<ModuleEnvironmentObject>().importBindings();
476 Shape* shape;
477 ModuleEnvironmentObject* env;
478 if (bindings.lookup(id, &env, &shape)) {
479 vp.set(env->getSlot(shape->slot()));
480 return true;
481 }
482
483 RootedNativeObject self(cx, &obj->as<NativeObject>());
484 return NativeGetProperty(cx, self, receiver, id, vp);
485 }
486
487 /* static */ bool
setProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue v,HandleValue receiver,JS::ObjectOpResult & result)488 ModuleEnvironmentObject::setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
489 HandleValue receiver, JS::ObjectOpResult& result)
490 {
491 RootedModuleEnvironmentObject self(cx, &obj->as<ModuleEnvironmentObject>());
492 if (self->importBindings().has(id))
493 return result.failReadOnly();
494
495 return NativeSetProperty(cx, self, id, v, receiver, Qualified, result);
496 }
497
498 /* static */ bool
getOwnPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<JSPropertyDescriptor> desc)499 ModuleEnvironmentObject::getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
500 MutableHandle<JSPropertyDescriptor> desc)
501 {
502 // We never call this hook on scope objects.
503 MOZ_CRASH();
504 }
505
506 /* static */ bool
deleteProperty(JSContext * cx,HandleObject obj,HandleId id,ObjectOpResult & result)507 ModuleEnvironmentObject::deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
508 ObjectOpResult& result)
509 {
510 return result.failCantDelete();
511 }
512
513 /* static */ bool
enumerate(JSContext * cx,HandleObject obj,AutoIdVector & properties,bool enumerableOnly)514 ModuleEnvironmentObject::enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
515 bool enumerableOnly)
516 {
517 RootedModuleEnvironmentObject self(cx, &obj->as<ModuleEnvironmentObject>());
518 const IndirectBindingMap& bs(self->importBindings());
519
520 MOZ_ASSERT(properties.length() == 0);
521 size_t count = bs.count() + self->slotSpan() - RESERVED_SLOTS;
522 if (!properties.reserve(count)) {
523 ReportOutOfMemory(cx);
524 return false;
525 }
526
527 bs.forEachExportedName([&] (jsid name) {
528 properties.infallibleAppend(name);
529 });
530
531 for (Shape::Range<NoGC> r(self->lastProperty()); !r.empty(); r.popFront())
532 properties.infallibleAppend(r.front().propid());
533
534 MOZ_ASSERT(properties.length() == count);
535 return true;
536 }
537
538 /*****************************************************************************/
539
540 const Class DeclEnvObject::class_ = {
541 js_Object_str,
542 JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
543 JSCLASS_HAS_CACHED_PROTO(JSProto_Object)
544 };
545
546 /*
547 * Create a DeclEnvObject for a JSScript that is not initialized to any
548 * particular callsite. This object can either be initialized (with an enclosing
549 * scope and callee) or used as a template for jit compilation.
550 */
551 DeclEnvObject*
createTemplateObject(JSContext * cx,HandleFunction fun,NewObjectKind newKind)552 DeclEnvObject::createTemplateObject(JSContext* cx, HandleFunction fun, NewObjectKind newKind)
553 {
554 Rooted<DeclEnvObject*> obj(cx);
555 obj = NewObjectWithNullTaggedProto<DeclEnvObject>(cx, newKind, BaseShape::DELEGATE);
556 if (!obj)
557 return nullptr;
558
559 // Assign a fixed slot to a property with the same name as the lambda.
560 Rooted<jsid> id(cx, AtomToId(fun->atom()));
561 const Class* clasp = obj->getClass();
562 unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
563
564 JSGetterOp getter = clasp->getProperty;
565 JSSetterOp setter = clasp->setProperty;
566 MOZ_ASSERT(getter != JS_PropertyStub);
567 MOZ_ASSERT(setter != JS_StrictPropertyStub);
568
569 if (!NativeObject::putProperty(cx, obj, id, getter, setter, lambdaSlot(), attrs, 0))
570 return nullptr;
571
572 MOZ_ASSERT(!obj->hasDynamicSlots());
573 return obj;
574 }
575
576 DeclEnvObject*
create(JSContext * cx,HandleObject enclosing,HandleFunction callee)577 DeclEnvObject::create(JSContext* cx, HandleObject enclosing, HandleFunction callee)
578 {
579 Rooted<DeclEnvObject*> obj(cx, createTemplateObject(cx, callee, GenericObject));
580 if (!obj)
581 return nullptr;
582
583 obj->setEnclosingScope(enclosing);
584 obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
585 return obj;
586 }
587
588 template<XDRMode mode>
589 bool
XDRStaticWithObject(XDRState<mode> * xdr,HandleObject enclosingScope,MutableHandle<StaticWithObject * > objp)590 js::XDRStaticWithObject(XDRState<mode>* xdr, HandleObject enclosingScope,
591 MutableHandle<StaticWithObject*> objp)
592 {
593 if (mode == XDR_DECODE) {
594 JSContext* cx = xdr->cx();
595 Rooted<StaticWithObject*> obj(cx, StaticWithObject::create(cx));
596 if (!obj)
597 return false;
598 obj->initEnclosingScope(enclosingScope);
599 objp.set(obj);
600 }
601 // For encoding, there is nothing to do. The only information that is
602 // encoded by a StaticWithObject is its presence on the scope chain, and the
603 // script XDR handler already takes care of that.
604
605 return true;
606 }
607
608 template bool
609 js::XDRStaticWithObject(XDRState<XDR_ENCODE>*, HandleObject, MutableHandle<StaticWithObject*>);
610
611 template bool
612 js::XDRStaticWithObject(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticWithObject*>);
613
614 StaticWithObject*
create(ExclusiveContext * cx)615 StaticWithObject::create(ExclusiveContext* cx)
616 {
617 return NewObjectWithNullTaggedProto<StaticWithObject>(cx, TenuredObject, BaseShape::DELEGATE);
618 }
619
620 static JSObject*
CloneStaticWithObject(JSContext * cx,HandleObject enclosingScope,Handle<StaticWithObject * > srcWith)621 CloneStaticWithObject(JSContext* cx, HandleObject enclosingScope, Handle<StaticWithObject*> srcWith)
622 {
623 Rooted<StaticWithObject*> clone(cx, StaticWithObject::create(cx));
624 if (!clone)
625 return nullptr;
626
627 clone->initEnclosingScope(enclosingScope);
628
629 return clone;
630 }
631
632 DynamicWithObject*
create(JSContext * cx,HandleObject object,HandleObject enclosing,HandleObject staticWith,WithKind kind)633 DynamicWithObject::create(JSContext* cx, HandleObject object, HandleObject enclosing,
634 HandleObject staticWith, WithKind kind)
635 {
636 MOZ_ASSERT(staticWith->is<StaticWithObject>());
637
638 Rooted<TaggedProto> proto(cx, TaggedProto(staticWith));
639 Rooted<DynamicWithObject*> obj(cx);
640 obj = NewObjectWithGivenTaggedProto<DynamicWithObject>(cx, proto, GenericObject,
641 BaseShape::DELEGATE);
642 if (!obj)
643 return nullptr;
644
645 Value thisv = GetThisValue(object);
646
647 obj->setEnclosingScope(enclosing);
648 obj->setFixedSlot(OBJECT_SLOT, ObjectValue(*object));
649 obj->setFixedSlot(THIS_SLOT, thisv);
650 obj->setFixedSlot(KIND_SLOT, Int32Value(kind));
651
652 return obj;
653 }
654
655 static bool
with_LookupProperty(JSContext * cx,HandleObject obj,HandleId id,MutableHandleObject objp,MutableHandleShape propp)656 with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
657 MutableHandleObject objp, MutableHandleShape propp)
658 {
659 if (JSID_IS_ATOM(id, cx->names().dotThis)) {
660 objp.set(nullptr);
661 propp.set(nullptr);
662 return true;
663 }
664 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
665 return LookupProperty(cx, actual, id, objp, propp);
666 }
667
668 static bool
with_DefineProperty(JSContext * cx,HandleObject obj,HandleId id,Handle<PropertyDescriptor> desc,ObjectOpResult & result)669 with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
670 ObjectOpResult& result)
671 {
672 MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
673 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
674 return DefineProperty(cx, actual, id, desc, result);
675 }
676
677 static bool
with_HasProperty(JSContext * cx,HandleObject obj,HandleId id,bool * foundp)678 with_HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
679 {
680 MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
681 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
682 return HasProperty(cx, actual, id, foundp);
683 }
684
685 static bool
with_GetProperty(JSContext * cx,HandleObject obj,HandleValue receiver,HandleId id,MutableHandleValue vp)686 with_GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
687 MutableHandleValue vp)
688 {
689 MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
690 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
691 RootedValue actualReceiver(cx, receiver);
692 if (receiver.isObject() && &receiver.toObject() == obj)
693 actualReceiver.setObject(*actual);
694 return GetProperty(cx, actual, actualReceiver, id, vp);
695 }
696
697 static bool
with_SetProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result)698 with_SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
699 HandleValue receiver, ObjectOpResult& result)
700 {
701 MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
702 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
703 RootedValue actualReceiver(cx, receiver);
704 if (receiver.isObject() && &receiver.toObject() == obj)
705 actualReceiver.setObject(*actual);
706 return SetProperty(cx, actual, id, v, actualReceiver, result);
707 }
708
709 static bool
with_GetOwnPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<JSPropertyDescriptor> desc)710 with_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
711 MutableHandle<JSPropertyDescriptor> desc)
712 {
713 MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
714 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
715 return GetOwnPropertyDescriptor(cx, actual, id, desc);
716 }
717
718 static bool
with_DeleteProperty(JSContext * cx,HandleObject obj,HandleId id,ObjectOpResult & result)719 with_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
720 {
721 MOZ_ASSERT(!JSID_IS_ATOM(id, cx->names().dotThis));
722 RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
723 return DeleteProperty(cx, actual, id, result);
724 }
725
726 const Class StaticWithObject::class_ = {
727 "WithTemplate",
728 JSCLASS_HAS_RESERVED_SLOTS(StaticWithObject::RESERVED_SLOTS) |
729 JSCLASS_IS_ANONYMOUS
730 };
731
732 const Class DynamicWithObject::class_ = {
733 "With",
734 JSCLASS_HAS_RESERVED_SLOTS(DynamicWithObject::RESERVED_SLOTS) |
735 JSCLASS_IS_ANONYMOUS,
736 nullptr, /* addProperty */
737 nullptr, /* delProperty */
738 nullptr, /* getProperty */
739 nullptr, /* setProperty */
740 nullptr, /* enumerate */
741 nullptr, /* resolve */
742 nullptr, /* mayResolve */
743 nullptr, /* finalize */
744 nullptr, /* call */
745 nullptr, /* hasInstance */
746 nullptr, /* construct */
747 nullptr, /* trace */
748 JS_NULL_CLASS_SPEC,
749 JS_NULL_CLASS_EXT,
750 {
751 with_LookupProperty,
752 with_DefineProperty,
753 with_HasProperty,
754 with_GetProperty,
755 with_SetProperty,
756 with_GetOwnPropertyDescriptor,
757 with_DeleteProperty,
758 nullptr, nullptr, /* watch/unwatch */
759 nullptr, /* getElements */
760 nullptr, /* enumerate (native enumeration of target doesn't work) */
761 nullptr,
762 }
763 };
764
765 /* static */ StaticEvalObject*
create(JSContext * cx,HandleObject enclosing)766 StaticEvalObject::create(JSContext* cx, HandleObject enclosing)
767 {
768 StaticEvalObject* obj =
769 NewObjectWithNullTaggedProto<StaticEvalObject>(cx, TenuredObject, BaseShape::DELEGATE);
770 if (!obj)
771 return nullptr;
772
773 obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(enclosing));
774 obj->setReservedSlot(STRICT_SLOT, BooleanValue(false));
775 return obj;
776 }
777
778 const Class StaticEvalObject::class_ = {
779 "StaticEval",
780 JSCLASS_HAS_RESERVED_SLOTS(StaticEvalObject::RESERVED_SLOTS) |
781 JSCLASS_IS_ANONYMOUS
782 };
783
784 /* static */ StaticNonSyntacticScopeObjects*
create(JSContext * cx,HandleObject enclosing)785 StaticNonSyntacticScopeObjects::create(JSContext*cx, HandleObject enclosing)
786 {
787 StaticNonSyntacticScopeObjects* obj =
788 NewObjectWithNullTaggedProto<StaticNonSyntacticScopeObjects>(cx, TenuredObject,
789 BaseShape::DELEGATE);
790 if (!obj)
791 return nullptr;
792
793 obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectOrNullValue(enclosing));
794 return obj;
795 }
796
797 const Class StaticNonSyntacticScopeObjects::class_ = {
798 "StaticNonSyntacticScopeObjects",
799 JSCLASS_HAS_RESERVED_SLOTS(StaticNonSyntacticScopeObjects::RESERVED_SLOTS) |
800 JSCLASS_IS_ANONYMOUS
801 };
802
803 /* static */ NonSyntacticVariablesObject*
create(JSContext * cx,Handle<ClonedBlockObject * > globalLexical)804 NonSyntacticVariablesObject::create(JSContext* cx, Handle<ClonedBlockObject*> globalLexical)
805 {
806 MOZ_ASSERT(globalLexical->isGlobal());
807
808 Rooted<NonSyntacticVariablesObject*> obj(cx,
809 NewObjectWithNullTaggedProto<NonSyntacticVariablesObject>(cx, TenuredObject,
810 BaseShape::DELEGATE));
811 if (!obj)
812 return nullptr;
813
814 MOZ_ASSERT(obj->isUnqualifiedVarObj());
815 if (!obj->setQualifiedVarObj(cx))
816 return nullptr;
817
818 obj->setEnclosingScope(globalLexical);
819 return obj;
820 }
821
822 const Class NonSyntacticVariablesObject::class_ = {
823 "NonSyntacticVariablesObject",
824 JSCLASS_HAS_RESERVED_SLOTS(NonSyntacticVariablesObject::RESERVED_SLOTS) |
825 JSCLASS_IS_ANONYMOUS
826 };
827
828 /*****************************************************************************/
829
830 bool
isExtensible() const831 BlockObject::isExtensible() const
832 {
833 return nonProxyIsExtensible();
834 }
835
836 /* static */ ClonedBlockObject*
create(JSContext * cx,Handle<StaticBlockObject * > block,HandleObject enclosing)837 ClonedBlockObject::create(JSContext* cx, Handle<StaticBlockObject*> block, HandleObject enclosing)
838 {
839 MOZ_ASSERT(block->getClass() == &BlockObject::class_);
840
841 RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &BlockObject::class_,
842 TaggedProto(block.get())));
843 if (!group)
844 return nullptr;
845
846 RootedShape shape(cx, block->lastProperty());
847
848 gc::AllocKind allocKind = gc::GetGCObjectKind(&BlockObject::class_);
849 if (CanBeFinalizedInBackground(allocKind, &BlockObject::class_))
850 allocKind = GetBackgroundAllocKind(allocKind);
851 RootedNativeObject obj(cx, MaybeNativeObject(JSObject::create(cx, allocKind,
852 gc::TenuredHeap, shape, group)));
853 if (!obj)
854 return nullptr;
855
856 MOZ_ASSERT(!obj->inDictionaryMode());
857 MOZ_ASSERT(obj->slotSpan() >= block->numVariables() + RESERVED_SLOTS);
858
859 obj->setReservedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*enclosing));
860
861 MOZ_ASSERT(obj->isDelegate());
862
863 ClonedBlockObject* res = &obj->as<ClonedBlockObject>();
864
865 if (res->isGlobal() || !res->isSyntactic())
866 res->setReservedSlot(THIS_VALUE_SLOT, GetThisValue(enclosing));
867
868 return res;
869 }
870
871 /* static */ ClonedBlockObject*
create(JSContext * cx,Handle<StaticBlockObject * > block,AbstractFramePtr frame)872 ClonedBlockObject::create(JSContext* cx, Handle<StaticBlockObject*> block, AbstractFramePtr frame)
873 {
874 assertSameCompartment(cx, frame);
875 RootedObject enclosing(cx, frame.scopeChain());
876 return create(cx, block, enclosing);
877 }
878
879 /* static */ ClonedBlockObject*
createGlobal(JSContext * cx,Handle<GlobalObject * > global)880 ClonedBlockObject::createGlobal(JSContext* cx, Handle<GlobalObject*> global)
881 {
882 Rooted<StaticBlockObject*> staticLexical(cx, StaticBlockObject::create(cx));
883 if (!staticLexical)
884 return nullptr;
885
886 // Currently the global lexical scope cannot have any bindings with frame
887 // slots.
888 staticLexical->setLocalOffset(UINT32_MAX);
889 staticLexical->initEnclosingScope(nullptr);
890 Rooted<ClonedBlockObject*> lexical(cx, ClonedBlockObject::create(cx, staticLexical, global));
891 if (!lexical)
892 return nullptr;
893 if (!JSObject::setSingleton(cx, lexical))
894 return nullptr;
895 return lexical;
896 }
897
898 /* static */ ClonedBlockObject*
createNonSyntactic(JSContext * cx,HandleObject enclosingStatic,HandleObject enclosingScope)899 ClonedBlockObject::createNonSyntactic(JSContext* cx, HandleObject enclosingStatic,
900 HandleObject enclosingScope)
901 {
902 MOZ_ASSERT(enclosingStatic->is<StaticNonSyntacticScopeObjects>());
903 MOZ_ASSERT(!IsSyntacticScope(enclosingScope));
904
905 Rooted<StaticBlockObject*> staticLexical(cx, StaticBlockObject::create(cx));
906 if (!staticLexical)
907 return nullptr;
908
909 staticLexical->setLocalOffset(UINT32_MAX);
910 staticLexical->initEnclosingScope(enclosingStatic);
911 Rooted<ClonedBlockObject*> lexical(cx, ClonedBlockObject::create(cx, staticLexical,
912 enclosingScope));
913 if (!lexical)
914 return nullptr;
915 return lexical;
916 }
917
918 /* static */ ClonedBlockObject*
createHollowForDebug(JSContext * cx,Handle<StaticBlockObject * > block)919 ClonedBlockObject::createHollowForDebug(JSContext* cx, Handle<StaticBlockObject*> block)
920 {
921 MOZ_ASSERT(!block->needsClone());
922
923 // This scope's parent link is never used: the DebugScopeObject that
924 // refers to this scope carries its own parent link, which is what
925 // Debugger uses to construct the tree of Debugger.Environment objects. So
926 // just parent this scope directly to the global lexical scope.
927 Rooted<GlobalObject*> global(cx, &block->global());
928 RootedObject globalLexical(cx, &global->lexicalScope());
929 Rooted<ClonedBlockObject*> obj(cx, create(cx, block, globalLexical));
930 if (!obj)
931 return nullptr;
932
933 for (unsigned i = 0; i < block->numVariables(); i++)
934 obj->setVar(i, MagicValue(JS_OPTIMIZED_OUT), DONT_CHECK_ALIASING);
935
936 return obj;
937 }
938
939 void
copyUnaliasedValues(AbstractFramePtr frame)940 ClonedBlockObject::copyUnaliasedValues(AbstractFramePtr frame)
941 {
942 StaticBlockObject& block = staticBlock();
943 for (unsigned i = 0; i < numVariables(); ++i) {
944 if (!block.isAliased(i)) {
945 Value& val = frame.unaliasedLocal(block.blockIndexToLocalIndex(i));
946 setVar(i, val, DONT_CHECK_ALIASING);
947 }
948 }
949 }
950
951 /* static */ ClonedBlockObject*
clone(JSContext * cx,Handle<ClonedBlockObject * > clonedBlock)952 ClonedBlockObject::clone(JSContext* cx, Handle<ClonedBlockObject*> clonedBlock)
953 {
954 Rooted<StaticBlockObject*> staticBlock(cx, &clonedBlock->staticBlock());
955 MOZ_ASSERT(!staticBlock->isExtensible());
956 RootedObject enclosing(cx, &clonedBlock->enclosingScope());
957
958 Rooted<ClonedBlockObject*> copy(cx, create(cx, staticBlock, enclosing));
959 if (!copy)
960 return nullptr;
961
962 for (uint32_t i = 0, count = staticBlock->numVariables(); i < count; i++)
963 copy->setVar(i, clonedBlock->var(i, DONT_CHECK_ALIASING), DONT_CHECK_ALIASING);
964
965 return copy;
966 }
967
968 StaticBlockObject*
create(ExclusiveContext * cx)969 StaticBlockObject::create(ExclusiveContext* cx)
970 {
971 return NewObjectWithNullTaggedProto<StaticBlockObject>(cx, TenuredObject, BaseShape::DELEGATE);
972 }
973
974 Shape*
lookupAliasedName(PropertyName * name)975 StaticBlockObject::lookupAliasedName(PropertyName* name)
976 {
977 Shape::Range<NoGC> r(lastProperty());
978 while (!r.empty()) {
979 jsid id = r.front().propidRaw();
980 if (JSID_TO_ATOM(id)->asPropertyName() == name && isAliased(shapeToIndex(r.front())))
981 return &r.front();
982 r.popFront();
983 }
984 return nullptr;
985 }
986
987 bool
makeNonExtensible(ExclusiveContext * cx)988 StaticBlockObject::makeNonExtensible(ExclusiveContext* cx)
989 {
990 // Do not do all the work of js::PreventExtensions, as BlockObjects are
991 // known to be NativeObjects, have no lazy properties, and no dense
992 // elements. Indeed, we do not have a JSContext as parsing may happen
993 // off-thread.
994 if (!isExtensible())
995 return true;
996 return setFlags(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE);
997 }
998
999 /* static */ Shape*
addVar(ExclusiveContext * cx,Handle<StaticBlockObject * > block,HandleId id,bool constant,unsigned index,bool * redeclared)1000 StaticBlockObject::addVar(ExclusiveContext* cx, Handle<StaticBlockObject*> block, HandleId id,
1001 bool constant, unsigned index, bool* redeclared)
1002 {
1003 MOZ_ASSERT(JSID_IS_ATOM(id));
1004 MOZ_ASSERT(index < LOCAL_INDEX_LIMIT);
1005
1006 *redeclared = false;
1007
1008 /* Inline NativeObject::addProperty in order to trap the redefinition case. */
1009 ShapeTable::Entry* entry;
1010 if (Shape::search(cx, block->lastProperty(), id, &entry, true)) {
1011 *redeclared = true;
1012 return nullptr;
1013 }
1014
1015 /*
1016 * Don't convert this object to dictionary mode so that we can clone the
1017 * block's shape later.
1018 */
1019 uint32_t slot = JSSLOT_FREE(&BlockObject::class_) + index;
1020 uint32_t readonly = constant ? JSPROP_READONLY : 0;
1021 uint32_t propFlags = readonly | JSPROP_ENUMERATE | JSPROP_PERMANENT;
1022 return NativeObject::addPropertyInternal(cx, block, id,
1023 /* getter = */ nullptr,
1024 /* setter = */ nullptr,
1025 slot,
1026 propFlags,
1027 /* attrs = */ 0,
1028 entry,
1029 /* allowDictionary = */ false);
1030 }
1031
1032 Value
thisValue() const1033 ClonedBlockObject::thisValue() const
1034 {
1035 MOZ_ASSERT(isGlobal() || !isSyntactic());
1036 Value v = getReservedSlot(THIS_VALUE_SLOT);
1037 if (v.isObject()) {
1038 // If `v` is a Window, return the WindowProxy instead. We called
1039 // GetThisValue (which also does ToWindowProxyIfWindow) when storing
1040 // the value in THIS_VALUE_SLOT, but it's possible the WindowProxy was
1041 // attached to the global *after* we set THIS_VALUE_SLOT.
1042 return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
1043 }
1044 return v;
1045 }
1046
1047 const Class BlockObject::class_ = {
1048 "Block",
1049 JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) |
1050 JSCLASS_IS_ANONYMOUS,
1051 nullptr, /* addProperty */
1052 nullptr, /* delProperty */
1053 nullptr, /* getProperty */
1054 nullptr, /* setProperty */
1055 nullptr, /* enumerate */
1056 nullptr, /* resolve */
1057 nullptr, /* mayResolve */
1058 nullptr, /* finalize */
1059 nullptr, /* call */
1060 nullptr, /* hasInstance */
1061 nullptr, /* construct */
1062 nullptr, /* trace */
1063 JS_NULL_CLASS_SPEC,
1064 JS_NULL_CLASS_EXT,
1065 {
1066 nullptr, /* lookupProperty */
1067 nullptr, /* defineProperty */
1068 nullptr, /* hasProperty */
1069 nullptr, /* getProperty */
1070 nullptr, /* setProperty */
1071 nullptr, /* getOwnPropertyDescriptor */
1072 nullptr, /* deleteProperty */
1073 nullptr, nullptr, /* watch/unwatch */
1074 nullptr, /* getElements */
1075 nullptr, /* enumerate (native enumeration of target doesn't work) */
1076 nullptr,
1077 }
1078 };
1079
1080 template<XDRMode mode>
1081 bool
XDRStaticBlockObject(XDRState<mode> * xdr,HandleObject enclosingScope,MutableHandle<StaticBlockObject * > objp)1082 js::XDRStaticBlockObject(XDRState<mode>* xdr, HandleObject enclosingScope,
1083 MutableHandle<StaticBlockObject*> objp)
1084 {
1085 /* NB: Keep this in sync with CloneStaticBlockObject. */
1086
1087 JSContext* cx = xdr->cx();
1088
1089 Rooted<StaticBlockObject*> obj(cx);
1090 uint32_t count = 0, offset = 0;
1091 uint8_t extensible = 0;
1092
1093 if (mode == XDR_ENCODE) {
1094 obj = objp;
1095 count = obj->numVariables();
1096 offset = obj->localOffset();
1097 extensible = obj->isExtensible() ? 1 : 0;
1098 }
1099
1100 if (mode == XDR_DECODE) {
1101 obj = StaticBlockObject::create(cx);
1102 if (!obj)
1103 return false;
1104 obj->initEnclosingScope(enclosingScope);
1105 objp.set(obj);
1106 }
1107
1108 if (!xdr->codeUint32(&count))
1109 return false;
1110 if (!xdr->codeUint32(&offset))
1111 return false;
1112 if (!xdr->codeUint8(&extensible))
1113 return false;
1114
1115 /*
1116 * XDR the block object's properties. We know that there are 'count'
1117 * properties to XDR, stored as id/aliased pairs. (The empty string as
1118 * id indicates an int id.)
1119 */
1120 if (mode == XDR_DECODE) {
1121 obj->setLocalOffset(offset);
1122
1123 for (unsigned i = 0; i < count; i++) {
1124 RootedAtom atom(cx);
1125 if (!XDRAtom(xdr, &atom))
1126 return false;
1127
1128 RootedId id(cx, atom != cx->runtime()->emptyString
1129 ? AtomToId(atom)
1130 : INT_TO_JSID(i));
1131
1132 uint32_t propFlags;
1133 if (!xdr->codeUint32(&propFlags))
1134 return false;
1135
1136 bool readonly = !!(propFlags & 1);
1137
1138 bool redeclared;
1139 if (!StaticBlockObject::addVar(cx, obj, id, readonly, i, &redeclared)) {
1140 MOZ_ASSERT(!redeclared);
1141 return false;
1142 }
1143
1144 bool aliased = !!(propFlags >> 1);
1145 obj->setAliased(i, aliased);
1146 }
1147
1148 if (!extensible) {
1149 if (!obj->makeNonExtensible(cx))
1150 return false;
1151 }
1152 } else {
1153 Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
1154 if (!shapes.growBy(count))
1155 return false;
1156
1157 for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
1158 shapes[obj->shapeToIndex(r.front())].set(&r.front());
1159
1160 RootedShape shape(cx);
1161 RootedId propid(cx);
1162 RootedAtom atom(cx);
1163 for (unsigned i = 0; i < count; i++) {
1164 shape = shapes[i];
1165 MOZ_ASSERT(shape->hasDefaultGetter());
1166 MOZ_ASSERT(obj->shapeToIndex(*shape) == i);
1167
1168 propid = shape->propid();
1169 MOZ_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid));
1170
1171 atom = JSID_IS_ATOM(propid)
1172 ? JSID_TO_ATOM(propid)
1173 : cx->runtime()->emptyString;
1174 if (!XDRAtom(xdr, &atom))
1175 return false;
1176
1177 bool aliased = obj->isAliased(i);
1178 bool readonly = !shape->writable();
1179 uint32_t propFlags = (aliased << 1) | readonly;
1180 if (!xdr->codeUint32(&propFlags))
1181 return false;
1182 }
1183 }
1184 return true;
1185 }
1186
1187 template bool
1188 js::XDRStaticBlockObject(XDRState<XDR_ENCODE>*, HandleObject, MutableHandle<StaticBlockObject*>);
1189
1190 template bool
1191 js::XDRStaticBlockObject(XDRState<XDR_DECODE>*, HandleObject, MutableHandle<StaticBlockObject*>);
1192
1193 static JSObject*
CloneStaticBlockObject(JSContext * cx,HandleObject enclosingScope,Handle<StaticBlockObject * > srcBlock)1194 CloneStaticBlockObject(JSContext* cx, HandleObject enclosingScope, Handle<StaticBlockObject*> srcBlock)
1195 {
1196 /* NB: Keep this in sync with XDRStaticBlockObject. */
1197
1198 Rooted<StaticBlockObject*> clone(cx, StaticBlockObject::create(cx));
1199 if (!clone)
1200 return nullptr;
1201
1202 clone->initEnclosingScope(enclosingScope);
1203 clone->setLocalOffset(srcBlock->localOffset());
1204
1205 /* Shape::Range is reverse order, so build a list in forward order. */
1206 Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
1207 if (!shapes.growBy(srcBlock->numVariables()))
1208 return nullptr;
1209
1210 for (Shape::Range<NoGC> r(srcBlock->lastProperty()); !r.empty(); r.popFront())
1211 shapes[srcBlock->shapeToIndex(r.front())].set(&r.front());
1212
1213 RootedId id(cx);
1214 for (Shape* shape : shapes) {
1215 id = shape->propid();
1216 unsigned i = srcBlock->shapeToIndex(*shape);
1217
1218 bool redeclared;
1219 if (!StaticBlockObject::addVar(cx, clone, id, !shape->writable(), i, &redeclared)) {
1220 MOZ_ASSERT(!redeclared);
1221 return nullptr;
1222 }
1223
1224 clone->setAliased(i, srcBlock->isAliased(i));
1225 }
1226
1227 if (!srcBlock->isExtensible()) {
1228 if (!clone->makeNonExtensible(cx))
1229 return nullptr;
1230 }
1231
1232 return clone;
1233 }
1234
1235 JSObject*
CloneNestedScopeObject(JSContext * cx,HandleObject enclosingScope,Handle<NestedScopeObject * > srcBlock)1236 js::CloneNestedScopeObject(JSContext* cx, HandleObject enclosingScope, Handle<NestedScopeObject*> srcBlock)
1237 {
1238 if (srcBlock->is<StaticBlockObject>()) {
1239 Rooted<StaticBlockObject*> blockObj(cx, &srcBlock->as<StaticBlockObject>());
1240 return CloneStaticBlockObject(cx, enclosingScope, blockObj);
1241 } else {
1242 Rooted<StaticWithObject*> withObj(cx, &srcBlock->as<StaticWithObject>());
1243 return CloneStaticWithObject(cx, enclosingScope, withObj);
1244 }
1245 }
1246
1247 /* static */ RuntimeLexicalErrorObject*
create(JSContext * cx,HandleObject enclosing,unsigned errorNumber)1248 RuntimeLexicalErrorObject::create(JSContext* cx, HandleObject enclosing, unsigned errorNumber)
1249 {
1250 RuntimeLexicalErrorObject* obj =
1251 NewObjectWithNullTaggedProto<RuntimeLexicalErrorObject>(cx, GenericObject,
1252 BaseShape::DELEGATE);
1253 if (!obj)
1254 return nullptr;
1255 obj->setEnclosingScope(enclosing);
1256 obj->setReservedSlot(ERROR_SLOT, Int32Value(int32_t(errorNumber)));
1257 return obj;
1258 }
1259
1260 static void
ReportRuntimeLexicalErrorId(JSContext * cx,unsigned errorNumber,HandleId id)1261 ReportRuntimeLexicalErrorId(JSContext* cx, unsigned errorNumber, HandleId id)
1262 {
1263 if (JSID_IS_ATOM(id)) {
1264 RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
1265 ReportRuntimeLexicalError(cx, errorNumber, name);
1266 return;
1267 }
1268 MOZ_CRASH("RuntimeLexicalErrorObject should only be used with property names");
1269 }
1270
1271 static bool
lexicalError_LookupProperty(JSContext * cx,HandleObject obj,HandleId id,MutableHandleObject objp,MutableHandleShape propp)1272 lexicalError_LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
1273 MutableHandleObject objp, MutableHandleShape propp)
1274 {
1275 ReportRuntimeLexicalErrorId(cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
1276 return false;
1277 }
1278
1279 static bool
lexicalError_HasProperty(JSContext * cx,HandleObject obj,HandleId id,bool * foundp)1280 lexicalError_HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
1281 {
1282 ReportRuntimeLexicalErrorId(cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
1283 return false;
1284 }
1285
1286 static bool
lexicalError_GetProperty(JSContext * cx,HandleObject obj,HandleValue receiver,HandleId id,MutableHandleValue vp)1287 lexicalError_GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
1288 MutableHandleValue vp)
1289 {
1290 ReportRuntimeLexicalErrorId(cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
1291 return false;
1292 }
1293
1294 static bool
lexicalError_SetProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result)1295 lexicalError_SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
1296 HandleValue receiver, ObjectOpResult& result)
1297 {
1298 ReportRuntimeLexicalErrorId(cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
1299 return false;
1300 }
1301
1302 static bool
lexicalError_GetOwnPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<JSPropertyDescriptor> desc)1303 lexicalError_GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
1304 MutableHandle<JSPropertyDescriptor> desc)
1305 {
1306 ReportRuntimeLexicalErrorId(cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
1307 return false;
1308 }
1309
1310 static bool
lexicalError_DeleteProperty(JSContext * cx,HandleObject obj,HandleId id,ObjectOpResult & result)1311 lexicalError_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
1312 {
1313 ReportRuntimeLexicalErrorId(cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id);
1314 return false;
1315 }
1316
1317 const Class RuntimeLexicalErrorObject::class_ = {
1318 "RuntimeLexicalError",
1319 JSCLASS_HAS_RESERVED_SLOTS(RuntimeLexicalErrorObject::RESERVED_SLOTS) |
1320 JSCLASS_IS_ANONYMOUS,
1321 nullptr, /* addProperty */
1322 nullptr, /* delProperty */
1323 nullptr, /* getProperty */
1324 nullptr, /* setProperty */
1325 nullptr, /* enumerate */
1326 nullptr, /* resolve */
1327 nullptr, /* mayResolve */
1328 nullptr, /* finalize */
1329 nullptr, /* call */
1330 nullptr, /* hasInstance */
1331 nullptr, /* construct */
1332 nullptr, /* trace */
1333 JS_NULL_CLASS_SPEC,
1334 JS_NULL_CLASS_EXT,
1335 {
1336 lexicalError_LookupProperty,
1337 nullptr, /* defineProperty */
1338 lexicalError_HasProperty,
1339 lexicalError_GetProperty,
1340 lexicalError_SetProperty,
1341 lexicalError_GetOwnPropertyDescriptor,
1342 lexicalError_DeleteProperty,
1343 nullptr, nullptr, /* watch/unwatch */
1344 nullptr, /* getElements */
1345 nullptr, /* enumerate (native enumeration of target doesn't work) */
1346 nullptr, /* this */
1347 }
1348 };
1349
1350 /*****************************************************************************/
1351
1352 // Any name atom for a function which will be added as a DeclEnv object to the
1353 // scope chain above call objects for fun.
1354 static inline JSAtom*
CallObjectLambdaName(JSFunction & fun)1355 CallObjectLambdaName(JSFunction& fun)
1356 {
1357 return fun.isNamedLambda() ? fun.atom() : nullptr;
1358 }
1359
ScopeIter(JSContext * cx,const ScopeIter & si MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)1360 ScopeIter::ScopeIter(JSContext* cx, const ScopeIter& si
1361 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
1362 : ssi_(cx, si.ssi_),
1363 scope_(cx, si.scope_),
1364 frame_(si.frame_)
1365 {
1366 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1367 }
1368
ScopeIter(JSContext * cx,JSObject * scope,JSObject * staticScope MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)1369 ScopeIter::ScopeIter(JSContext* cx, JSObject* scope, JSObject* staticScope
1370 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
1371 : ssi_(cx, staticScope),
1372 scope_(cx, scope),
1373 frame_(NullFramePtr())
1374 {
1375 settle();
1376 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1377 }
1378
ScopeIter(JSContext * cx,AbstractFramePtr frame,jsbytecode * pc MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)1379 ScopeIter::ScopeIter(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc
1380 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
1381 : ssi_(cx, frame.script()->innermostStaticScope(pc)),
1382 scope_(cx, frame.scopeChain()),
1383 frame_(frame)
1384 {
1385 assertSameCompartment(cx, frame);
1386 settle();
1387 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1388 }
1389
1390 void
incrementStaticScopeIter()1391 ScopeIter::incrementStaticScopeIter()
1392 {
1393 // If settled on a non-syntactic static scope, only increment ssi_ once
1394 // we've iterated through all the non-syntactic dynamic ScopeObjects.
1395 if (ssi_.type() == StaticScopeIter<CanGC>::NonSyntactic) {
1396 if (!hasNonSyntacticScopeObject())
1397 ssi_++;
1398 } else {
1399 ssi_++;
1400 }
1401
1402 // For named lambdas, DeclEnvObject scopes are always attached to their
1403 // CallObjects. Skip it here, as they are special cased in users of
1404 // ScopeIter.
1405 if (!ssi_.done() && ssi_.type() == StaticScopeIter<CanGC>::NamedLambda)
1406 ssi_++;
1407 }
1408
1409 void
settle()1410 ScopeIter::settle()
1411 {
1412 // Check for trying to iterate a function frame before the prologue has
1413 // created the CallObject, in which case we have to skip.
1414 if (frame_ && frame_.isNonEvalFunctionFrame() && frame_.fun()->needsCallObject() &&
1415 !frame_.hasCallObj())
1416 {
1417 MOZ_ASSERT(ssi_.type() == StaticScopeIter<CanGC>::Function);
1418 incrementStaticScopeIter();
1419 }
1420
1421 // Check for trying to iterate a strict eval frame before the prologue has
1422 // created the CallObject.
1423 if (frame_ && frame_.isStrictEvalFrame() && !frame_.hasCallObj() && !ssi_.done()) {
1424 MOZ_ASSERT(ssi_.type() == StaticScopeIter<CanGC>::Block);
1425 incrementStaticScopeIter();
1426 MOZ_ASSERT(ssi_.type() == StaticScopeIter<CanGC>::Eval);
1427 MOZ_ASSERT(maybeStaticScope() == frame_.script()->enclosingStaticScope());
1428 incrementStaticScopeIter();
1429 frame_ = NullFramePtr();
1430 }
1431
1432 // Check if we have left the extent of the initial frame after we've
1433 // settled on a static scope.
1434 if (frame_ && (ssi_.done() || maybeStaticScope() == frame_.script()->enclosingStaticScope()))
1435 frame_ = NullFramePtr();
1436
1437 #ifdef DEBUG
1438 if (!ssi_.done() && hasAnyScopeObject()) {
1439 switch (ssi_.type()) {
1440 case StaticScopeIter<CanGC>::Module:
1441 MOZ_ASSERT(scope_->as<ModuleEnvironmentObject>().module() == ssi_.module());
1442 break;
1443 case StaticScopeIter<CanGC>::Function:
1444 MOZ_ASSERT(scope_->as<CallObject>().callee().nonLazyScript() == ssi_.funScript());
1445 break;
1446 case StaticScopeIter<CanGC>::Block:
1447 MOZ_ASSERT(scope_->as<ClonedBlockObject>().staticBlock() == staticBlock());
1448 break;
1449 case StaticScopeIter<CanGC>::With:
1450 MOZ_ASSERT(scope_->as<DynamicWithObject>().staticScope() == &staticWith());
1451 break;
1452 case StaticScopeIter<CanGC>::Eval:
1453 MOZ_ASSERT(scope_->as<CallObject>().isForEval());
1454 break;
1455 case StaticScopeIter<CanGC>::NonSyntactic:
1456 MOZ_ASSERT(!IsSyntacticScope(scope_));
1457 break;
1458 case StaticScopeIter<CanGC>::NamedLambda:
1459 MOZ_CRASH("named lambda static scopes should have been skipped");
1460 }
1461 }
1462 #endif
1463 }
1464
1465 ScopeIter&
operator ++()1466 ScopeIter::operator++()
1467 {
1468 if (hasAnyScopeObject()) {
1469 scope_ = &scope_->as<ScopeObject>().enclosingScope();
1470 if (scope_->is<DeclEnvObject>())
1471 scope_ = &scope_->as<DeclEnvObject>().enclosingScope();
1472 }
1473
1474 incrementStaticScopeIter();
1475 settle();
1476
1477 return *this;
1478 }
1479
1480 ScopeIter::Type
type() const1481 ScopeIter::type() const
1482 {
1483 MOZ_ASSERT(!done());
1484
1485 switch (ssi_.type()) {
1486 case StaticScopeIter<CanGC>::Module:
1487 return Module;
1488 case StaticScopeIter<CanGC>::Function:
1489 return Call;
1490 case StaticScopeIter<CanGC>::Block:
1491 return Block;
1492 case StaticScopeIter<CanGC>::With:
1493 return With;
1494 case StaticScopeIter<CanGC>::Eval:
1495 return Eval;
1496 case StaticScopeIter<CanGC>::NonSyntactic:
1497 return NonSyntactic;
1498 case StaticScopeIter<CanGC>::NamedLambda:
1499 MOZ_CRASH("named lambda static scopes should have been skipped");
1500 default:
1501 MOZ_CRASH("bad SSI type");
1502 }
1503 }
1504
1505 ScopeObject&
scope() const1506 ScopeIter::scope() const
1507 {
1508 MOZ_ASSERT(hasAnyScopeObject());
1509 return scope_->as<ScopeObject>();
1510 }
1511
1512 JSObject*
maybeStaticScope() const1513 ScopeIter::maybeStaticScope() const
1514 {
1515 if (ssi_.done())
1516 return nullptr;
1517
1518 switch (ssi_.type()) {
1519 case StaticScopeIter<CanGC>::Function:
1520 return &fun();
1521 case StaticScopeIter<CanGC>::Module:
1522 return &module();
1523 case StaticScopeIter<CanGC>::Block:
1524 return &staticBlock();
1525 case StaticScopeIter<CanGC>::With:
1526 return &staticWith();
1527 case StaticScopeIter<CanGC>::Eval:
1528 return &staticEval();
1529 case StaticScopeIter<CanGC>::NonSyntactic:
1530 return &staticNonSyntactic();
1531 case StaticScopeIter<CanGC>::NamedLambda:
1532 MOZ_CRASH("named lambda static scopes should have been skipped");
1533 default:
1534 MOZ_CRASH("bad SSI type");
1535 }
1536 }
1537
1538 /* static */ HashNumber
hash(MissingScopeKey sk)1539 MissingScopeKey::hash(MissingScopeKey sk)
1540 {
1541 return size_t(sk.frame_.raw()) ^ size_t(sk.staticScope_);
1542 }
1543
1544 /* static */ bool
match(MissingScopeKey sk1,MissingScopeKey sk2)1545 MissingScopeKey::match(MissingScopeKey sk1, MissingScopeKey sk2)
1546 {
1547 return sk1.frame_ == sk2.frame_ && sk1.staticScope_ == sk2.staticScope_;
1548 }
1549
1550 bool
needsSweep()1551 LiveScopeVal::needsSweep()
1552 {
1553 if (staticScope_)
1554 MOZ_ALWAYS_FALSE(IsAboutToBeFinalized(&staticScope_));
1555 return false;
1556 }
1557
1558 // Live ScopeIter values may be added to DebugScopes::liveScopes, as
1559 // LiveScopeVal instances. They need to have write barriers when they are added
1560 // to the hash table, but no barriers when rehashing inside GC. It's a nasty
1561 // hack, but the important thing is that LiveScopeVal and MissingScopeKey need to
1562 // alias each other.
1563 void
staticAsserts()1564 LiveScopeVal::staticAsserts()
1565 {
1566 static_assert(sizeof(LiveScopeVal) == sizeof(MissingScopeKey),
1567 "LiveScopeVal must be same size of MissingScopeKey");
1568 static_assert(offsetof(LiveScopeVal, staticScope_) == offsetof(MissingScopeKey, staticScope_),
1569 "LiveScopeVal.staticScope_ must alias MissingScopeKey.staticScope_");
1570 }
1571
1572 /*****************************************************************************/
1573
1574 namespace {
1575
1576 /*
1577 * DebugScopeProxy is the handler for DebugScopeObject proxy objects. Having a
1578 * custom handler (rather than trying to reuse js::Wrapper) gives us several
1579 * important abilities:
1580 * - We want to pass the ScopeObject as the receiver to forwarded scope
1581 * property ops on aliased variables so that Call/Block/With ops do not all
1582 * require a 'normalization' step.
1583 * - The debug scope proxy can directly manipulate the stack frame to allow
1584 * the debugger to read/write args/locals that were otherwise unaliased.
1585 * - The debug scope proxy can store unaliased variables after the stack frame
1586 * is popped so that they may still be read/written by the debugger.
1587 * - The engine has made certain assumptions about the possible reads/writes
1588 * in a scope. DebugScopeProxy allows us to prevent the debugger from
1589 * breaking those assumptions.
1590 * - The engine makes optimizations that are observable to the debugger. The
1591 * proxy can either hide these optimizations or make the situation more
1592 * clear to the debugger. An example is 'arguments'.
1593 */
1594 class DebugScopeProxy : public BaseProxyHandler
1595 {
1596 enum Action { SET, GET };
1597
1598 enum AccessResult {
1599 ACCESS_UNALIASED,
1600 ACCESS_GENERIC,
1601 ACCESS_LOST
1602 };
1603
1604 /*
1605 * This function handles access to unaliased locals/formals. Since they are
1606 * unaliased, the values of these variables are not stored in the slots of
1607 * the normal Call/BlockObject scope objects and thus must be recovered
1608 * from somewhere else:
1609 * + if the invocation for which the scope was created is still executing,
1610 * there is a JS frame live on the stack holding the values;
1611 * + if the invocation for which the scope was created finished executing:
1612 * - and there was a DebugScopeObject associated with scope, then the
1613 * DebugScopes::onPop(Call|Block) handler copied out the unaliased
1614 * variables:
1615 * . for block scopes, the unaliased values were copied directly
1616 * into the block object, since there is a slot allocated for every
1617 * block binding, regardless of whether it is aliased;
1618 * . for function scopes, a dense array is created in onPopCall to hold
1619 * the unaliased values and attached to the DebugScopeObject;
1620 * - and there was not a DebugScopeObject yet associated with the
1621 * scope, then the unaliased values are lost and not recoverable.
1622 *
1623 * Callers should check accessResult for non-failure results:
1624 * - ACCESS_UNALIASED if the access was unaliased and completed
1625 * - ACCESS_GENERIC if the access was aliased or the property not found
1626 * - ACCESS_LOST if the value has been lost to the debugger
1627 */
handleUnaliasedAccess(JSContext * cx,Handle<DebugScopeObject * > debugScope,Handle<ScopeObject * > scope,HandleId id,Action action,MutableHandleValue vp,AccessResult * accessResult) const1628 bool handleUnaliasedAccess(JSContext* cx, Handle<DebugScopeObject*> debugScope,
1629 Handle<ScopeObject*> scope, HandleId id, Action action,
1630 MutableHandleValue vp, AccessResult* accessResult) const
1631 {
1632 MOZ_ASSERT(&debugScope->scope() == scope);
1633 MOZ_ASSERT_IF(action == SET, !debugScope->isOptimizedOut());
1634 *accessResult = ACCESS_GENERIC;
1635 LiveScopeVal* maybeLiveScope = DebugScopes::hasLiveScope(*scope);
1636
1637 if (scope->is<ModuleEnvironmentObject>()) {
1638 /* Everything is aliased and stored in the environment object. */
1639 return true;
1640 }
1641
1642 /* Handle unaliased formals, vars, lets, and consts at function scope. */
1643 if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
1644 CallObject& callobj = scope->as<CallObject>();
1645 RootedScript script(cx, callobj.callee().getOrCreateScript(cx));
1646 if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
1647 return false;
1648
1649 Bindings& bindings = script->bindings;
1650 BindingIter bi(script);
1651 while (bi && NameToId(bi->name()) != id)
1652 bi++;
1653 if (!bi)
1654 return true;
1655
1656 if (bi->kind() == Binding::VARIABLE || bi->kind() == Binding::CONSTANT) {
1657 if (script->bindingIsAliased(bi))
1658 return true;
1659
1660 uint32_t i = bi.frameIndex();
1661 if (maybeLiveScope) {
1662 AbstractFramePtr frame = maybeLiveScope->frame();
1663 if (action == GET)
1664 vp.set(frame.unaliasedLocal(i));
1665 else
1666 frame.unaliasedLocal(i) = vp;
1667 } else if (NativeObject* snapshot = debugScope->maybeSnapshot()) {
1668 if (action == GET)
1669 vp.set(snapshot->getDenseElement(bindings.numArgs() + i));
1670 else
1671 snapshot->setDenseElement(bindings.numArgs() + i, vp);
1672 } else {
1673 /* The unaliased value has been lost to the debugger. */
1674 if (action == GET) {
1675 *accessResult = ACCESS_LOST;
1676 return true;
1677 }
1678 }
1679 } else {
1680 MOZ_ASSERT(bi->kind() == Binding::ARGUMENT);
1681 unsigned i = bi.argIndex();
1682 if (script->formalIsAliased(i))
1683 return true;
1684
1685 if (maybeLiveScope) {
1686 AbstractFramePtr frame = maybeLiveScope->frame();
1687 if (script->argsObjAliasesFormals() && frame.hasArgsObj()) {
1688 if (action == GET)
1689 vp.set(frame.argsObj().arg(i));
1690 else
1691 frame.argsObj().setArg(i, vp);
1692 } else {
1693 if (action == GET)
1694 vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING));
1695 else
1696 frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp;
1697 }
1698 } else if (NativeObject* snapshot = debugScope->maybeSnapshot()) {
1699 if (action == GET)
1700 vp.set(snapshot->getDenseElement(i));
1701 else
1702 snapshot->setDenseElement(i, vp);
1703 } else {
1704 /* The unaliased value has been lost to the debugger. */
1705 if (action == GET) {
1706 *accessResult = ACCESS_LOST;
1707 return true;
1708 }
1709 }
1710
1711 if (action == SET)
1712 TypeScript::SetArgument(cx, script, i, vp);
1713 }
1714
1715 *accessResult = ACCESS_UNALIASED;
1716 return true;
1717 }
1718
1719 /* Handle unaliased let and catch bindings at block scope. */
1720 if (scope->is<ClonedBlockObject>()) {
1721 Rooted<ClonedBlockObject*> block(cx, &scope->as<ClonedBlockObject>());
1722 Shape* shape = block->lastProperty()->search(cx, id);
1723 if (!shape)
1724 return true;
1725
1726 // Currently consider all global and non-syntactic top-level lexical
1727 // bindings to be aliased.
1728 if (block->isExtensible()) {
1729 MOZ_ASSERT(IsGlobalLexicalScope(block) || !IsSyntacticScope(block));
1730 return true;
1731 }
1732
1733 unsigned i = block->staticBlock().shapeToIndex(*shape);
1734 if (block->staticBlock().isAliased(i))
1735 return true;
1736
1737 if (maybeLiveScope) {
1738 AbstractFramePtr frame = maybeLiveScope->frame();
1739 uint32_t local = block->staticBlock().blockIndexToLocalIndex(i);
1740 MOZ_ASSERT(local < frame.script()->nfixed());
1741 if (action == GET)
1742 vp.set(frame.unaliasedLocal(local));
1743 else
1744 frame.unaliasedLocal(local) = vp;
1745 } else {
1746 if (action == GET) {
1747 // A ClonedBlockObject whose static block does not need
1748 // cloning is a "hollow" block object reflected for
1749 // missing block scopes. Their slot values are lost.
1750 if (!block->staticBlock().needsClone()) {
1751 *accessResult = ACCESS_LOST;
1752 return true;
1753 }
1754 vp.set(block->var(i, DONT_CHECK_ALIASING));
1755 } else {
1756 block->setVar(i, vp, DONT_CHECK_ALIASING);
1757 }
1758 }
1759
1760 *accessResult = ACCESS_UNALIASED;
1761 return true;
1762 }
1763
1764 /* The rest of the internal scopes do not have unaliased vars. */
1765 MOZ_ASSERT(scope->is<DeclEnvObject>() || scope->is<DynamicWithObject>() ||
1766 scope->as<CallObject>().isForEval());
1767 return true;
1768 }
1769
isArguments(JSContext * cx,jsid id)1770 static bool isArguments(JSContext* cx, jsid id)
1771 {
1772 return id == NameToId(cx->names().arguments);
1773 }
isThis(JSContext * cx,jsid id)1774 static bool isThis(JSContext* cx, jsid id)
1775 {
1776 return id == NameToId(cx->names().dotThis);
1777 }
1778
isFunctionScope(const JSObject & scope)1779 static bool isFunctionScope(const JSObject& scope)
1780 {
1781 return scope.is<CallObject>() && !scope.as<CallObject>().isForEval();
1782 }
1783
1784 /*
1785 * In theory, every function scope contains an 'arguments' bindings.
1786 * However, the engine only adds a binding if 'arguments' is used in the
1787 * function body. Thus, from the debugger's perspective, 'arguments' may be
1788 * missing from the list of bindings.
1789 */
isMissingArgumentsBinding(ScopeObject & scope)1790 static bool isMissingArgumentsBinding(ScopeObject& scope)
1791 {
1792 return isFunctionScope(scope) &&
1793 !scope.as<CallObject>().callee().nonLazyScript()->argumentsHasVarBinding();
1794 }
1795
1796 /*
1797 * Similar to 'arguments' above, we don't add a 'this' binding to functions
1798 * if it's not used.
1799 */
isMissingThisBinding(ScopeObject & scope)1800 static bool isMissingThisBinding(ScopeObject& scope)
1801 {
1802 return isFunctionScopeWithThis(scope) &&
1803 !scope.as<CallObject>().callee().nonLazyScript()->functionHasThisBinding();
1804 }
1805
1806 /*
1807 * This function checks if an arguments object needs to be created when
1808 * the debugger requests 'arguments' for a function scope where the
1809 * arguments object has been optimized away (either because the binding is
1810 * missing altogether or because !ScriptAnalysis::needsArgsObj).
1811 */
isMissingArguments(JSContext * cx,jsid id,ScopeObject & scope)1812 static bool isMissingArguments(JSContext* cx, jsid id, ScopeObject& scope)
1813 {
1814 return isArguments(cx, id) && isFunctionScope(scope) &&
1815 !scope.as<CallObject>().callee().nonLazyScript()->needsArgsObj();
1816 }
isMissingThis(JSContext * cx,jsid id,ScopeObject & scope)1817 static bool isMissingThis(JSContext* cx, jsid id, ScopeObject& scope)
1818 {
1819 return isThis(cx, id) && isMissingThisBinding(scope);
1820 }
1821
1822 /*
1823 * Check if the value is the magic value JS_OPTIMIZED_ARGUMENTS. The
1824 * arguments analysis may have optimized out the 'arguments', and this
1825 * magic value could have propagated to other local slots. e.g.,
1826 *
1827 * function f() { var a = arguments; h(); }
1828 * function h() { evalInFrame(1, "a.push(0)"); }
1829 *
1830 * where evalInFrame(N, str) means to evaluate str N frames up.
1831 *
1832 * In this case we don't know we need to recover a missing arguments
1833 * object until after we've performed the property get.
1834 */
isMagicMissingArgumentsValue(JSContext * cx,ScopeObject & scope,HandleValue v)1835 static bool isMagicMissingArgumentsValue(JSContext* cx, ScopeObject& scope, HandleValue v)
1836 {
1837 bool isMagic = v.isMagic() && v.whyMagic() == JS_OPTIMIZED_ARGUMENTS;
1838 MOZ_ASSERT_IF(isMagic,
1839 isFunctionScope(scope) &&
1840 scope.as<CallObject>().callee().nonLazyScript()->argumentsHasVarBinding());
1841 return isMagic;
1842 }
1843
1844 /*
1845 * Create a missing arguments object. If the function returns true but
1846 * argsObj is null, it means the scope is dead.
1847 */
createMissingArguments(JSContext * cx,ScopeObject & scope,MutableHandleArgumentsObject argsObj)1848 static bool createMissingArguments(JSContext* cx, ScopeObject& scope,
1849 MutableHandleArgumentsObject argsObj)
1850 {
1851 argsObj.set(nullptr);
1852
1853 LiveScopeVal* maybeScope = DebugScopes::hasLiveScope(scope);
1854 if (!maybeScope)
1855 return true;
1856
1857 argsObj.set(ArgumentsObject::createUnexpected(cx, maybeScope->frame()));
1858 return !!argsObj;
1859 }
1860
1861 /*
1862 * Create a missing this Value. If the function returns true but
1863 * *success is false, it means the scope is dead.
1864 */
createMissingThis(JSContext * cx,ScopeObject & scope,MutableHandleValue thisv,bool * success)1865 static bool createMissingThis(JSContext* cx, ScopeObject& scope,
1866 MutableHandleValue thisv, bool* success)
1867 {
1868 *success = false;
1869
1870 LiveScopeVal* maybeScope = DebugScopes::hasLiveScope(scope);
1871 if (!maybeScope)
1872 return true;
1873
1874 if (!GetFunctionThis(cx, maybeScope->frame(), thisv))
1875 return false;
1876
1877 *success = true;
1878 return true;
1879 }
1880
1881 public:
1882 static const char family;
1883 static const DebugScopeProxy singleton;
1884
DebugScopeProxy()1885 MOZ_CONSTEXPR DebugScopeProxy() : BaseProxyHandler(&family) {}
1886
isFunctionScopeWithThis(const JSObject & scope)1887 static bool isFunctionScopeWithThis(const JSObject& scope)
1888 {
1889 // All functions except arrows and generator expression lambdas should
1890 // have their own this binding.
1891 return isFunctionScope(scope) && !scope.as<CallObject>().callee().hasLexicalThis();
1892 }
1893
preventExtensions(JSContext * cx,HandleObject proxy,ObjectOpResult & result) const1894 bool preventExtensions(JSContext* cx, HandleObject proxy,
1895 ObjectOpResult& result) const override
1896 {
1897 // always [[Extensible]], can't be made non-[[Extensible]], like most
1898 // proxies
1899 return result.fail(JSMSG_CANT_CHANGE_EXTENSIBILITY);
1900 }
1901
isExtensible(JSContext * cx,HandleObject proxy,bool * extensible) const1902 bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override
1903 {
1904 // See above.
1905 *extensible = true;
1906 return true;
1907 }
1908
getPropertyDescriptor(JSContext * cx,HandleObject proxy,HandleId id,MutableHandle<PropertyDescriptor> desc) const1909 bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
1910 MutableHandle<PropertyDescriptor> desc) const override
1911 {
1912 return getOwnPropertyDescriptor(cx, proxy, id, desc);
1913 }
1914
getMissingArgumentsPropertyDescriptor(JSContext * cx,Handle<DebugScopeObject * > debugScope,ScopeObject & scope,MutableHandle<PropertyDescriptor> desc) const1915 bool getMissingArgumentsPropertyDescriptor(JSContext* cx,
1916 Handle<DebugScopeObject*> debugScope,
1917 ScopeObject& scope,
1918 MutableHandle<PropertyDescriptor> desc) const
1919 {
1920 RootedArgumentsObject argsObj(cx);
1921 if (!createMissingArguments(cx, scope, &argsObj))
1922 return false;
1923
1924 if (!argsObj) {
1925 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
1926 "Debugger scope");
1927 return false;
1928 }
1929
1930 desc.object().set(debugScope);
1931 desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
1932 desc.value().setObject(*argsObj);
1933 desc.setGetter(nullptr);
1934 desc.setSetter(nullptr);
1935 return true;
1936 }
getMissingThisPropertyDescriptor(JSContext * cx,Handle<DebugScopeObject * > debugScope,ScopeObject & scope,MutableHandle<PropertyDescriptor> desc) const1937 bool getMissingThisPropertyDescriptor(JSContext* cx,
1938 Handle<DebugScopeObject*> debugScope,
1939 ScopeObject& scope,
1940 MutableHandle<PropertyDescriptor> desc) const
1941 {
1942 RootedValue thisv(cx);
1943 bool success;
1944 if (!createMissingThis(cx, scope, &thisv, &success))
1945 return false;
1946
1947 if (!success) {
1948 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
1949 "Debugger scope");
1950 return false;
1951 }
1952
1953 desc.object().set(debugScope);
1954 desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
1955 desc.value().set(thisv);
1956 desc.setGetter(nullptr);
1957 desc.setSetter(nullptr);
1958 return true;
1959 }
1960
getOwnPropertyDescriptor(JSContext * cx,HandleObject proxy,HandleId id,MutableHandle<PropertyDescriptor> desc) const1961 bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
1962 MutableHandle<PropertyDescriptor> desc) const override
1963 {
1964 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
1965 Rooted<ScopeObject*> scope(cx, &debugScope->scope());
1966
1967 if (isMissingArguments(cx, id, *scope))
1968 return getMissingArgumentsPropertyDescriptor(cx, debugScope, *scope, desc);
1969
1970 if (isMissingThis(cx, id, *scope))
1971 return getMissingThisPropertyDescriptor(cx, debugScope, *scope, desc);
1972
1973 RootedValue v(cx);
1974 AccessResult access;
1975 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, &v, &access))
1976 return false;
1977
1978 switch (access) {
1979 case ACCESS_UNALIASED:
1980 if (isMagicMissingArgumentsValue(cx, *scope, v))
1981 return getMissingArgumentsPropertyDescriptor(cx, debugScope, *scope, desc);
1982 desc.object().set(debugScope);
1983 desc.setAttributes(JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);
1984 desc.value().set(v);
1985 desc.setGetter(nullptr);
1986 desc.setSetter(nullptr);
1987 return true;
1988 case ACCESS_GENERIC:
1989 return JS_GetOwnPropertyDescriptorById(cx, scope, id, desc);
1990 case ACCESS_LOST:
1991 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT);
1992 return false;
1993 default:
1994 MOZ_CRASH("bad AccessResult");
1995 }
1996 }
1997
getMissingArguments(JSContext * cx,ScopeObject & scope,MutableHandleValue vp) const1998 bool getMissingArguments(JSContext* cx, ScopeObject& scope, MutableHandleValue vp) const
1999 {
2000 RootedArgumentsObject argsObj(cx);
2001 if (!createMissingArguments(cx, scope, &argsObj))
2002 return false;
2003
2004 if (!argsObj) {
2005 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
2006 "Debugger scope");
2007 return false;
2008 }
2009
2010 vp.setObject(*argsObj);
2011 return true;
2012 }
2013
getMissingThis(JSContext * cx,ScopeObject & scope,MutableHandleValue vp) const2014 bool getMissingThis(JSContext* cx, ScopeObject& scope, MutableHandleValue vp) const
2015 {
2016 RootedValue thisv(cx);
2017 bool success;
2018 if (!createMissingThis(cx, scope, &thisv, &success))
2019 return false;
2020
2021 if (!success) {
2022 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
2023 "Debugger scope");
2024 return false;
2025 }
2026
2027 vp.set(thisv);
2028 return true;
2029 }
2030
get(JSContext * cx,HandleObject proxy,HandleValue receiver,HandleId id,MutableHandleValue vp) const2031 bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
2032 MutableHandleValue vp) const override
2033 {
2034 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
2035 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
2036
2037 if (isMissingArguments(cx, id, *scope))
2038 return getMissingArguments(cx, *scope, vp);
2039
2040 if (isMissingThis(cx, id, *scope))
2041 return getMissingThis(cx, *scope, vp);
2042
2043 AccessResult access;
2044 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access))
2045 return false;
2046
2047 switch (access) {
2048 case ACCESS_UNALIASED:
2049 if (isMagicMissingArgumentsValue(cx, *scope, vp))
2050 return getMissingArguments(cx, *scope, vp);
2051 return true;
2052 case ACCESS_GENERIC:
2053 return GetProperty(cx, scope, scope, id, vp);
2054 case ACCESS_LOST:
2055 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT);
2056 return false;
2057 default:
2058 MOZ_CRASH("bad AccessResult");
2059 }
2060 }
2061
getMissingArgumentsMaybeSentinelValue(JSContext * cx,ScopeObject & scope,MutableHandleValue vp) const2062 bool getMissingArgumentsMaybeSentinelValue(JSContext* cx, ScopeObject& scope,
2063 MutableHandleValue vp) const
2064 {
2065 RootedArgumentsObject argsObj(cx);
2066 if (!createMissingArguments(cx, scope, &argsObj))
2067 return false;
2068 vp.set(argsObj ? ObjectValue(*argsObj) : MagicValue(JS_OPTIMIZED_ARGUMENTS));
2069 return true;
2070 }
2071
getMissingThisMaybeSentinelValue(JSContext * cx,ScopeObject & scope,MutableHandleValue vp) const2072 bool getMissingThisMaybeSentinelValue(JSContext* cx, ScopeObject& scope,
2073 MutableHandleValue vp) const
2074 {
2075 RootedValue thisv(cx);
2076 bool success;
2077 if (!createMissingThis(cx, scope, &thisv, &success))
2078 return false;
2079 vp.set(success ? thisv : MagicValue(JS_OPTIMIZED_OUT));
2080 return true;
2081 }
2082
2083 /*
2084 * Like 'get', but returns sentinel values instead of throwing on
2085 * exceptional cases.
2086 */
getMaybeSentinelValue(JSContext * cx,Handle<DebugScopeObject * > debugScope,HandleId id,MutableHandleValue vp) const2087 bool getMaybeSentinelValue(JSContext* cx, Handle<DebugScopeObject*> debugScope, HandleId id,
2088 MutableHandleValue vp) const
2089 {
2090 Rooted<ScopeObject*> scope(cx, &debugScope->scope());
2091
2092 if (isMissingArguments(cx, id, *scope))
2093 return getMissingArgumentsMaybeSentinelValue(cx, *scope, vp);
2094 if (isMissingThis(cx, id, *scope))
2095 return getMissingThisMaybeSentinelValue(cx, *scope, vp);
2096
2097 AccessResult access;
2098 if (!handleUnaliasedAccess(cx, debugScope, scope, id, GET, vp, &access))
2099 return false;
2100
2101 switch (access) {
2102 case ACCESS_UNALIASED:
2103 if (isMagicMissingArgumentsValue(cx, *scope, vp))
2104 return getMissingArgumentsMaybeSentinelValue(cx, *scope, vp);
2105 return true;
2106 case ACCESS_GENERIC:
2107 return GetProperty(cx, scope, scope, id, vp);
2108 case ACCESS_LOST:
2109 vp.setMagic(JS_OPTIMIZED_OUT);
2110 return true;
2111 default:
2112 MOZ_CRASH("bad AccessResult");
2113 }
2114 }
2115
set(JSContext * cx,HandleObject proxy,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result) const2116 bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, HandleValue receiver,
2117 ObjectOpResult& result) const override
2118 {
2119 Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
2120 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
2121
2122 if (debugScope->isOptimizedOut())
2123 return Throw(cx, id, JSMSG_DEBUG_CANT_SET_OPT_ENV);
2124
2125 AccessResult access;
2126 RootedValue valCopy(cx, v);
2127 if (!handleUnaliasedAccess(cx, debugScope, scope, id, SET, &valCopy, &access))
2128 return false;
2129
2130 switch (access) {
2131 case ACCESS_UNALIASED:
2132 return result.succeed();
2133 case ACCESS_GENERIC:
2134 {
2135 RootedValue scopeVal(cx, ObjectValue(*scope));
2136 return SetProperty(cx, scope, id, v, scopeVal, result);
2137 }
2138 default:
2139 MOZ_CRASH("bad AccessResult");
2140 }
2141 }
2142
defineProperty(JSContext * cx,HandleObject proxy,HandleId id,Handle<PropertyDescriptor> desc,ObjectOpResult & result) const2143 bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
2144 Handle<PropertyDescriptor> desc,
2145 ObjectOpResult& result) const override
2146 {
2147 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
2148
2149 bool found;
2150 if (!has(cx, proxy, id, &found))
2151 return false;
2152 if (found)
2153 return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
2154
2155 return JS_DefinePropertyById(cx, scope, id, desc, result);
2156 }
2157
ownPropertyKeys(JSContext * cx,HandleObject proxy,AutoIdVector & props) const2158 bool ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const override
2159 {
2160 Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
2161
2162 if (isMissingArgumentsBinding(*scope)) {
2163 if (!props.append(NameToId(cx->names().arguments)))
2164 return false;
2165 }
2166 if (isMissingThisBinding(*scope)) {
2167 if (!props.append(NameToId(cx->names().dotThis)))
2168 return false;
2169 }
2170
2171 // DynamicWithObject isn't a very good proxy. It doesn't have a
2172 // JSNewEnumerateOp implementation, because if it just delegated to the
2173 // target object, the object would indicate that native enumeration is
2174 // the thing to do, but native enumeration over the DynamicWithObject
2175 // wrapper yields no properties. So instead here we hack around the
2176 // issue, and punch a hole through to the with object target.
2177 Rooted<JSObject*> target(cx, (scope->is<DynamicWithObject>()
2178 ? &scope->as<DynamicWithObject>().object() : scope));
2179 if (!GetPropertyKeys(cx, target, JSITER_OWNONLY, &props))
2180 return false;
2181
2182 /*
2183 * Function scopes are optimized to not contain unaliased variables so
2184 * they must be manually appended here.
2185 */
2186 if (isFunctionScope(*scope)) {
2187 RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript());
2188 for (BindingIter bi(script); bi; bi++) {
2189 if (!bi->aliased() && !props.append(NameToId(bi->name())))
2190 return false;
2191 }
2192 }
2193
2194 return true;
2195 }
2196
enumerate(JSContext * cx,HandleObject proxy,MutableHandleObject objp) const2197 bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const override
2198 {
2199 return BaseProxyHandler::enumerate(cx, proxy, objp);
2200 }
2201
has(JSContext * cx,HandleObject proxy,HandleId id_,bool * bp) const2202 bool has(JSContext* cx, HandleObject proxy, HandleId id_, bool* bp) const override
2203 {
2204 RootedId id(cx, id_);
2205 ScopeObject& scopeObj = proxy->as<DebugScopeObject>().scope();
2206
2207 if (isArguments(cx, id) && isFunctionScope(scopeObj)) {
2208 *bp = true;
2209 return true;
2210 }
2211 if (isThis(cx, id) && isFunctionScopeWithThis(scopeObj)) {
2212 *bp = true;
2213 return true;
2214 }
2215
2216 bool found;
2217 RootedObject scope(cx, &scopeObj);
2218 if (!JS_HasPropertyById(cx, scope, id, &found))
2219 return false;
2220
2221 /*
2222 * Function scopes are optimized to not contain unaliased variables so
2223 * a manual search is necessary.
2224 */
2225 if (!found && isFunctionScope(*scope)) {
2226 RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript());
2227 for (BindingIter bi(script); bi; bi++) {
2228 if (!bi->aliased() && NameToId(bi->name()) == id) {
2229 found = true;
2230 break;
2231 }
2232 }
2233 }
2234
2235 *bp = found;
2236 return true;
2237 }
2238
delete_(JSContext * cx,HandleObject proxy,HandleId id,ObjectOpResult & result) const2239 bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
2240 ObjectOpResult& result) const override
2241 {
2242 return result.fail(JSMSG_CANT_DELETE);
2243 }
2244 };
2245
2246 } /* anonymous namespace */
2247
2248 template<>
2249 bool
is() const2250 JSObject::is<js::DebugScopeObject>() const
2251 {
2252 return IsDerivedProxyObject(this, &DebugScopeProxy::singleton);
2253 }
2254
2255 const char DebugScopeProxy::family = 0;
2256 const DebugScopeProxy DebugScopeProxy::singleton;
2257
2258 /* static */ DebugScopeObject*
create(JSContext * cx,ScopeObject & scope,HandleObject enclosing)2259 DebugScopeObject::create(JSContext* cx, ScopeObject& scope, HandleObject enclosing)
2260 {
2261 MOZ_ASSERT(scope.compartment() == cx->compartment());
2262 MOZ_ASSERT(!enclosing->is<ScopeObject>());
2263
2264 RootedValue priv(cx, ObjectValue(scope));
2265 JSObject* obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv,
2266 nullptr /* proto */);
2267 if (!obj)
2268 return nullptr;
2269
2270 DebugScopeObject* debugScope = &obj->as<DebugScopeObject>();
2271 debugScope->setExtra(ENCLOSING_EXTRA, ObjectValue(*enclosing));
2272 debugScope->setExtra(SNAPSHOT_EXTRA, NullValue());
2273
2274 return debugScope;
2275 }
2276
2277 ScopeObject&
scope() const2278 DebugScopeObject::scope() const
2279 {
2280 return target()->as<ScopeObject>();
2281 }
2282
2283 JSObject&
enclosingScope() const2284 DebugScopeObject::enclosingScope() const
2285 {
2286 return extra(ENCLOSING_EXTRA).toObject();
2287 }
2288
2289 ArrayObject*
maybeSnapshot() const2290 DebugScopeObject::maybeSnapshot() const
2291 {
2292 MOZ_ASSERT(!scope().as<CallObject>().isForEval());
2293 JSObject* obj = extra(SNAPSHOT_EXTRA).toObjectOrNull();
2294 return obj ? &obj->as<ArrayObject>() : nullptr;
2295 }
2296
2297 void
initSnapshot(ArrayObject & o)2298 DebugScopeObject::initSnapshot(ArrayObject& o)
2299 {
2300 MOZ_ASSERT(maybeSnapshot() == nullptr);
2301 setExtra(SNAPSHOT_EXTRA, ObjectValue(o));
2302 }
2303
2304 bool
isForDeclarative() const2305 DebugScopeObject::isForDeclarative() const
2306 {
2307 ScopeObject& s = scope();
2308 return s.is<LexicalScopeBase>() || s.is<BlockObject>() || s.is<DeclEnvObject>();
2309 }
2310
2311 bool
getMaybeSentinelValue(JSContext * cx,HandleId id,MutableHandleValue vp)2312 DebugScopeObject::getMaybeSentinelValue(JSContext* cx, HandleId id, MutableHandleValue vp)
2313 {
2314 Rooted<DebugScopeObject*> self(cx, this);
2315 return DebugScopeProxy::singleton.getMaybeSentinelValue(cx, self, id, vp);
2316 }
2317
2318 bool
isFunctionScopeWithThis()2319 DebugScopeObject::isFunctionScopeWithThis()
2320 {
2321 return DebugScopeProxy::isFunctionScopeWithThis(scope());
2322 }
2323
2324 bool
isOptimizedOut() const2325 DebugScopeObject::isOptimizedOut() const
2326 {
2327 ScopeObject& s = scope();
2328
2329 if (DebugScopes::hasLiveScope(s))
2330 return false;
2331
2332 if (s.is<ClonedBlockObject>())
2333 return !s.as<ClonedBlockObject>().staticBlock().needsClone();
2334
2335 if (s.is<CallObject>()) {
2336 return !s.as<CallObject>().isForEval() &&
2337 !s.as<CallObject>().callee().needsCallObject() &&
2338 !maybeSnapshot();
2339 }
2340
2341 return false;
2342 }
2343
2344 /*****************************************************************************/
2345
DebugScopes(JSContext * cx)2346 DebugScopes::DebugScopes(JSContext* cx)
2347 : proxiedScopes(cx),
2348 missingScopes(cx->runtime()),
2349 liveScopes(cx->runtime())
2350 {}
2351
~DebugScopes()2352 DebugScopes::~DebugScopes()
2353 {
2354 MOZ_ASSERT(missingScopes.empty());
2355 }
2356
2357 bool
init()2358 DebugScopes::init()
2359 {
2360 return proxiedScopes.init() && missingScopes.init() && liveScopes.init();
2361 }
2362
2363 void
mark(JSTracer * trc)2364 DebugScopes::mark(JSTracer* trc)
2365 {
2366 proxiedScopes.trace(trc);
2367 }
2368
2369 void
sweep(JSRuntime * rt)2370 DebugScopes::sweep(JSRuntime* rt)
2371 {
2372 /*
2373 * missingScopes points to debug scopes weakly so that debug scopes can be
2374 * released more eagerly.
2375 */
2376 for (MissingScopeMap::Enum e(missingScopes); !e.empty(); e.popFront()) {
2377 if (IsAboutToBeFinalized(&e.front().value())) {
2378 /*
2379 * Note that onPopCall and onPopBlock rely on missingScopes to find
2380 * scope objects that we synthesized for the debugger's sake, and
2381 * clean up the synthetic scope objects' entries in liveScopes. So
2382 * if we remove an entry frcom missingScopes here, we must also
2383 * remove the corresponding liveScopes entry.
2384 *
2385 * Since the DebugScopeObject is the only thing using its scope
2386 * object, and the DSO is about to be finalized, you might assume
2387 * that the synthetic SO is also about to be finalized too, and thus
2388 * the loop below will take care of things. But complex GC behavior
2389 * means that marks are only conservative approximations of
2390 * liveness; we should assume that anything could be marked.
2391 *
2392 * Thus, we must explicitly remove the entries from both liveScopes
2393 * and missingScopes here.
2394 */
2395 liveScopes.remove(&e.front().value().unbarrieredGet()->scope());
2396 e.removeFront();
2397 } else {
2398 MissingScopeKey key = e.front().key();
2399 if (IsForwarded(key.staticScope())) {
2400 key.updateStaticScope(Forwarded(key.staticScope()));
2401 e.rekeyFront(key);
2402 }
2403 }
2404 }
2405
2406 /*
2407 * Scopes can be finalized when a debugger-synthesized ScopeObject is
2408 * no longer reachable via its DebugScopeObject.
2409 */
2410 liveScopes.sweep();
2411 }
2412
2413 #ifdef JSGC_HASH_TABLE_CHECKS
2414 void
checkHashTablesAfterMovingGC(JSRuntime * runtime)2415 DebugScopes::checkHashTablesAfterMovingGC(JSRuntime* runtime)
2416 {
2417 /*
2418 * This is called at the end of StoreBuffer::mark() to check that our
2419 * postbarriers have worked and that no hashtable keys (or values) are left
2420 * pointing into the nursery.
2421 */
2422 proxiedScopes.checkAfterMovingGC();
2423 for (MissingScopeMap::Range r = missingScopes.all(); !r.empty(); r.popFront()) {
2424 CheckGCThingAfterMovingGC(r.front().key().staticScope());
2425 CheckGCThingAfterMovingGC(r.front().value().get());
2426 }
2427 for (LiveScopeMap::Range r = liveScopes.all(); !r.empty(); r.popFront()) {
2428 CheckGCThingAfterMovingGC(r.front().key());
2429 CheckGCThingAfterMovingGC(r.front().value().staticScope_.get());
2430 }
2431 }
2432 #endif
2433
2434 /*
2435 * Unfortunately, GetDebugScopeForFrame needs to work even outside debug mode
2436 * (in particular, JS_GetFrameScopeChain does not require debug mode). Since
2437 * DebugScopes::onPop* are only called in debuggee frames, this means we
2438 * cannot use any of the maps in DebugScopes. This will produce debug scope
2439 * chains that do not obey the debugger invariants but that is just fine.
2440 */
2441 static bool
CanUseDebugScopeMaps(JSContext * cx)2442 CanUseDebugScopeMaps(JSContext* cx)
2443 {
2444 return cx->compartment()->isDebuggee();
2445 }
2446
2447 DebugScopes*
ensureCompartmentData(JSContext * cx)2448 DebugScopes::ensureCompartmentData(JSContext* cx)
2449 {
2450 JSCompartment* c = cx->compartment();
2451 if (c->debugScopes)
2452 return c->debugScopes;
2453
2454 auto debugScopes = cx->make_unique<DebugScopes>(cx);
2455 if (!debugScopes || !debugScopes->init()) {
2456 ReportOutOfMemory(cx);
2457 return nullptr;
2458 }
2459
2460 c->debugScopes = debugScopes.release();
2461 return c->debugScopes;
2462 }
2463
2464 DebugScopeObject*
hasDebugScope(JSContext * cx,ScopeObject & scope)2465 DebugScopes::hasDebugScope(JSContext* cx, ScopeObject& scope)
2466 {
2467 DebugScopes* scopes = scope.compartment()->debugScopes;
2468 if (!scopes)
2469 return nullptr;
2470
2471 if (JSObject* obj = scopes->proxiedScopes.lookup(&scope)) {
2472 MOZ_ASSERT(CanUseDebugScopeMaps(cx));
2473 return &obj->as<DebugScopeObject>();
2474 }
2475
2476 return nullptr;
2477 }
2478
2479 bool
addDebugScope(JSContext * cx,ScopeObject & scope,DebugScopeObject & debugScope)2480 DebugScopes::addDebugScope(JSContext* cx, ScopeObject& scope, DebugScopeObject& debugScope)
2481 {
2482 MOZ_ASSERT(cx->compartment() == scope.compartment());
2483 MOZ_ASSERT(cx->compartment() == debugScope.compartment());
2484
2485 if (!CanUseDebugScopeMaps(cx))
2486 return true;
2487
2488 DebugScopes* scopes = ensureCompartmentData(cx);
2489 if (!scopes)
2490 return false;
2491
2492 return scopes->proxiedScopes.add(cx, &scope, &debugScope);
2493 }
2494
2495 DebugScopeObject*
hasDebugScope(JSContext * cx,const ScopeIter & si)2496 DebugScopes::hasDebugScope(JSContext* cx, const ScopeIter& si)
2497 {
2498 MOZ_ASSERT(!si.hasSyntacticScopeObject());
2499
2500 DebugScopes* scopes = cx->compartment()->debugScopes;
2501 if (!scopes)
2502 return nullptr;
2503
2504 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(MissingScopeKey(si))) {
2505 MOZ_ASSERT(CanUseDebugScopeMaps(cx));
2506 return p->value();
2507 }
2508 return nullptr;
2509 }
2510
2511 bool
addDebugScope(JSContext * cx,const ScopeIter & si,DebugScopeObject & debugScope)2512 DebugScopes::addDebugScope(JSContext* cx, const ScopeIter& si, DebugScopeObject& debugScope)
2513 {
2514 MOZ_ASSERT(!si.hasSyntacticScopeObject());
2515 MOZ_ASSERT(cx->compartment() == debugScope.compartment());
2516 // Generators should always reify their scopes.
2517 MOZ_ASSERT_IF(si.type() == ScopeIter::Call, !si.fun().isGenerator());
2518
2519 if (!CanUseDebugScopeMaps(cx))
2520 return true;
2521
2522 DebugScopes* scopes = ensureCompartmentData(cx);
2523 if (!scopes)
2524 return false;
2525
2526 MissingScopeKey key(si);
2527 MOZ_ASSERT(!scopes->missingScopes.has(key));
2528 if (!scopes->missingScopes.put(key, ReadBarriered<DebugScopeObject*>(&debugScope))) {
2529 ReportOutOfMemory(cx);
2530 return false;
2531 }
2532
2533 // Only add to liveScopes if we synthesized the debug scope on a live
2534 // frame.
2535 if (si.withinInitialFrame()) {
2536 MOZ_ASSERT(!scopes->liveScopes.has(&debugScope.scope()));
2537 if (!scopes->liveScopes.put(&debugScope.scope(), LiveScopeVal(si))) {
2538 ReportOutOfMemory(cx);
2539 return false;
2540 }
2541 }
2542
2543 return true;
2544 }
2545
2546 void
onPopCall(AbstractFramePtr frame,JSContext * cx)2547 DebugScopes::onPopCall(AbstractFramePtr frame, JSContext* cx)
2548 {
2549 assertSameCompartment(cx, frame);
2550
2551 DebugScopes* scopes = cx->compartment()->debugScopes;
2552 if (!scopes)
2553 return;
2554
2555 Rooted<DebugScopeObject*> debugScope(cx, nullptr);
2556
2557 if (frame.fun()->needsCallObject()) {
2558 /*
2559 * The frame may be observed before the prologue has created the
2560 * CallObject. See ScopeIter::settle.
2561 */
2562 if (!frame.hasCallObj())
2563 return;
2564
2565 if (frame.fun()->isGenerator())
2566 return;
2567
2568 CallObject& callobj = frame.scopeChain()->as<CallObject>();
2569 scopes->liveScopes.remove(&callobj);
2570 if (JSObject* obj = scopes->proxiedScopes.lookup(&callobj))
2571 debugScope = &obj->as<DebugScopeObject>();
2572 } else {
2573 ScopeIter si(cx, frame, frame.script()->main());
2574 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(MissingScopeKey(si))) {
2575 debugScope = p->value();
2576 scopes->liveScopes.remove(&debugScope->scope().as<CallObject>());
2577 scopes->missingScopes.remove(p);
2578 }
2579 }
2580
2581 /*
2582 * When the JS stack frame is popped, the values of unaliased variables
2583 * are lost. If there is any debug scope referring to this scope, save a
2584 * copy of the unaliased variables' values in an array for later debugger
2585 * access via DebugScopeProxy::handleUnaliasedAccess.
2586 *
2587 * Note: since it is simplest for this function to be infallible, failure
2588 * in this code will be silently ignored. This does not break any
2589 * invariants since DebugScopeObject::maybeSnapshot can already be nullptr.
2590 */
2591 if (debugScope) {
2592 /*
2593 * Copy all frame values into the snapshot, regardless of
2594 * aliasing. This unnecessarily includes aliased variables
2595 * but it simplifies later indexing logic.
2596 */
2597 AutoValueVector vec(cx);
2598 if (!frame.copyRawFrameSlots(&vec) || vec.length() == 0)
2599 return;
2600
2601 /*
2602 * Copy in formals that are not aliased via the scope chain
2603 * but are aliased via the arguments object.
2604 */
2605 RootedScript script(cx, frame.script());
2606 if (script->analyzedArgsUsage() && script->needsArgsObj() && frame.hasArgsObj()) {
2607 for (unsigned i = 0; i < frame.numFormalArgs(); ++i) {
2608 if (script->formalLivesInArgumentsObject(i))
2609 vec[i].set(frame.argsObj().arg(i));
2610 }
2611 }
2612
2613 /*
2614 * Use a dense array as storage (since proxies do not have trace
2615 * hooks). This array must not escape into the wild.
2616 */
2617 RootedArrayObject snapshot(cx, NewDenseCopiedArray(cx, vec.length(), vec.begin()));
2618 if (!snapshot) {
2619 cx->clearPendingException();
2620 return;
2621 }
2622
2623 debugScope->initSnapshot(*snapshot);
2624 }
2625 }
2626
2627 void
onPopBlock(JSContext * cx,AbstractFramePtr frame,jsbytecode * pc)2628 DebugScopes::onPopBlock(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc)
2629 {
2630 assertSameCompartment(cx, frame);
2631
2632 DebugScopes* scopes = cx->compartment()->debugScopes;
2633 if (!scopes)
2634 return;
2635
2636 ScopeIter si(cx, frame, pc);
2637 onPopBlock(cx, si);
2638 }
2639
2640 void
onPopBlock(JSContext * cx,const ScopeIter & si)2641 DebugScopes::onPopBlock(JSContext* cx, const ScopeIter& si)
2642 {
2643 DebugScopes* scopes = cx->compartment()->debugScopes;
2644 if (!scopes)
2645 return;
2646
2647 MOZ_ASSERT(si.withinInitialFrame());
2648 MOZ_ASSERT(si.type() == ScopeIter::Block);
2649
2650 if (si.staticBlock().needsClone()) {
2651 ClonedBlockObject& clone = si.scope().as<ClonedBlockObject>();
2652 clone.copyUnaliasedValues(si.initialFrame());
2653 scopes->liveScopes.remove(&clone);
2654 } else {
2655 if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(MissingScopeKey(si))) {
2656 ClonedBlockObject& clone = p->value()->scope().as<ClonedBlockObject>();
2657 clone.copyUnaliasedValues(si.initialFrame());
2658 scopes->liveScopes.remove(&clone);
2659 scopes->missingScopes.remove(p);
2660 }
2661 }
2662 }
2663
2664 void
onPopWith(AbstractFramePtr frame)2665 DebugScopes::onPopWith(AbstractFramePtr frame)
2666 {
2667 DebugScopes* scopes = frame.compartment()->debugScopes;
2668 if (scopes)
2669 scopes->liveScopes.remove(&frame.scopeChain()->as<DynamicWithObject>());
2670 }
2671
2672 void
onPopStrictEvalScope(AbstractFramePtr frame)2673 DebugScopes::onPopStrictEvalScope(AbstractFramePtr frame)
2674 {
2675 DebugScopes* scopes = frame.compartment()->debugScopes;
2676 if (!scopes)
2677 return;
2678
2679 /*
2680 * The stack frame may be observed before the prologue has created the
2681 * CallObject. See ScopeIter::settle.
2682 */
2683 if (frame.hasCallObj())
2684 scopes->liveScopes.remove(&frame.scopeChain()->as<CallObject>());
2685 }
2686
2687 void
onCompartmentUnsetIsDebuggee(JSCompartment * c)2688 DebugScopes::onCompartmentUnsetIsDebuggee(JSCompartment* c)
2689 {
2690 DebugScopes* scopes = c->debugScopes;
2691 if (scopes) {
2692 scopes->proxiedScopes.clear();
2693 scopes->missingScopes.clear();
2694 scopes->liveScopes.clear();
2695 }
2696 }
2697
2698 bool
updateLiveScopes(JSContext * cx)2699 DebugScopes::updateLiveScopes(JSContext* cx)
2700 {
2701 JS_CHECK_RECURSION(cx, return false);
2702
2703 /*
2704 * Note that we must always update the top frame's scope objects' entries
2705 * in liveScopes because we can't be sure code hasn't run in that frame to
2706 * change the scope chain since we were last called. The fp->prevUpToDate()
2707 * flag indicates whether the scopes of frames older than fp are already
2708 * included in liveScopes. It might seem simpler to have fp instead carry a
2709 * flag indicating whether fp itself is accurately described, but then we
2710 * would need to clear that flag whenever fp ran code. By storing the 'up
2711 * to date' bit for fp->prev() in fp, simply popping fp effectively clears
2712 * the flag for us, at exactly the time when execution resumes fp->prev().
2713 */
2714 for (AllFramesIter i(cx); !i.done(); ++i) {
2715 if (!i.hasUsableAbstractFramePtr())
2716 continue;
2717
2718 AbstractFramePtr frame = i.abstractFramePtr();
2719 if (frame.scopeChain()->compartment() != cx->compartment())
2720 continue;
2721
2722 if (frame.isFunctionFrame() && frame.callee()->isGenerator())
2723 continue;
2724
2725 if (!frame.isDebuggee())
2726 continue;
2727
2728 for (ScopeIter si(cx, frame, i.pc()); si.withinInitialFrame(); ++si) {
2729 if (si.hasSyntacticScopeObject()) {
2730 MOZ_ASSERT(si.scope().compartment() == cx->compartment());
2731 DebugScopes* scopes = ensureCompartmentData(cx);
2732 if (!scopes)
2733 return false;
2734 if (!scopes->liveScopes.put(&si.scope(), LiveScopeVal(si)))
2735 return false;
2736 }
2737 }
2738
2739 if (frame.prevUpToDate())
2740 return true;
2741 MOZ_ASSERT(frame.scopeChain()->compartment()->isDebuggee());
2742 frame.setPrevUpToDate();
2743 }
2744
2745 return true;
2746 }
2747
2748 LiveScopeVal*
hasLiveScope(ScopeObject & scope)2749 DebugScopes::hasLiveScope(ScopeObject& scope)
2750 {
2751 DebugScopes* scopes = scope.compartment()->debugScopes;
2752 if (!scopes)
2753 return nullptr;
2754
2755 if (LiveScopeMap::Ptr p = scopes->liveScopes.lookup(&scope))
2756 return &p->value();
2757
2758 return nullptr;
2759 }
2760
2761 /* static */ void
unsetPrevUpToDateUntil(JSContext * cx,AbstractFramePtr until)2762 DebugScopes::unsetPrevUpToDateUntil(JSContext* cx, AbstractFramePtr until)
2763 {
2764 // This are two exceptions where fp->prevUpToDate() is cleared without
2765 // popping the frame. When a frame is rematerialized or has its
2766 // debuggeeness toggled off->on, all frames younger than the frame must
2767 // have their prevUpToDate set to false. This is because unrematerialized
2768 // Ion frames and non-debuggee frames are skipped by updateLiveScopes. If
2769 // in the future a frame suddenly gains a usable AbstractFramePtr via
2770 // rematerialization or becomes a debuggee, the prevUpToDate invariant
2771 // will no longer hold for older frames on its stack.
2772 for (AllFramesIter i(cx); !i.done(); ++i) {
2773 if (!i.hasUsableAbstractFramePtr())
2774 continue;
2775
2776 AbstractFramePtr frame = i.abstractFramePtr();
2777 if (frame == until)
2778 return;
2779
2780 if (frame.scopeChain()->compartment() != cx->compartment())
2781 continue;
2782
2783 frame.unsetPrevUpToDate();
2784 }
2785 }
2786
2787 /* static */ void
forwardLiveFrame(JSContext * cx,AbstractFramePtr from,AbstractFramePtr to)2788 DebugScopes::forwardLiveFrame(JSContext* cx, AbstractFramePtr from, AbstractFramePtr to)
2789 {
2790 DebugScopes* scopes = cx->compartment()->debugScopes;
2791 if (!scopes)
2792 return;
2793
2794 for (MissingScopeMap::Enum e(scopes->missingScopes); !e.empty(); e.popFront()) {
2795 MissingScopeKey key = e.front().key();
2796 if (key.frame() == from) {
2797 key.updateFrame(to);
2798 e.rekeyFront(key);
2799 }
2800 }
2801
2802 for (LiveScopeMap::Enum e(scopes->liveScopes); !e.empty(); e.popFront()) {
2803 LiveScopeVal& val = e.front().value();
2804 if (val.frame() == from)
2805 val.updateFrame(to);
2806 }
2807 }
2808
2809 /*****************************************************************************/
2810
2811 static JSObject*
2812 GetDebugScope(JSContext* cx, const ScopeIter& si);
2813
2814 static DebugScopeObject*
GetDebugScopeForScope(JSContext * cx,const ScopeIter & si)2815 GetDebugScopeForScope(JSContext* cx, const ScopeIter& si)
2816 {
2817 Rooted<ScopeObject*> scope(cx, &si.scope());
2818 if (DebugScopeObject* debugScope = DebugScopes::hasDebugScope(cx, *scope))
2819 return debugScope;
2820
2821 ScopeIter copy(cx, si);
2822 RootedObject enclosingDebug(cx, GetDebugScope(cx, ++copy));
2823 if (!enclosingDebug)
2824 return nullptr;
2825
2826 JSObject& maybeDecl = scope->enclosingScope();
2827 if (maybeDecl.is<DeclEnvObject>()) {
2828 MOZ_ASSERT(CallObjectLambdaName(scope->as<CallObject>().callee()));
2829 enclosingDebug = DebugScopeObject::create(cx, maybeDecl.as<DeclEnvObject>(), enclosingDebug);
2830 if (!enclosingDebug)
2831 return nullptr;
2832 }
2833
2834 DebugScopeObject* debugScope = DebugScopeObject::create(cx, *scope, enclosingDebug);
2835 if (!debugScope)
2836 return nullptr;
2837
2838 if (!DebugScopes::addDebugScope(cx, *scope, *debugScope))
2839 return nullptr;
2840
2841 return debugScope;
2842 }
2843
2844 static DebugScopeObject*
GetDebugScopeForMissing(JSContext * cx,const ScopeIter & si)2845 GetDebugScopeForMissing(JSContext* cx, const ScopeIter& si)
2846 {
2847 MOZ_ASSERT(!si.hasSyntacticScopeObject() && si.canHaveSyntacticScopeObject());
2848
2849 if (DebugScopeObject* debugScope = DebugScopes::hasDebugScope(cx, si))
2850 return debugScope;
2851
2852 ScopeIter copy(cx, si);
2853 RootedObject enclosingDebug(cx, GetDebugScope(cx, ++copy));
2854 if (!enclosingDebug)
2855 return nullptr;
2856
2857 /*
2858 * Create the missing scope object. For block objects, this takes care of
2859 * storing variable values after the stack frame has been popped. For call
2860 * objects, we only use the pretend call object to access callee, bindings
2861 * and to receive dynamically added properties. Together, this provides the
2862 * nice invariant that every DebugScopeObject has a ScopeObject.
2863 *
2864 * Note: to preserve scopeChain depth invariants, these lazily-reified
2865 * scopes must not be put on the frame's scope chain; instead, they are
2866 * maintained via DebugScopes hooks.
2867 */
2868 DebugScopeObject* debugScope = nullptr;
2869 switch (si.type()) {
2870 case ScopeIter::Module:
2871 MOZ_CRASH(); // TODO: Implement debug scopes for modules.
2872 break;
2873
2874 case ScopeIter::Call: {
2875 RootedFunction callee(cx, &si.fun());
2876 // Generators should always reify their scopes.
2877 MOZ_ASSERT(!callee->isGenerator());
2878
2879 Rooted<CallObject*> callobj(cx);
2880 if (si.withinInitialFrame())
2881 callobj = CallObject::createForFunction(cx, si.initialFrame());
2882 else
2883 callobj = CallObject::createHollowForDebug(cx, callee);
2884 if (!callobj)
2885 return nullptr;
2886
2887 if (callobj->enclosingScope().is<DeclEnvObject>()) {
2888 MOZ_ASSERT(CallObjectLambdaName(callobj->callee()));
2889 DeclEnvObject& declenv = callobj->enclosingScope().as<DeclEnvObject>();
2890 enclosingDebug = DebugScopeObject::create(cx, declenv, enclosingDebug);
2891 if (!enclosingDebug)
2892 return nullptr;
2893 }
2894
2895 debugScope = DebugScopeObject::create(cx, *callobj, enclosingDebug);
2896 break;
2897 }
2898 case ScopeIter::Block: {
2899 // Generators should always reify their scopes, except in this one
2900 // weird case of deprecated let expressions where we can create a
2901 // 0-variable StaticBlockObject inside a generator that does not need
2902 // cloning.
2903 //
2904 // For example, |let ({} = "") { yield evalInFrame("foo"); }|.
2905 MOZ_ASSERT_IF(si.staticBlock().numVariables() > 0 &&
2906 si.withinInitialFrame() &&
2907 si.initialFrame().isFunctionFrame(),
2908 !si.initialFrame().callee()->isGenerator());
2909
2910 Rooted<StaticBlockObject*> staticBlock(cx, &si.staticBlock());
2911 ClonedBlockObject* block;
2912 if (si.withinInitialFrame())
2913 block = ClonedBlockObject::create(cx, staticBlock, si.initialFrame());
2914 else
2915 block = ClonedBlockObject::createHollowForDebug(cx, staticBlock);
2916 if (!block)
2917 return nullptr;
2918
2919 debugScope = DebugScopeObject::create(cx, *block, enclosingDebug);
2920 break;
2921 }
2922 case ScopeIter::With:
2923 case ScopeIter::Eval:
2924 MOZ_CRASH("should already have a scope");
2925 case ScopeIter::NonSyntactic:
2926 MOZ_CRASH("non-syntactic scopes cannot be synthesized");
2927 }
2928 if (!debugScope)
2929 return nullptr;
2930
2931 if (!DebugScopes::addDebugScope(cx, si, *debugScope))
2932 return nullptr;
2933
2934 return debugScope;
2935 }
2936
2937 static JSObject*
GetDebugScopeForNonScopeObject(const ScopeIter & si)2938 GetDebugScopeForNonScopeObject(const ScopeIter& si)
2939 {
2940 JSObject& enclosing = si.enclosingScope();
2941 MOZ_ASSERT(!enclosing.is<ScopeObject>());
2942 #ifdef DEBUG
2943 JSObject* o = &enclosing;
2944 while ((o = o->enclosingScope()))
2945 MOZ_ASSERT(!o->is<ScopeObject>());
2946 #endif
2947 return &enclosing;
2948 }
2949
2950 static JSObject*
GetDebugScope(JSContext * cx,const ScopeIter & si)2951 GetDebugScope(JSContext* cx, const ScopeIter& si)
2952 {
2953 JS_CHECK_RECURSION(cx, return nullptr);
2954
2955 if (si.done())
2956 return GetDebugScopeForNonScopeObject(si);
2957
2958 if (si.hasAnyScopeObject())
2959 return GetDebugScopeForScope(cx, si);
2960
2961 if (si.canHaveSyntacticScopeObject())
2962 return GetDebugScopeForMissing(cx, si);
2963
2964 ScopeIter copy(cx, si);
2965 return GetDebugScope(cx, ++copy);
2966 }
2967
2968 JSObject*
GetDebugScopeForFunction(JSContext * cx,HandleFunction fun)2969 js::GetDebugScopeForFunction(JSContext* cx, HandleFunction fun)
2970 {
2971 assertSameCompartment(cx, fun);
2972 MOZ_ASSERT(CanUseDebugScopeMaps(cx));
2973 if (!DebugScopes::updateLiveScopes(cx))
2974 return nullptr;
2975 JSScript* script = fun->getOrCreateScript(cx);
2976 if (!script)
2977 return nullptr;
2978 ScopeIter si(cx, fun->environment(), script->enclosingStaticScope());
2979 return GetDebugScope(cx, si);
2980 }
2981
2982 JSObject*
GetDebugScopeForFrame(JSContext * cx,AbstractFramePtr frame,jsbytecode * pc)2983 js::GetDebugScopeForFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc)
2984 {
2985 assertSameCompartment(cx, frame);
2986 if (CanUseDebugScopeMaps(cx) && !DebugScopes::updateLiveScopes(cx))
2987 return nullptr;
2988
2989 ScopeIter si(cx, frame, pc);
2990 return GetDebugScope(cx, si);
2991 }
2992
2993 JSObject*
GetDebugScopeForGlobalLexicalScope(JSContext * cx)2994 js::GetDebugScopeForGlobalLexicalScope(JSContext* cx)
2995 {
2996 ScopeIter si(cx, &cx->global()->lexicalScope(), &cx->global()->lexicalScope().staticBlock());
2997 return GetDebugScope(cx, si);
2998 }
2999
3000 // See declaration and documentation in jsfriendapi.h
JS_FRIEND_API(JSObject *)3001 JS_FRIEND_API(JSObject*)
3002 js::GetNearestEnclosingWithScopeObjectForFunction(JSFunction* fun)
3003 {
3004 if (!fun->isInterpreted())
3005 return &fun->global();
3006
3007 JSObject* env = fun->environment();
3008 while (env && !env->is<DynamicWithObject>())
3009 env = env->enclosingScope();
3010
3011 if (!env)
3012 return &fun->global();
3013
3014 return &env->as<DynamicWithObject>().object();
3015 }
3016
3017 bool
CreateScopeObjectsForScopeChain(JSContext * cx,AutoObjectVector & scopeChain,HandleObject dynamicTerminatingScope,MutableHandleObject dynamicScopeObj)3018 js::CreateScopeObjectsForScopeChain(JSContext* cx, AutoObjectVector& scopeChain,
3019 HandleObject dynamicTerminatingScope,
3020 MutableHandleObject dynamicScopeObj)
3021 {
3022 #ifdef DEBUG
3023 for (size_t i = 0; i < scopeChain.length(); ++i) {
3024 assertSameCompartment(cx, scopeChain[i]);
3025 MOZ_ASSERT(!scopeChain[i]->is<GlobalObject>());
3026 }
3027 #endif
3028
3029 // Construct With object wrappers for the things on this scope
3030 // chain and use the result as the thing to scope the function to.
3031 Rooted<StaticWithObject*> staticWith(cx);
3032 RootedObject staticEnclosingScope(cx);
3033 Rooted<DynamicWithObject*> dynamicWith(cx);
3034 RootedObject dynamicEnclosingScope(cx, dynamicTerminatingScope);
3035 for (size_t i = scopeChain.length(); i > 0; ) {
3036 staticWith = StaticWithObject::create(cx);
3037 if (!staticWith)
3038 return false;
3039 staticWith->initEnclosingScope(staticEnclosingScope);
3040 staticEnclosingScope = staticWith;
3041
3042 dynamicWith = DynamicWithObject::create(cx, scopeChain[--i], dynamicEnclosingScope,
3043 staticWith, DynamicWithObject::NonSyntacticWith);
3044 if (!dynamicWith)
3045 return false;
3046 dynamicEnclosingScope = dynamicWith;
3047 }
3048
3049 dynamicScopeObj.set(dynamicEnclosingScope);
3050 return true;
3051 }
3052
3053 bool
HasNonSyntacticStaticScopeChain(JSObject * staticScope)3054 js::HasNonSyntacticStaticScopeChain(JSObject* staticScope)
3055 {
3056 for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++) {
3057 // If we hit a function scope, we can short circuit the logic, as
3058 // scripts cache whether they are under a non-syntactic scope.
3059 if (ssi.type() == StaticScopeIter<NoGC>::Function)
3060 return ssi.funScript()->hasNonSyntacticScope();
3061 if (ssi.type() == StaticScopeIter<NoGC>::NonSyntactic)
3062 return true;
3063 }
3064 return false;
3065 }
3066
3067 uint32_t
StaticScopeChainLength(JSObject * staticScope)3068 js::StaticScopeChainLength(JSObject* staticScope)
3069 {
3070 uint32_t length = 0;
3071 for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++)
3072 length++;
3073 return length;
3074 }
3075
3076 ModuleEnvironmentObject*
GetModuleEnvironmentForScript(JSScript * script)3077 js::GetModuleEnvironmentForScript(JSScript* script)
3078 {
3079 StaticScopeIter<NoGC> ssi(script->enclosingStaticScope());
3080 while (!ssi.done() && ssi.type() != StaticScopeIter<NoGC>::Module)
3081 ssi++;
3082 if (ssi.done())
3083 return nullptr;
3084
3085 return ssi.module().environment();
3086 }
3087
3088 bool
GetThisValueForDebuggerMaybeOptimizedOut(JSContext * cx,AbstractFramePtr frame,jsbytecode * pc,MutableHandleValue res)3089 js::GetThisValueForDebuggerMaybeOptimizedOut(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc,
3090 MutableHandleValue res)
3091 {
3092 for (ScopeIter si(cx, frame, pc); !si.done(); ++si) {
3093 if (si.type() == ScopeIter::Module) {
3094 res.setUndefined();
3095 return true;
3096 }
3097
3098 if (si.type() != ScopeIter::Call || si.fun().hasLexicalThis())
3099 continue;
3100
3101 RootedScript script(cx, si.fun().nonLazyScript());
3102
3103 if (!script->functionHasThisBinding()) {
3104 MOZ_ASSERT(!script->isDerivedClassConstructor(),
3105 "Derived class constructors always have a this-binding");
3106
3107 // If we're still inside `frame`, we can use the this-value passed
3108 // to it, if it does not require boxing.
3109 if (si.withinInitialFrame() && (frame.thisArgument().isObject() || script->strict()))
3110 res.set(frame.thisArgument());
3111 else
3112 res.setMagic(JS_OPTIMIZED_OUT);
3113 return true;
3114 }
3115
3116 BindingIter bi = Bindings::thisBinding(cx, script);
3117
3118 if (script->bindingIsAliased(bi)) {
3119 RootedObject callObj(cx, &si.scope().as<CallObject>());
3120 return GetProperty(cx, callObj, callObj, cx->names().dotThis, res);
3121 }
3122
3123 if (si.withinInitialFrame())
3124 res.set(frame.unaliasedLocal(bi.frameIndex()));
3125 else
3126 res.setMagic(JS_OPTIMIZED_OUT);
3127 return true;
3128 }
3129
3130 RootedObject scopeChain(cx, frame.scopeChain());
3131 return GetNonSyntacticGlobalThis(cx, scopeChain, res);
3132 }
3133
3134 bool
CheckLexicalNameConflict(JSContext * cx,Handle<ClonedBlockObject * > lexicalScope,HandleObject varObj,HandlePropertyName name)3135 js::CheckLexicalNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
3136 HandleObject varObj, HandlePropertyName name)
3137 {
3138 mozilla::Maybe<frontend::Definition::Kind> redeclKind;
3139 RootedId id(cx, NameToId(name));
3140 RootedShape shape(cx);
3141 if ((shape = lexicalScope->lookup(cx, name))) {
3142 redeclKind = mozilla::Some(shape->writable() ? frontend::Definition::LET
3143 : frontend::Definition::CONSTANT);
3144 } else if (varObj->isNative() && (shape = varObj->as<NativeObject>().lookup(cx, name))) {
3145 if (!shape->configurable())
3146 redeclKind = mozilla::Some(frontend::Definition::VAR);
3147 } else {
3148 Rooted<PropertyDescriptor> desc(cx);
3149 if (!GetOwnPropertyDescriptor(cx, varObj, id, &desc))
3150 return false;
3151 if (desc.object() && desc.hasConfigurable() && !desc.configurable())
3152 redeclKind = mozilla::Some(frontend::Definition::VAR);
3153 }
3154
3155 if (redeclKind.isSome()) {
3156 ReportRuntimeRedeclaration(cx, name, *redeclKind);
3157 return false;
3158 }
3159
3160 return true;
3161 }
3162
3163 bool
CheckVarNameConflict(JSContext * cx,Handle<ClonedBlockObject * > lexicalScope,HandlePropertyName name)3164 js::CheckVarNameConflict(JSContext* cx, Handle<ClonedBlockObject*> lexicalScope,
3165 HandlePropertyName name)
3166 {
3167 if (Shape* shape = lexicalScope->lookup(cx, name)) {
3168 ReportRuntimeRedeclaration(cx, name, shape->writable() ? frontend::Definition::LET
3169 : frontend::Definition::CONSTANT);
3170 return false;
3171 }
3172 return true;
3173 }
3174
3175 static bool
CheckVarNameConflict(JSContext * cx,Handle<CallObject * > callObj,HandlePropertyName name)3176 CheckVarNameConflict(JSContext* cx, Handle<CallObject*> callObj, HandlePropertyName name)
3177 {
3178 RootedFunction fun(cx, &callObj->callee());
3179 RootedScript script(cx, fun->nonLazyScript());
3180 uint32_t bodyLevelLexicalsStart = script->bindings.numVars();
3181
3182 for (BindingIter bi(script); !bi.done(); bi++) {
3183 if (name == bi->name() &&
3184 bi.isBodyLevelLexical() &&
3185 bi.localIndex() >= bodyLevelLexicalsStart)
3186 {
3187 ReportRuntimeRedeclaration(cx, name,
3188 bi->kind() == Binding::CONSTANT
3189 ? frontend::Definition::CONSTANT
3190 : frontend::Definition::LET);
3191 return false;
3192 }
3193 }
3194
3195 return true;
3196 }
3197
3198 bool
CheckGlobalDeclarationConflicts(JSContext * cx,HandleScript script,Handle<ClonedBlockObject * > lexicalScope,HandleObject varObj)3199 js::CheckGlobalDeclarationConflicts(JSContext* cx, HandleScript script,
3200 Handle<ClonedBlockObject*> lexicalScope,
3201 HandleObject varObj)
3202 {
3203 // Due to the extensibility of the global lexical scope, we must check for
3204 // redeclaring a binding.
3205 //
3206 // In the case of non-syntactic scope chains, we are checking
3207 // redeclarations against the non-syntactic lexical scope and the
3208 // variables object that the lexical scope corresponds to.
3209 RootedPropertyName name(cx);
3210 BindingIter bi(script);
3211
3212 for (uint32_t i = 0; i < script->bindings.numVars(); i++, bi++) {
3213 name = bi->name();
3214 if (!CheckVarNameConflict(cx, lexicalScope, name))
3215 return false;
3216 }
3217
3218 for (uint32_t i = 0; i < script->bindings.numBodyLevelLexicals(); i++, bi++) {
3219 name = bi->name();
3220 if (!CheckLexicalNameConflict(cx, lexicalScope, varObj, name))
3221 return false;
3222 }
3223
3224 return true;
3225 }
3226
3227 template <class ScopeT>
3228 static bool
CheckVarNameConflictsInScope(JSContext * cx,HandleScript script,HandleObject obj)3229 CheckVarNameConflictsInScope(JSContext* cx, HandleScript script, HandleObject obj)
3230 {
3231 Rooted<ScopeT*> scope(cx);
3232
3233 // We return true when the scope object is not ScopeT below, because
3234 // ScopeT is either ClonedBlockObject or CallObject. No other scope
3235 // objects can contain lexical bindings, and there are no other overloads
3236 // for CheckVarNameConflict.
3237
3238 if (obj->is<ScopeT>())
3239 scope = &obj->as<ScopeT>();
3240 else if (obj->is<DebugScopeObject>() && obj->as<DebugScopeObject>().scope().is<ScopeT>())
3241 scope = &obj->as<DebugScopeObject>().scope().as<ScopeT>();
3242 else
3243 return true;
3244
3245 RootedPropertyName name(cx);
3246
3247 for (BindingIter bi(script); !bi.done(); bi++) {
3248 name = bi->name();
3249 if (!CheckVarNameConflict(cx, scope, name))
3250 return false;
3251 }
3252
3253 return true;
3254 }
3255
3256 bool
CheckEvalDeclarationConflicts(JSContext * cx,HandleScript script,HandleObject scopeChain,HandleObject varObj)3257 js::CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script,
3258 HandleObject scopeChain, HandleObject varObj)
3259 {
3260 // We don't need to check body-level lexical bindings for conflict. Eval
3261 // scripts always execute under their own lexical scope.
3262 if (script->bindings.numVars() == 0)
3263 return true;
3264
3265 RootedObject obj(cx, scopeChain);
3266
3267 // ES6 18.2.1.2 step d
3268 //
3269 // Check that a direct eval will not hoist 'var' bindings over lexical
3270 // bindings with the same name.
3271 while (obj != varObj) {
3272 if (!CheckVarNameConflictsInScope<ClonedBlockObject>(cx, script, obj))
3273 return false;
3274 obj = obj->enclosingScope();
3275 }
3276
3277 return CheckVarNameConflictsInScope<CallObject>(cx, script, varObj);
3278 }
3279
3280 #ifdef DEBUG
3281
3282 void
DumpStaticScopeChain(JSScript * script)3283 js::DumpStaticScopeChain(JSScript* script)
3284 {
3285 DumpStaticScopeChain(script->enclosingStaticScope());
3286 }
3287
3288 void
DumpStaticScopeChain(JSObject * staticScope)3289 js::DumpStaticScopeChain(JSObject* staticScope)
3290 {
3291 for (StaticScopeIter<NoGC> ssi(staticScope); !ssi.done(); ssi++) {
3292 switch (ssi.type()) {
3293 case StaticScopeIter<NoGC>::Module:
3294 fprintf(stdout, "module [%p]", &ssi.module());
3295 break;
3296 case StaticScopeIter<NoGC>::Function:
3297 if (ssi.fun().isBeingParsed())
3298 fprintf(stdout, "funbox [%p fun=%p]", ssi.maybeFunctionBox(), &ssi.fun());
3299 else
3300 fprintf(stdout, "function [%p]", &ssi.fun());
3301 break;
3302 case StaticScopeIter<NoGC>::Block:
3303 fprintf(stdout, "block [%p]", &ssi.block());
3304 break;
3305 case StaticScopeIter<NoGC>::With:
3306 fprintf(stdout, "with [%p]", &ssi.staticWith());
3307 break;
3308 case StaticScopeIter<NoGC>::NamedLambda:
3309 fprintf(stdout, "named lambda");
3310 break;
3311 case StaticScopeIter<NoGC>::Eval:
3312 fprintf(stdout, "eval [%p]", &ssi.eval());
3313 break;
3314 case StaticScopeIter<NoGC>::NonSyntactic:
3315 fprintf(stdout, "non-syntactic [%p]", &ssi.nonSyntactic());
3316 break;
3317 }
3318 fprintf(stdout, " -> ");
3319 }
3320 fprintf(stdout, "global\n");
3321 }
3322
3323 typedef HashSet<PropertyName*> PropertyNameSet;
3324
3325 static bool
RemoveReferencedNames(JSContext * cx,HandleScript script,PropertyNameSet & remainingNames)3326 RemoveReferencedNames(JSContext* cx, HandleScript script, PropertyNameSet& remainingNames)
3327 {
3328 // Remove from remainingNames --- the closure variables in some outer
3329 // script --- any free variables in this script. This analysis isn't perfect:
3330 //
3331 // - It will not account for free variables in an inner script which are
3332 // actually accessing some name in an intermediate script between the
3333 // inner and outer scripts. This can cause remainingNames to be an
3334 // underapproximation.
3335 //
3336 // - It will not account for new names introduced via eval. This can cause
3337 // remainingNames to be an overapproximation. This would be easy to fix
3338 // but is nice to have as the eval will probably not access these
3339 // these names and putting eval in an inner script is bad news if you
3340 // care about entraining variables unnecessarily.
3341
3342 for (jsbytecode* pc = script->code(); pc != script->codeEnd(); pc += GetBytecodeLength(pc)) {
3343 PropertyName* name;
3344
3345 switch (JSOp(*pc)) {
3346 case JSOP_GETNAME:
3347 case JSOP_SETNAME:
3348 case JSOP_STRICTSETNAME:
3349 name = script->getName(pc);
3350 break;
3351
3352 case JSOP_GETGNAME:
3353 case JSOP_SETGNAME:
3354 case JSOP_STRICTSETGNAME:
3355 if (script->hasNonSyntacticScope())
3356 name = script->getName(pc);
3357 else
3358 name = nullptr;
3359 break;
3360
3361 case JSOP_GETALIASEDVAR:
3362 case JSOP_SETALIASEDVAR:
3363 name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
3364 break;
3365
3366 default:
3367 name = nullptr;
3368 break;
3369 }
3370
3371 if (name)
3372 remainingNames.remove(name);
3373 }
3374
3375 if (script->hasObjects()) {
3376 ObjectArray* objects = script->objects();
3377 for (size_t i = 0; i < objects->length; i++) {
3378 JSObject* obj = objects->vector[i];
3379 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
3380 JSFunction* fun = &obj->as<JSFunction>();
3381 RootedScript innerScript(cx, fun->getOrCreateScript(cx));
3382 if (!innerScript)
3383 return false;
3384
3385 if (!RemoveReferencedNames(cx, innerScript, remainingNames))
3386 return false;
3387 }
3388 }
3389 }
3390
3391 return true;
3392 }
3393
3394 static bool
AnalyzeEntrainedVariablesInScript(JSContext * cx,HandleScript script,HandleScript innerScript)3395 AnalyzeEntrainedVariablesInScript(JSContext* cx, HandleScript script, HandleScript innerScript)
3396 {
3397 PropertyNameSet remainingNames(cx);
3398 if (!remainingNames.init())
3399 return false;
3400
3401 for (BindingIter bi(script); bi; bi++) {
3402 if (bi->aliased()) {
3403 PropertyNameSet::AddPtr p = remainingNames.lookupForAdd(bi->name());
3404 if (!p && !remainingNames.add(p, bi->name()))
3405 return false;
3406 }
3407 }
3408
3409 if (!RemoveReferencedNames(cx, innerScript, remainingNames))
3410 return false;
3411
3412 if (!remainingNames.empty()) {
3413 Sprinter buf(cx);
3414 if (!buf.init())
3415 return false;
3416
3417 buf.printf("Script ");
3418
3419 if (JSAtom* name = script->functionNonDelazifying()->displayAtom()) {
3420 buf.putString(name);
3421 buf.printf(" ");
3422 }
3423
3424 buf.printf("(%s:%" PRIuSIZE ") has variables entrained by ", script->filename(), script->lineno());
3425
3426 if (JSAtom* name = innerScript->functionNonDelazifying()->displayAtom()) {
3427 buf.putString(name);
3428 buf.printf(" ");
3429 }
3430
3431 buf.printf("(%s:%" PRIuSIZE ") ::", innerScript->filename(), innerScript->lineno());
3432
3433 for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); r.popFront()) {
3434 buf.printf(" ");
3435 buf.putString(r.front());
3436 }
3437
3438 printf("%s\n", buf.string());
3439 }
3440
3441 if (innerScript->hasObjects()) {
3442 ObjectArray* objects = innerScript->objects();
3443 for (size_t i = 0; i < objects->length; i++) {
3444 JSObject* obj = objects->vector[i];
3445 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
3446 JSFunction* fun = &obj->as<JSFunction>();
3447 RootedScript innerInnerScript(cx, fun->getOrCreateScript(cx));
3448 if (!innerInnerScript ||
3449 !AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript))
3450 {
3451 return false;
3452 }
3453 }
3454 }
3455 }
3456
3457 return true;
3458 }
3459
3460 // Look for local variables in script or any other script inner to it, which are
3461 // part of the script's call object and are unnecessarily entrained by their own
3462 // inner scripts which do not refer to those variables. An example is:
3463 //
3464 // function foo() {
3465 // var a, b;
3466 // function bar() { return a; }
3467 // function baz() { return b; }
3468 // }
3469 //
3470 // |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|.
3471 bool
AnalyzeEntrainedVariables(JSContext * cx,HandleScript script)3472 js::AnalyzeEntrainedVariables(JSContext* cx, HandleScript script)
3473 {
3474 if (!script->hasObjects())
3475 return true;
3476
3477 ObjectArray* objects = script->objects();
3478 for (size_t i = 0; i < objects->length; i++) {
3479 JSObject* obj = objects->vector[i];
3480 if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted()) {
3481 JSFunction* fun = &obj->as<JSFunction>();
3482 RootedScript innerScript(cx, fun->getOrCreateScript(cx));
3483 if (!innerScript)
3484 return false;
3485
3486 if (script->functionDelazifying() && script->functionDelazifying()->needsCallObject()) {
3487 if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript))
3488 return false;
3489 }
3490
3491 if (!AnalyzeEntrainedVariables(cx, innerScript))
3492 return false;
3493 }
3494 }
3495
3496 return true;
3497 }
3498
3499 #endif
3500