EnvironmentObject.cpp (156351B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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/EnvironmentObject-inl.h" 8 9 #include "mozilla/Maybe.h" 10 11 #include "builtin/Array.h" 12 #include "builtin/ModuleObject.h" 13 #include "js/EnvironmentChain.h" // JS::EnvironmentChain 14 #include "js/Exception.h" 15 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 16 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit 17 #include "js/friend/WindowProxy.h" // js::IsWindow, js::IsWindowProxy 18 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById, JS_HasProperty, JS_HasPropertyById 19 #include "vm/ArgumentsObject.h" 20 #include "vm/BytecodeIterator.h" 21 #include "vm/BytecodeLocation.h" 22 #include "vm/GeneratorObject.h" // js::GetGeneratorObjectForEnvironment 23 #include "vm/GlobalObject.h" 24 #include "vm/JSObject.h" 25 #include "vm/ProxyObject.h" 26 #include "vm/Realm.h" 27 #include "vm/Scope.h" 28 #include "vm/Shape.h" 29 #include "wasm/WasmDebug.h" 30 #include "wasm/WasmDebugFrame.h" 31 #include "wasm/WasmInstance.h" 32 33 #include "gc/Marking-inl.h" 34 #include "gc/StableCellHasher-inl.h" 35 #include "vm/BytecodeIterator-inl.h" 36 #include "vm/Stack-inl.h" 37 38 using namespace js; 39 40 /*****************************************************************************/ 41 42 /* 43 * Return a shape representing the static scope containing the variable 44 * accessed by the ALIASEDVAR op at 'pc'. 45 */ 46 static SharedShape* EnvironmentCoordinateToEnvironmentShape(JSScript* script, 47 jsbytecode* pc) { 48 MOZ_ASSERT(JOF_OPTYPE(JSOp(*pc)) == JOF_ENVCOORD); 49 ScopeIter si(script->innermostScope(pc)); 50 uint32_t hops = EnvironmentCoordinate(pc).hops(); 51 while (true) { 52 MOZ_ASSERT(!si.done()); 53 if (si.hasSyntacticEnvironment()) { 54 if (!hops) { 55 break; 56 } 57 hops--; 58 } 59 si++; 60 } 61 return si.environmentShape(); 62 } 63 64 PropertyName* js::EnvironmentCoordinateNameSlow(JSScript* script, 65 jsbytecode* pc) { 66 SharedShape* shape = EnvironmentCoordinateToEnvironmentShape(script, pc); 67 EnvironmentCoordinate ec(pc); 68 69 SharedShapePropertyIter<NoGC> iter(shape); 70 while (iter->slot() != ec.slot()) { 71 iter++; 72 } 73 jsid id = iter->key(); 74 75 /* Beware nameless destructuring formal. */ 76 if (!id.isAtom()) { 77 return script->runtimeFromAnyThread()->commonNames->empty_; 78 } 79 return id.toAtom()->asPropertyName(); 80 } 81 82 /*****************************************************************************/ 83 84 template <typename T> 85 static T* CreateEnvironmentObject(JSContext* cx, Handle<SharedShape*> shape, 86 gc::Heap heap, 87 gc::AllocSite* site = nullptr) { 88 static_assert(std::is_base_of_v<EnvironmentObject, T>, 89 "T must be an EnvironmentObject"); 90 91 // Environment objects do not have finalizers. 92 gc::AllocKind allocKind = gc::GetGCObjectKind(shape->numFixedSlots()); 93 MOZ_ASSERT(gc::GetObjectFinalizeKind(&T::class_) == gc::FinalizeKind::None); 94 MOZ_ASSERT(!gc::IsFinalizedKind(allocKind)); 95 96 return NativeObject::create<T>(cx, allocKind, heap, shape, site); 97 } 98 99 // Helper function for simple environment objects that don't need the overloads 100 // above. 101 template <typename T> 102 static T* CreateEnvironmentObject(JSContext* cx, Handle<SharedShape*> shape, 103 NewObjectKind newKind = GenericObject) { 104 gc::Heap heap = GetInitialHeap(newKind, &T::class_); 105 return CreateEnvironmentObject<T>(cx, shape, heap); 106 } 107 108 CallObject* CallObject::createWithShape(JSContext* cx, 109 Handle<SharedShape*> shape, 110 gc::Heap heap) { 111 return CreateEnvironmentObject<CallObject>(cx, shape, heap); 112 } 113 114 /* 115 * Create a CallObject for a JSScript that is not initialized to any particular 116 * callsite. This object can either be initialized (with an enclosing scope and 117 * callee) or used as a template for jit compilation. 118 */ 119 CallObject* CallObject::create(JSContext* cx, HandleScript script, 120 HandleObject enclosing, gc::Heap heap, 121 gc::AllocSite* site) { 122 Rooted<SharedShape*> shape( 123 cx, script->bodyScope()->as<FunctionScope>().environmentShape()); 124 MOZ_ASSERT(shape->getObjectClass() == &class_); 125 126 // The JITs assume the result is nursery allocated unless we collected the 127 // nursery, so don't change |heap| here. 128 129 auto* callObj = CreateEnvironmentObject<CallObject>(cx, shape, heap, site); 130 if (!callObj) { 131 return nullptr; 132 } 133 134 if (enclosing) { 135 callObj->initEnclosingEnvironment(enclosing); 136 } 137 138 return callObj; 139 } 140 141 CallObject* CallObject::createTemplateObject(JSContext* cx, HandleScript script, 142 HandleObject enclosing) { 143 return create(cx, script, enclosing, gc::Heap::Tenured); 144 } 145 146 CallObject* CallObject::createForFrame(JSContext* cx, AbstractFramePtr frame, 147 gc::AllocSite* site) { 148 MOZ_ASSERT(frame.isFunctionFrame()); 149 cx->check(frame); 150 151 RootedObject envChain(cx, frame.environmentChain()); 152 RootedFunction callee(cx, frame.callee()); 153 RootedScript script(cx, callee->nonLazyScript()); 154 gc::Heap heap = site ? site->initialHeap() : gc::Heap::Default; 155 156 CallObject* callobj = create(cx, script, envChain, heap, site); 157 if (!callobj) { 158 return nullptr; 159 } 160 161 callobj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee)); 162 163 return callobj; 164 } 165 166 template <class EnvT> 167 EnvT* FindEnclosingEnv(JSObject* env) { 168 for (;;) { 169 if (env->is<EnvT>()) { 170 break; 171 } else if (env->is<EnvironmentObject>()) { 172 env = &env->as<EnvironmentObject>().enclosingEnvironment(); 173 } else if (env->is<DebugEnvironmentProxy>()) { 174 EnvironmentObject& unwrapped = 175 env->as<DebugEnvironmentProxy>().environment(); 176 if (unwrapped.is<EnvT>()) { 177 env = &unwrapped; 178 break; 179 } 180 env = &env->as<DebugEnvironmentProxy>().enclosingEnvironment(); 181 } else { 182 MOZ_ASSERT(env->is<GlobalObject>()); 183 return nullptr; 184 } 185 } 186 return &env->as<EnvT>(); 187 } 188 189 CallObject* CallObject::find(JSObject* env) { 190 return FindEnclosingEnv<CallObject>(env); 191 } 192 193 ModuleEnvironmentObject* ModuleEnvironmentObject::find(JSObject* env) { 194 return FindEnclosingEnv<ModuleEnvironmentObject>(env); 195 } 196 197 CallObject* CallObject::createHollowForDebug(JSContext* cx, 198 HandleFunction callee) { 199 MOZ_ASSERT(!callee->needsCallObject()); 200 201 RootedScript script(cx, callee->nonLazyScript()); 202 Rooted<FunctionScope*> scope(cx, &script->bodyScope()->as<FunctionScope>()); 203 Rooted<SharedShape*> shape(cx, EmptyEnvironmentShape<CallObject>(cx)); 204 if (!shape) { 205 return nullptr; 206 } 207 Rooted<CallObject*> callobj(cx, createWithShape(cx, shape)); 208 if (!callobj) { 209 return nullptr; 210 } 211 212 // This environment's enclosing link is never used: the 213 // DebugEnvironmentProxy that refers to this scope carries its own 214 // enclosing link, which is what Debugger uses to construct the tree of 215 // Debugger.Environment objects. 216 callobj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment()); 217 callobj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee)); 218 219 RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT)); 220 RootedId id(cx); 221 for (Rooted<BindingIter> bi(cx, BindingIter(script)); bi; bi++) { 222 id = NameToId(bi.name()->asPropertyName()); 223 if (!SetProperty(cx, callobj, id, optimizedOut)) { 224 return nullptr; 225 } 226 } 227 228 return callobj; 229 } 230 231 const JSClass CallObject::class_ = { 232 "Call", 233 JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS), 234 }; 235 236 /*****************************************************************************/ 237 238 /* static */ 239 VarEnvironmentObject* VarEnvironmentObject::createInternal( 240 JSContext* cx, Handle<SharedShape*> shape, HandleObject enclosing, 241 gc::Heap heap) { 242 MOZ_ASSERT(shape->getObjectClass() == &class_); 243 244 auto* env = CreateEnvironmentObject<VarEnvironmentObject>(cx, shape, heap); 245 if (!env) { 246 return nullptr; 247 } 248 249 MOZ_ASSERT(!env->inDictionaryMode()); 250 251 if (enclosing) { 252 env->initEnclosingEnvironment(enclosing); 253 } 254 255 return env; 256 } 257 258 /* static */ 259 VarEnvironmentObject* VarEnvironmentObject::create(JSContext* cx, 260 Handle<Scope*> scope, 261 HandleObject enclosing, 262 gc::Heap heap) { 263 MOZ_ASSERT(scope->is<EvalScope>() || scope->is<VarScope>()); 264 265 Rooted<SharedShape*> shape(cx, scope->environmentShape()); 266 auto* env = createInternal(cx, shape, enclosing, heap); 267 if (!env) { 268 return nullptr; 269 } 270 env->initScope(scope); 271 return env; 272 } 273 274 /* static */ 275 VarEnvironmentObject* VarEnvironmentObject::createForFrame( 276 JSContext* cx, Handle<Scope*> scope, AbstractFramePtr frame) { 277 #ifdef DEBUG 278 if (frame.isEvalFrame()) { 279 MOZ_ASSERT(scope->is<EvalScope>() && scope == frame.script()->bodyScope()); 280 MOZ_ASSERT_IF(frame.isInterpreterFrame(), 281 cx->interpreterFrame() == frame.asInterpreterFrame()); 282 MOZ_ASSERT_IF(frame.isInterpreterFrame(), 283 cx->interpreterRegs().pc == frame.script()->code()); 284 } else { 285 MOZ_ASSERT(frame.environmentChain()); 286 MOZ_ASSERT_IF( 287 frame.callee()->needsCallObject(), 288 &frame.environmentChain()->as<CallObject>().callee() == frame.callee()); 289 } 290 #endif 291 292 RootedObject envChain(cx, frame.environmentChain()); 293 return create(cx, scope, envChain, gc::Heap::Default); 294 } 295 296 /* static */ 297 VarEnvironmentObject* VarEnvironmentObject::createHollowForDebug( 298 JSContext* cx, Handle<Scope*> scope) { 299 MOZ_ASSERT(scope->is<VarScope>() || scope->kind() == ScopeKind::StrictEval); 300 MOZ_ASSERT(!scope->hasEnvironment()); 301 302 Rooted<SharedShape*> shape(cx, 303 EmptyEnvironmentShape<VarEnvironmentObject>(cx)); 304 if (!shape) { 305 return nullptr; 306 } 307 308 // This environment's enclosing link is never used: the 309 // DebugEnvironmentProxy that refers to this scope carries its own 310 // enclosing link, which is what Debugger uses to construct the tree of 311 // Debugger.Environment objects. 312 RootedObject enclosingEnv(cx, &cx->global()->lexicalEnvironment()); 313 Rooted<VarEnvironmentObject*> env( 314 cx, createInternal(cx, shape, enclosingEnv, gc::Heap::Default)); 315 if (!env) { 316 return nullptr; 317 } 318 319 RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT)); 320 RootedId id(cx); 321 for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) { 322 id = NameToId(bi.name()->asPropertyName()); 323 if (!SetProperty(cx, env, id, optimizedOut)) { 324 return nullptr; 325 } 326 } 327 328 env->initScope(scope); 329 return env; 330 } 331 332 /* static */ 333 VarEnvironmentObject* VarEnvironmentObject::createTemplateObject( 334 JSContext* cx, Handle<VarScope*> scope) { 335 return create(cx, scope, nullptr, gc::Heap::Tenured); 336 } 337 338 /* static */ 339 VarEnvironmentObject* VarEnvironmentObject::createWithoutEnclosing( 340 JSContext* cx, Handle<VarScope*> scope) { 341 return create(cx, scope, nullptr, gc::Heap::Default); 342 } 343 344 const JSClass VarEnvironmentObject::class_ = { 345 "Var", 346 JSCLASS_HAS_RESERVED_SLOTS(VarEnvironmentObject::RESERVED_SLOTS), 347 }; 348 349 /*****************************************************************************/ 350 351 const ObjectOps ModuleEnvironmentObject::objectOps_ = { 352 ModuleEnvironmentObject::lookupProperty, // lookupProperty 353 nullptr, // defineProperty 354 ModuleEnvironmentObject::hasProperty, // hasProperty 355 ModuleEnvironmentObject::getProperty, // getProperty 356 ModuleEnvironmentObject::setProperty, // setProperty 357 ModuleEnvironmentObject:: 358 getOwnPropertyDescriptor, // getOwnPropertyDescriptor 359 ModuleEnvironmentObject::deleteProperty, // deleteProperty 360 nullptr, // getElements 361 nullptr, // funToString 362 }; 363 364 const JSClassOps ModuleEnvironmentObject::classOps_ = { 365 nullptr, // addProperty 366 nullptr, // delProperty 367 nullptr, // enumerate 368 ModuleEnvironmentObject::newEnumerate, // newEnumerate 369 nullptr, // resolve 370 nullptr, // mayResolve 371 nullptr, // finalize 372 nullptr, // call 373 nullptr, // construct 374 nullptr, // trace 375 }; 376 377 const JSClass ModuleEnvironmentObject::class_ = { 378 "ModuleEnvironmentObject", 379 JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS), 380 &ModuleEnvironmentObject::classOps_, 381 JS_NULL_CLASS_SPEC, 382 JS_NULL_CLASS_EXT, 383 &ModuleEnvironmentObject::objectOps_, 384 }; 385 386 /* static */ 387 ModuleEnvironmentObject* ModuleEnvironmentObject::create( 388 JSContext* cx, Handle<ModuleObject*> module) { 389 RootedScript script(cx, module->script()); 390 Rooted<SharedShape*> shape( 391 cx, script->bodyScope()->as<ModuleScope>().environmentShape()); 392 MOZ_ASSERT(shape->getObjectClass() == &class_); 393 394 ModuleEnvironmentObject* env = 395 CreateEnvironmentObject<ModuleEnvironmentObject>(cx, shape, 396 TenuredObject); 397 if (!env) { 398 return nullptr; 399 } 400 401 env->initReservedSlot(MODULE_SLOT, ObjectValue(*module)); 402 403 // Initialize this early so that we can manipulate the env object without 404 // causing assertions. 405 env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment()); 406 407 // Initialize all lexical bindings and imports as uninitialized. Imports 408 // get uninitialized because they have a special TDZ for cyclic imports. 409 for (BindingIter bi(script); bi; bi++) { 410 BindingLocation loc = bi.location(); 411 if (loc.kind() == BindingLocation::Kind::Environment && 412 BindingKindIsLexical(bi.kind())) { 413 env->initSlot(loc.slot(), MagicValue(JS_UNINITIALIZED_LEXICAL)); 414 } 415 } 416 417 // It is not be possible to add or remove bindings from a module environment 418 // after this point as module code is always strict. 419 #ifdef DEBUG 420 for (ShapePropertyIter<NoGC> iter(env->shape()); !iter.done(); iter++) { 421 MOZ_ASSERT(!iter->configurable()); 422 } 423 MOZ_ASSERT(env->hasFlag(ObjectFlag::NotExtensible)); 424 MOZ_ASSERT(!env->inDictionaryMode()); 425 #endif 426 427 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 428 env->initSlot(ModuleEnvironmentObject::DISPOSABLE_RESOURCE_STACK_SLOT, 429 UndefinedValue()); 430 #endif 431 432 return env; 433 } 434 435 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 436 static ArrayObject* initialiseAndSetDisposeCapabilityHelper( 437 JSContext* cx, JS::Handle<EnvironmentObject*> env, uint32_t slot) { 438 JS::Value slotData = env->getReservedSlot(slot); 439 ArrayObject* disposablesList = nullptr; 440 if (slotData.isUndefined()) { 441 disposablesList = NewDenseEmptyArray(cx); 442 if (!disposablesList) { 443 return nullptr; 444 } 445 env->setReservedSlot(slot, ObjectValue(*disposablesList)); 446 } else { 447 disposablesList = &slotData.toObject().as<ArrayObject>(); 448 } 449 return disposablesList; 450 } 451 452 ArrayObject* DisposableEnvironmentObject::getOrCreateDisposeCapability( 453 JSContext* cx) { 454 Rooted<DisposableEnvironmentObject*> env(cx, this); 455 return initialiseAndSetDisposeCapabilityHelper( 456 cx, env, DISPOSABLE_RESOURCE_STACK_SLOT); 457 } 458 459 // TODO: The get & clear disposables function can be merged. (bug 1907736) 460 JS::Value DisposableEnvironmentObject::getDisposables() { 461 return getReservedSlot(DISPOSABLE_RESOURCE_STACK_SLOT); 462 } 463 464 void DisposableEnvironmentObject::clearDisposables() { 465 setReservedSlot(DISPOSABLE_RESOURCE_STACK_SLOT, UndefinedValue()); 466 } 467 #endif 468 469 /* static */ 470 ModuleEnvironmentObject* ModuleEnvironmentObject::createSynthetic( 471 JSContext* cx, Handle<ModuleObject*> module) { 472 Rooted<SharedShape*> shape(cx, 473 CreateEnvironmentShapeForSyntheticModule( 474 cx, &class_, JSSLOT_FREE(&class_), module)); 475 if (!shape) { 476 return nullptr; 477 } 478 479 MOZ_ASSERT(shape->getObjectClass() == &class_); 480 481 ModuleEnvironmentObject* env = 482 CreateEnvironmentObject<ModuleEnvironmentObject>(cx, shape, 483 TenuredObject); 484 if (!env) { 485 return nullptr; 486 } 487 488 env->initReservedSlot(MODULE_SLOT, ObjectValue(*module)); 489 490 // Initialize this early so that we can manipulate the env object without 491 // causing assertions. 492 env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment()); 493 494 // It is not be possible to add or remove bindings from a module environment 495 // after this point as module code is always strict. 496 #ifdef DEBUG 497 for (ShapePropertyIter<NoGC> iter(env->shape()); !iter.done(); iter++) { 498 MOZ_ASSERT(!iter->configurable()); 499 } 500 MOZ_ASSERT(env->hasFlag(ObjectFlag::NotExtensible)); 501 MOZ_ASSERT(!env->inDictionaryMode()); 502 #endif 503 504 return env; 505 } 506 507 ModuleObject& ModuleEnvironmentObject::module() const { 508 return getReservedSlot(MODULE_SLOT).toObject().as<ModuleObject>(); 509 } 510 511 IndirectBindingMap& ModuleEnvironmentObject::importBindings() const { 512 return module().importBindings(); 513 } 514 515 bool ModuleEnvironmentObject::createImportBinding(JSContext* cx, 516 Handle<JSAtom*> importName, 517 Handle<ModuleObject*> module, 518 Handle<JSAtom*> localName) { 519 RootedId importNameId(cx, AtomToId(importName)); 520 RootedId localNameId(cx, AtomToId(localName)); 521 Rooted<ModuleEnvironmentObject*> env(cx, &module->initialEnvironment()); 522 return importBindings().put(cx, importNameId, env, localNameId); 523 } 524 525 bool ModuleEnvironmentObject::hasImportBinding(Handle<PropertyName*> name) { 526 return importBindings().has(NameToId(name)); 527 } 528 529 bool ModuleEnvironmentObject::lookupImport( 530 jsid name, ModuleEnvironmentObject** envOut, 531 mozilla::Maybe<PropertyInfo>* propOut) { 532 return importBindings().lookup(name, envOut, propOut); 533 } 534 535 /* static */ 536 bool ModuleEnvironmentObject::lookupProperty(JSContext* cx, HandleObject obj, 537 HandleId id, 538 MutableHandleObject objp, 539 PropertyResult* propp) { 540 const IndirectBindingMap& bindings = 541 obj->as<ModuleEnvironmentObject>().importBindings(); 542 mozilla::Maybe<PropertyInfo> propInfo; 543 ModuleEnvironmentObject* env; 544 if (bindings.lookup(id, &env, &propInfo)) { 545 objp.set(env); 546 propp->setNativeProperty(*propInfo); 547 return true; 548 } 549 550 Rooted<NativeObject*> target(cx, &obj->as<NativeObject>()); 551 if (!NativeLookupOwnProperty<CanGC>(cx, target, id, propp)) { 552 return false; 553 } 554 555 objp.set(obj); 556 return true; 557 } 558 559 /* static */ 560 bool ModuleEnvironmentObject::hasProperty(JSContext* cx, HandleObject obj, 561 HandleId id, bool* foundp) { 562 if (obj->as<ModuleEnvironmentObject>().importBindings().has(id)) { 563 *foundp = true; 564 return true; 565 } 566 567 Rooted<NativeObject*> self(cx, &obj->as<NativeObject>()); 568 return NativeHasProperty(cx, self, id, foundp); 569 } 570 571 /* static */ 572 bool ModuleEnvironmentObject::getProperty(JSContext* cx, HandleObject obj, 573 HandleValue receiver, HandleId id, 574 MutableHandleValue vp) { 575 const IndirectBindingMap& bindings = 576 obj->as<ModuleEnvironmentObject>().importBindings(); 577 mozilla::Maybe<PropertyInfo> prop; 578 ModuleEnvironmentObject* env; 579 if (bindings.lookup(id, &env, &prop)) { 580 vp.set(env->getSlot(prop->slot())); 581 return true; 582 } 583 584 Rooted<NativeObject*> self(cx, &obj->as<NativeObject>()); 585 return NativeGetProperty(cx, self, receiver, id, vp); 586 } 587 588 /* static */ 589 bool ModuleEnvironmentObject::setProperty(JSContext* cx, HandleObject obj, 590 HandleId id, HandleValue v, 591 HandleValue receiver, 592 JS::ObjectOpResult& result) { 593 Rooted<ModuleEnvironmentObject*> self(cx, 594 &obj->as<ModuleEnvironmentObject>()); 595 if (self->importBindings().has(id)) { 596 return result.failReadOnly(); 597 } 598 599 return NativeSetProperty<Qualified>(cx, self, id, v, receiver, result); 600 } 601 602 /* static */ 603 bool ModuleEnvironmentObject::getOwnPropertyDescriptor( 604 JSContext* cx, HandleObject obj, HandleId id, 605 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) { 606 const IndirectBindingMap& bindings = 607 obj->as<ModuleEnvironmentObject>().importBindings(); 608 mozilla::Maybe<PropertyInfo> prop; 609 ModuleEnvironmentObject* env; 610 if (bindings.lookup(id, &env, &prop)) { 611 desc.set(mozilla::Some(PropertyDescriptor::Data( 612 env->getSlot(prop->slot()), 613 {JS::PropertyAttribute::Enumerable, JS::PropertyAttribute::Writable}))); 614 return true; 615 } 616 617 Rooted<NativeObject*> self(cx, &obj->as<NativeObject>()); 618 return NativeGetOwnPropertyDescriptor(cx, self, id, desc); 619 } 620 621 /* static */ 622 bool ModuleEnvironmentObject::deleteProperty(JSContext* cx, HandleObject obj, 623 HandleId id, 624 ObjectOpResult& result) { 625 return result.failCantDelete(); 626 } 627 628 /* static */ 629 bool ModuleEnvironmentObject::newEnumerate(JSContext* cx, HandleObject obj, 630 MutableHandleIdVector properties, 631 bool enumerableOnly) { 632 Rooted<ModuleEnvironmentObject*> self(cx, 633 &obj->as<ModuleEnvironmentObject>()); 634 const IndirectBindingMap& bs(self->importBindings()); 635 636 MOZ_ASSERT(properties.length() == 0); 637 size_t count = bs.count() + self->slotSpan() - RESERVED_SLOTS; 638 if (!properties.reserve(count)) { 639 ReportOutOfMemory(cx); 640 return false; 641 } 642 643 bs.forEachExportedName([&](jsid name) { properties.infallibleAppend(name); }); 644 645 for (ShapePropertyIter<NoGC> iter(self->shape()); !iter.done(); iter++) { 646 properties.infallibleAppend(iter->key()); 647 } 648 649 MOZ_ASSERT(properties.length() == count); 650 return true; 651 } 652 653 /*****************************************************************************/ 654 655 const JSClass WasmInstanceEnvironmentObject::class_ = { 656 "WasmInstance", 657 JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceEnvironmentObject::RESERVED_SLOTS), 658 }; 659 660 /* static */ 661 WasmInstanceEnvironmentObject* 662 WasmInstanceEnvironmentObject::createHollowForDebug( 663 JSContext* cx, Handle<WasmInstanceScope*> scope) { 664 Rooted<SharedShape*> shape( 665 cx, EmptyEnvironmentShape<WasmInstanceEnvironmentObject>(cx)); 666 if (!shape) { 667 return nullptr; 668 } 669 670 auto* env = CreateEnvironmentObject<WasmInstanceEnvironmentObject>(cx, shape); 671 if (!env) { 672 return nullptr; 673 } 674 675 env->initEnclosingEnvironment(&cx->global()->lexicalEnvironment()); 676 env->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope)); 677 678 return env; 679 } 680 681 /*****************************************************************************/ 682 683 const JSClass WasmFunctionCallObject::class_ = { 684 "WasmCall", 685 JSCLASS_HAS_RESERVED_SLOTS(WasmFunctionCallObject::RESERVED_SLOTS), 686 }; 687 688 /* static */ 689 WasmFunctionCallObject* WasmFunctionCallObject::createHollowForDebug( 690 JSContext* cx, HandleObject enclosing, Handle<WasmFunctionScope*> scope) { 691 Rooted<SharedShape*> shape(cx, 692 EmptyEnvironmentShape<WasmFunctionCallObject>(cx)); 693 if (!shape) { 694 return nullptr; 695 } 696 697 auto* callobj = CreateEnvironmentObject<WasmFunctionCallObject>(cx, shape); 698 if (!callobj) { 699 return nullptr; 700 } 701 702 callobj->initEnclosingEnvironment(enclosing); 703 callobj->initReservedSlot(SCOPE_SLOT, PrivateGCThingValue(scope)); 704 705 return callobj; 706 } 707 708 /*****************************************************************************/ 709 710 JSObject* js::GetThisObject(JSObject* obj) { 711 // Use the WindowProxy if the global is a Window, as Window must never be 712 // exposed to script. 713 if (obj->is<GlobalObject>()) { 714 return ToWindowProxyIfWindow(obj); 715 } 716 717 // We should not expose any environments except NSVOs to script. The NSVO is 718 // pretending to be the global object in this case. 719 MOZ_ASSERT_IF(obj->is<EnvironmentObject>(), 720 obj->is<NonSyntacticVariablesObject>()); 721 722 return obj; 723 } 724 725 WithEnvironmentObject* WithEnvironmentObject::create( 726 JSContext* cx, HandleObject object, HandleObject enclosing, 727 Handle<WithScope*> scope, JS::SupportUnscopables supportUnscopables) { 728 Rooted<SharedShape*> shape(cx, 729 EmptyEnvironmentShape<WithEnvironmentObject>(cx)); 730 if (!shape) { 731 return nullptr; 732 } 733 734 auto* obj = CreateEnvironmentObject<WithEnvironmentObject>(cx, shape); 735 if (!obj) { 736 return nullptr; 737 } 738 739 JSObject* thisObj = GetThisObject(object); 740 741 obj->initEnclosingEnvironment(enclosing); 742 obj->initReservedSlot(OBJECT_SLOT, ObjectValue(*object)); 743 obj->initReservedSlot(THIS_SLOT, ObjectValue(*thisObj)); 744 if (scope) { 745 MOZ_ASSERT(supportUnscopables == JS::SupportUnscopables::Yes, 746 "with-statements must support Symbol.unscopables"); 747 obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT, 748 PrivateGCThingValue(scope)); 749 } else { 750 Value v = BooleanValue(supportUnscopables == JS::SupportUnscopables::Yes); 751 obj->initReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT, v); 752 } 753 754 return obj; 755 } 756 757 WithEnvironmentObject* WithEnvironmentObject::createNonSyntactic( 758 JSContext* cx, HandleObject object, HandleObject enclosing, 759 JS::SupportUnscopables supportUnscopables) { 760 return create(cx, object, enclosing, nullptr, supportUnscopables); 761 } 762 763 static inline bool IsUnscopableDotName(JSContext* cx, HandleId id) { 764 return id.isAtom(cx->names().dot_this_) || 765 id.isAtom(cx->names().dot_newTarget_); 766 } 767 768 #ifdef DEBUG 769 static bool IsInternalDotName(JSContext* cx, HandleId id) { 770 return id.isAtom(cx->names().dot_this_) || 771 id.isAtom(cx->names().dot_generator_) || 772 id.isAtom(cx->names().dot_initializers_) || 773 id.isAtom(cx->names().dot_fieldKeys_) || 774 id.isAtom(cx->names().dot_staticInitializers_) || 775 id.isAtom(cx->names().dot_staticFieldKeys_) || 776 id.isAtom(cx->names().dot_args_) || 777 id.isAtom(cx->names().dot_newTarget_) || 778 id.isAtom(cx->names().star_namespace_star_); 779 } 780 #endif 781 782 /* Implements ES6 8.1.1.2.1 HasBinding steps 7-9. */ 783 static bool CheckUnscopables(JSContext* cx, HandleObject obj, HandleId id, 784 bool* scopable) { 785 RootedId unscopablesId( 786 cx, PropertyKey::Symbol(cx->wellKnownSymbols().unscopables)); 787 RootedValue v(cx); 788 if (!GetProperty(cx, obj, obj, unscopablesId, &v)) { 789 return false; 790 } 791 if (v.isObject()) { 792 RootedObject unscopablesObj(cx, &v.toObject()); 793 if (!GetProperty(cx, unscopablesObj, unscopablesObj, id, &v)) { 794 return false; 795 } 796 *scopable = !ToBoolean(v); 797 } else { 798 *scopable = true; 799 } 800 return true; 801 } 802 803 static bool with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id, 804 MutableHandleObject objp, 805 PropertyResult* propp) { 806 // SpiderMonkey-specific: consider the internal '.this' and '.newTarget' names 807 // to be unscopable. 808 if (IsUnscopableDotName(cx, id)) { 809 objp.set(nullptr); 810 propp->setNotFound(); 811 return true; 812 } 813 814 // Other internal dot-names shouldn't even end up in with-environments. 815 MOZ_ASSERT(!IsInternalDotName(cx, id)); 816 817 RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object()); 818 if (!LookupProperty(cx, actual, id, objp, propp)) { 819 return false; 820 } 821 822 if (propp->isFound()) { 823 bool scopable = true; 824 if (obj->as<WithEnvironmentObject>().supportUnscopables() && 825 !CheckUnscopables(cx, actual, id, &scopable)) { 826 return false; 827 } 828 if (!scopable) { 829 objp.set(nullptr); 830 propp->setNotFound(); 831 } 832 } 833 return true; 834 } 835 836 static bool with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, 837 Handle<PropertyDescriptor> desc, 838 ObjectOpResult& result) { 839 MOZ_ASSERT(!IsInternalDotName(cx, id)); 840 RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object()); 841 return DefineProperty(cx, actual, id, desc, result); 842 } 843 844 static bool with_HasProperty(JSContext* cx, HandleObject obj, HandleId id, 845 bool* foundp) { 846 MOZ_ASSERT(!IsInternalDotName(cx, id)); 847 RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object()); 848 849 // ES 8.1.1.2.1 step 3-5. 850 if (!HasProperty(cx, actual, id, foundp)) { 851 return false; 852 } 853 if (!*foundp || !obj->as<WithEnvironmentObject>().supportUnscopables()) { 854 return true; 855 } 856 857 // Steps 7-10. (Step 6 is a no-op.) 858 return CheckUnscopables(cx, actual, id, foundp); 859 } 860 861 static bool with_GetProperty(JSContext* cx, HandleObject obj, 862 HandleValue receiver, HandleId id, 863 MutableHandleValue vp) { 864 MOZ_ASSERT(!IsInternalDotName(cx, id)); 865 RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object()); 866 RootedValue actualReceiver(cx, receiver); 867 if (receiver.isObject() && &receiver.toObject() == obj) { 868 actualReceiver.setObject(*actual); 869 } 870 return GetProperty(cx, actual, actualReceiver, id, vp); 871 } 872 873 static bool with_SetProperty(JSContext* cx, HandleObject obj, HandleId id, 874 HandleValue v, HandleValue receiver, 875 ObjectOpResult& result) { 876 MOZ_ASSERT(!IsInternalDotName(cx, id)); 877 RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object()); 878 RootedValue actualReceiver(cx, receiver); 879 if (receiver.isObject() && &receiver.toObject() == obj) { 880 actualReceiver.setObject(*actual); 881 } 882 return SetProperty(cx, actual, id, v, actualReceiver, result); 883 } 884 885 static bool with_GetOwnPropertyDescriptor( 886 JSContext* cx, HandleObject obj, HandleId id, 887 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) { 888 MOZ_ASSERT(!IsInternalDotName(cx, id)); 889 RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object()); 890 return GetOwnPropertyDescriptor(cx, actual, id, desc); 891 } 892 893 static bool with_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, 894 ObjectOpResult& result) { 895 MOZ_ASSERT(!IsInternalDotName(cx, id)); 896 RootedObject actual(cx, &obj->as<WithEnvironmentObject>().object()); 897 return DeleteProperty(cx, actual, id, result); 898 } 899 900 static const ObjectOps WithEnvironmentObjectOps = { 901 with_LookupProperty, // lookupProperty 902 with_DefineProperty, // defineProperty 903 with_HasProperty, // hasProperty 904 with_GetProperty, // getProperty 905 with_SetProperty, // setProperty 906 with_GetOwnPropertyDescriptor, // getOwnPropertyDescriptor 907 with_DeleteProperty, // deleteProperty 908 nullptr, // getElements 909 nullptr, // funToString 910 }; 911 912 const JSClass WithEnvironmentObject::class_ = { 913 "With", 914 JSCLASS_HAS_RESERVED_SLOTS(WithEnvironmentObject::RESERVED_SLOTS), 915 JS_NULL_CLASS_OPS, 916 JS_NULL_CLASS_SPEC, 917 JS_NULL_CLASS_EXT, 918 &WithEnvironmentObjectOps, 919 }; 920 921 /* static */ 922 NonSyntacticVariablesObject* NonSyntacticVariablesObject::create( 923 JSContext* cx) { 924 Rooted<SharedShape*> shape( 925 cx, EmptyEnvironmentShape<NonSyntacticVariablesObject>(cx)); 926 if (!shape) { 927 return nullptr; 928 } 929 930 NonSyntacticVariablesObject* obj = 931 CreateEnvironmentObject<NonSyntacticVariablesObject>(cx, shape, 932 TenuredObject); 933 if (!obj) { 934 return nullptr; 935 } 936 937 // An NVSO holds both variables qualified with `var` and those that are not. 938 MOZ_ASSERT(obj->isUnqualifiedVarObj()); 939 MOZ_ASSERT(obj->isQualifiedVarObj()); 940 941 obj->initEnclosingEnvironment(&cx->global()->lexicalEnvironment()); 942 return obj; 943 } 944 945 const JSClass NonSyntacticVariablesObject::class_ = { 946 "NonSyntacticVariablesObject", 947 JSCLASS_HAS_RESERVED_SLOTS(NonSyntacticVariablesObject::RESERVED_SLOTS), 948 }; 949 950 NonSyntacticLexicalEnvironmentObject* js::CreateNonSyntacticEnvironmentChain( 951 JSContext* cx, const JS::EnvironmentChain& envChain) { 952 // Callers are responsible for segregating the NonSyntactic case from simple 953 // compilation cases. 954 MOZ_RELEASE_ASSERT(!envChain.empty()); 955 956 RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment()); 957 Rooted<WithEnvironmentObject*> env( 958 cx, CreateObjectsForEnvironmentChain(cx, envChain, globalLexical)); 959 if (!env) { 960 return nullptr; 961 } 962 963 // The XPConnect subscript loader, which may pass in its own 964 // environments to load scripts in, expects the environment chain to 965 // be the holder of "var" declarations. In SpiderMonkey, such objects 966 // are called "qualified varobjs", the "qualified" part meaning the 967 // declaration was qualified by "var". There is only sadness. 968 // 969 // See JSObject::isQualifiedVarObj. 970 if (!JSObject::setQualifiedVarObj(cx, env)) { 971 return nullptr; 972 } 973 974 // Also get a non-syntactic lexical environment to capture 'let' and 975 // 'const' bindings. To persist lexical bindings, we have a 1-1 976 // mapping with the final unwrapped environment object (the 977 // environment that stores the 'var' bindings) and the lexical 978 // environment. 979 // 980 // TODOshu: disallow the subscript loader from using non-distinguished 981 // objects as dynamic scopes. 982 return ObjectRealm::get(env).getOrCreateNonSyntacticLexicalEnvironment(cx, 983 env); 984 } 985 986 /*****************************************************************************/ 987 988 const JSClass LexicalEnvironmentObject::class_ = { 989 "LexicalEnvironment", 990 JSCLASS_HAS_RESERVED_SLOTS(LexicalEnvironmentObject::RESERVED_SLOTS), 991 JS_NULL_CLASS_OPS, 992 JS_NULL_CLASS_SPEC, 993 JS_NULL_CLASS_EXT, 994 JS_NULL_OBJECT_OPS, 995 }; 996 997 /* static */ 998 LexicalEnvironmentObject* LexicalEnvironmentObject::create( 999 JSContext* cx, Handle<SharedShape*> shape, HandleObject enclosing, 1000 gc::Heap heap, gc::AllocSite* site) { 1001 MOZ_ASSERT(shape->getObjectClass() == &LexicalEnvironmentObject::class_); 1002 1003 // The JITs assume the result is nursery allocated unless we collected the 1004 // nursery, so don't change |heap| here. 1005 1006 auto* env = 1007 CreateEnvironmentObject<LexicalEnvironmentObject>(cx, shape, heap, site); 1008 if (!env) { 1009 return nullptr; 1010 } 1011 1012 MOZ_ASSERT(!env->inDictionaryMode()); 1013 1014 if (enclosing) { 1015 env->initEnclosingEnvironment(enclosing); 1016 } 1017 1018 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 1019 env->initSlot(LexicalEnvironmentObject::DISPOSABLE_RESOURCE_STACK_SLOT, 1020 UndefinedValue()); 1021 #endif 1022 1023 return env; 1024 } 1025 1026 bool LexicalEnvironmentObject::isExtensible() const { 1027 return NativeObject::isExtensible(); 1028 } 1029 1030 /* static */ 1031 BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::create( 1032 JSContext* cx, Handle<LexicalScope*> scope, HandleObject enclosing, 1033 gc::Heap heap, gc::AllocSite* site) { 1034 cx->check(enclosing); 1035 MOZ_ASSERT(scope->hasEnvironment()); 1036 1037 Rooted<SharedShape*> shape(cx, scope->environmentShape()); 1038 auto* env = static_cast<BlockLexicalEnvironmentObject*>( 1039 LexicalEnvironmentObject::create(cx, shape, enclosing, heap, site)); 1040 if (!env) { 1041 return nullptr; 1042 } 1043 1044 // All lexical bindings start off uninitialized for TDZ. 1045 uint32_t lastSlot = env->getLastProperty().slot(); 1046 for (uint32_t slot = JSSLOT_FREE(&class_); slot <= lastSlot; slot++) { 1047 env->initSlot(slot, MagicValue(JS_UNINITIALIZED_LEXICAL)); 1048 } 1049 1050 env->initScope(scope); 1051 return env; 1052 } 1053 1054 /* static */ 1055 BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::createForFrame( 1056 JSContext* cx, Handle<LexicalScope*> scope, AbstractFramePtr frame) { 1057 RootedObject enclosing(cx, frame.environmentChain()); 1058 return create(cx, scope, enclosing, gc::Heap::Default); 1059 } 1060 1061 /* static */ 1062 BlockLexicalEnvironmentObject* 1063 BlockLexicalEnvironmentObject::createHollowForDebug( 1064 JSContext* cx, Handle<LexicalScope*> scope) { 1065 MOZ_ASSERT(!scope->hasEnvironment()); 1066 1067 Rooted<SharedShape*> shape( 1068 cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx)); 1069 if (!shape) { 1070 return nullptr; 1071 } 1072 1073 // This environment's enclosing link is never used: the 1074 // DebugEnvironmentProxy that refers to this scope carries its own 1075 // enclosing link, which is what Debugger uses to construct the tree of 1076 // Debugger.Environment objects. 1077 RootedObject enclosingEnv(cx, &cx->global()->lexicalEnvironment()); 1078 Rooted<LexicalEnvironmentObject*> env( 1079 cx, LexicalEnvironmentObject::create(cx, shape, enclosingEnv, 1080 gc::Heap::Tenured)); 1081 if (!env) { 1082 return nullptr; 1083 } 1084 1085 RootedValue optimizedOut(cx, MagicValue(JS_OPTIMIZED_OUT)); 1086 RootedId id(cx); 1087 for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) { 1088 id = NameToId(bi.name()->asPropertyName()); 1089 if (!SetProperty(cx, env, id, optimizedOut)) { 1090 return nullptr; 1091 } 1092 } 1093 1094 if (!JSObject::setFlag(cx, env, ObjectFlag::NotExtensible)) { 1095 return nullptr; 1096 } 1097 1098 env->as<ScopedLexicalEnvironmentObject>().initScope(scope); 1099 return &env->as<BlockLexicalEnvironmentObject>(); 1100 } 1101 1102 /* static */ 1103 BlockLexicalEnvironmentObject* 1104 BlockLexicalEnvironmentObject::createTemplateObject( 1105 JSContext* cx, Handle<LexicalScope*> scope) { 1106 return create(cx, scope, nullptr, gc::Heap::Tenured); 1107 } 1108 1109 /* static */ 1110 BlockLexicalEnvironmentObject* 1111 BlockLexicalEnvironmentObject::createWithoutEnclosing( 1112 JSContext* cx, Handle<LexicalScope*> scope) { 1113 return create(cx, scope, nullptr, gc::Heap::Default); 1114 } 1115 1116 /* static */ 1117 BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::clone( 1118 JSContext* cx, Handle<BlockLexicalEnvironmentObject*> env) { 1119 Rooted<LexicalScope*> scope(cx, &env->scope()); 1120 RootedObject enclosing(cx, &env->enclosingEnvironment()); 1121 BlockLexicalEnvironmentObject* copy = 1122 create(cx, scope, enclosing, gc::Heap::Default); 1123 if (!copy) { 1124 return nullptr; 1125 } 1126 1127 MOZ_ASSERT(env->shape() == copy->shape()); 1128 for (uint32_t i = JSSLOT_FREE(&class_); i < copy->slotSpan(); i++) { 1129 copy->setSlot(i, env->getSlot(i)); 1130 } 1131 1132 return copy; 1133 } 1134 1135 /* static */ 1136 BlockLexicalEnvironmentObject* BlockLexicalEnvironmentObject::recreate( 1137 JSContext* cx, Handle<BlockLexicalEnvironmentObject*> env) { 1138 Rooted<LexicalScope*> scope(cx, &env->scope()); 1139 RootedObject enclosing(cx, &env->enclosingEnvironment()); 1140 return create(cx, scope, enclosing, gc::Heap::Default); 1141 } 1142 1143 /* static */ 1144 NamedLambdaObject* NamedLambdaObject::create(JSContext* cx, 1145 HandleFunction callee, 1146 HandleObject enclosing, 1147 gc::Heap heap, 1148 gc::AllocSite* site) { 1149 MOZ_ASSERT(callee->isNamedLambda()); 1150 Rooted<Scope*> scope(cx, callee->nonLazyScript()->maybeNamedLambdaScope()); 1151 MOZ_ASSERT(scope && scope->environmentShape()); 1152 1153 #ifdef DEBUG 1154 { 1155 // Named lambda objects have one (non-writable) property. 1156 SharedShapePropertyIter<NoGC> iter(scope->environmentShape()); 1157 MOZ_ASSERT(iter->slot() == lambdaSlot()); 1158 MOZ_ASSERT(!iter->writable()); 1159 iter++; 1160 MOZ_ASSERT(iter.done()); 1161 1162 // There should be exactly one binding in the named lambda scope. 1163 BindingIter bi(scope); 1164 bi++; 1165 MOZ_ASSERT(bi.done()); 1166 } 1167 #endif 1168 1169 BlockLexicalEnvironmentObject* obj = BlockLexicalEnvironmentObject::create( 1170 cx, scope.as<LexicalScope>(), enclosing, heap, site); 1171 if (!obj) { 1172 return nullptr; 1173 } 1174 1175 obj->initFixedSlot(lambdaSlot(), ObjectValue(*callee)); 1176 return static_cast<NamedLambdaObject*>(obj); 1177 } 1178 1179 /* static */ 1180 NamedLambdaObject* NamedLambdaObject::createTemplateObject( 1181 JSContext* cx, HandleFunction callee) { 1182 return create(cx, callee, nullptr, gc::Heap::Tenured); 1183 } 1184 1185 /* static */ 1186 NamedLambdaObject* NamedLambdaObject::createWithoutEnclosing( 1187 JSContext* cx, HandleFunction callee, gc::Heap heap) { 1188 return create(cx, callee, nullptr, heap); 1189 } 1190 1191 /* static */ 1192 NamedLambdaObject* NamedLambdaObject::createForFrame(JSContext* cx, 1193 AbstractFramePtr frame, 1194 gc::AllocSite* site) { 1195 RootedFunction fun(cx, frame.callee()); 1196 RootedObject enclosing(cx, frame.environmentChain()); 1197 gc::Heap heap = site ? site->initialHeap() : gc::Heap::Default; 1198 return create(cx, fun, enclosing, heap, site); 1199 } 1200 1201 /* static */ 1202 size_t NamedLambdaObject::lambdaSlot() { 1203 // Named lambda environments have exactly one name. 1204 return JSSLOT_FREE(&LexicalEnvironmentObject::class_); 1205 } 1206 1207 /* static */ 1208 ClassBodyLexicalEnvironmentObject* ClassBodyLexicalEnvironmentObject::create( 1209 JSContext* cx, Handle<ClassBodyScope*> scope, HandleObject enclosing, 1210 gc::Heap heap) { 1211 cx->check(enclosing); 1212 MOZ_ASSERT(scope->hasEnvironment()); 1213 1214 Rooted<SharedShape*> shape(cx, scope->environmentShape()); 1215 auto* env = static_cast<ClassBodyLexicalEnvironmentObject*>( 1216 LexicalEnvironmentObject::create(cx, shape, enclosing, heap)); 1217 if (!env) { 1218 return nullptr; 1219 } 1220 1221 env->initScope(scope); 1222 return env; 1223 } 1224 1225 /* static */ 1226 ClassBodyLexicalEnvironmentObject* 1227 ClassBodyLexicalEnvironmentObject::createForFrame(JSContext* cx, 1228 Handle<ClassBodyScope*> scope, 1229 AbstractFramePtr frame) { 1230 RootedObject enclosing(cx, frame.environmentChain()); 1231 return create(cx, scope, enclosing, gc::Heap::Default); 1232 } 1233 1234 /* static */ 1235 ClassBodyLexicalEnvironmentObject* 1236 ClassBodyLexicalEnvironmentObject::createTemplateObject( 1237 JSContext* cx, Handle<ClassBodyScope*> scope) { 1238 return create(cx, scope, nullptr, gc::Heap::Tenured); 1239 } 1240 1241 /* static */ 1242 ClassBodyLexicalEnvironmentObject* 1243 ClassBodyLexicalEnvironmentObject::createWithoutEnclosing( 1244 JSContext* cx, Handle<ClassBodyScope*> scope) { 1245 return create(cx, scope, nullptr, gc::Heap::Default); 1246 } 1247 1248 JSObject* ExtensibleLexicalEnvironmentObject::thisObject() const { 1249 JSObject* obj = &getReservedSlot(THIS_VALUE_OR_SCOPE_SLOT).toObject(); 1250 1251 // Windows must never be exposed to script. initThisObject should have set 1252 // this to the WindowProxy. 1253 MOZ_ASSERT(!IsWindow(obj)); 1254 1255 // WarpBuilder relies on the return value not being nursery-allocated for the 1256 // global lexical environment. 1257 MOZ_ASSERT_IF(isGlobal(), obj->isTenured()); 1258 1259 return obj; 1260 } 1261 1262 /* static */ 1263 ExtensibleLexicalEnvironmentObject* 1264 ExtensibleLexicalEnvironmentObject::forVarEnvironment(JSObject* obj) { 1265 ExtensibleLexicalEnvironmentObject* lexical = nullptr; 1266 if (obj->is<GlobalObject>()) { 1267 lexical = &obj->as<GlobalObject>().lexicalEnvironment(); 1268 } else { 1269 lexical = ObjectRealm::get(obj).getNonSyntacticLexicalEnvironment(obj); 1270 } 1271 MOZ_ASSERT(lexical); 1272 return lexical; 1273 } 1274 1275 /* static */ 1276 GlobalLexicalEnvironmentObject* GlobalLexicalEnvironmentObject::create( 1277 JSContext* cx, Handle<GlobalObject*> global) { 1278 MOZ_ASSERT(global); 1279 1280 Rooted<SharedShape*> shape( 1281 cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx)); 1282 if (!shape) { 1283 return nullptr; 1284 } 1285 1286 Rooted<GlobalLexicalEnvironmentObject*> env(cx); 1287 env = static_cast<GlobalLexicalEnvironmentObject*>( 1288 LexicalEnvironmentObject::create(cx, shape, global, gc::Heap::Tenured)); 1289 if (!env) { 1290 return nullptr; 1291 } 1292 1293 if (ShouldUseObjectFuses() && JS::Prefs::objectfuse_for_global()) { 1294 if (!NativeObject::setHasObjectFuse(cx, env)) { 1295 return nullptr; 1296 } 1297 } 1298 1299 env->initThisObject(global); 1300 return env; 1301 } 1302 1303 void GlobalLexicalEnvironmentObject::setWindowProxyThisObject(JSObject* obj) { 1304 MOZ_ASSERT(IsWindowProxy(obj)); 1305 setReservedSlot(THIS_VALUE_OR_SCOPE_SLOT, ObjectValue(*obj)); 1306 } 1307 1308 /* static */ 1309 NonSyntacticLexicalEnvironmentObject* 1310 NonSyntacticLexicalEnvironmentObject::create(JSContext* cx, 1311 HandleObject enclosing, 1312 HandleObject thisv) { 1313 MOZ_ASSERT(enclosing); 1314 MOZ_ASSERT(!IsSyntacticEnvironment(enclosing)); 1315 1316 Rooted<SharedShape*> shape( 1317 cx, LexicalScope::getEmptyExtensibleEnvironmentShape(cx)); 1318 if (!shape) { 1319 return nullptr; 1320 } 1321 1322 auto* env = static_cast<NonSyntacticLexicalEnvironmentObject*>( 1323 LexicalEnvironmentObject::create(cx, shape, enclosing, 1324 gc::Heap::Tenured)); 1325 if (!env) { 1326 return nullptr; 1327 } 1328 1329 env->initThisObject(thisv); 1330 1331 return env; 1332 } 1333 1334 /* static */ 1335 RuntimeLexicalErrorObject* RuntimeLexicalErrorObject::create( 1336 JSContext* cx, HandleObject enclosing, unsigned errorNumber) { 1337 Rooted<SharedShape*> shape( 1338 cx, 1339 EmptyEnvironmentShape(cx, &class_, JSSLOT_FREE(&class_), ObjectFlags())); 1340 if (!shape) { 1341 return nullptr; 1342 } 1343 1344 auto* obj = CreateEnvironmentObject<RuntimeLexicalErrorObject>(cx, shape); 1345 if (!obj) { 1346 return nullptr; 1347 } 1348 obj->initEnclosingEnvironment(enclosing); 1349 obj->initReservedSlot(ERROR_SLOT, Int32Value(int32_t(errorNumber))); 1350 1351 return obj; 1352 } 1353 1354 static void ReportRuntimeLexicalErrorId(JSContext* cx, unsigned errorNumber, 1355 HandleId id) { 1356 if (id.isAtom()) { 1357 Rooted<PropertyName*> name(cx, id.toAtom()->asPropertyName()); 1358 ReportRuntimeLexicalError(cx, errorNumber, name); 1359 return; 1360 } 1361 MOZ_CRASH( 1362 "RuntimeLexicalErrorObject should only be used with property names"); 1363 } 1364 1365 static bool lexicalError_LookupProperty(JSContext* cx, HandleObject obj, 1366 HandleId id, MutableHandleObject objp, 1367 PropertyResult* propp) { 1368 ReportRuntimeLexicalErrorId( 1369 cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id); 1370 return false; 1371 } 1372 1373 static bool lexicalError_HasProperty(JSContext* cx, HandleObject obj, 1374 HandleId id, bool* foundp) { 1375 ReportRuntimeLexicalErrorId( 1376 cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id); 1377 return false; 1378 } 1379 1380 static bool lexicalError_GetProperty(JSContext* cx, HandleObject obj, 1381 HandleValue receiver, HandleId id, 1382 MutableHandleValue vp) { 1383 ReportRuntimeLexicalErrorId( 1384 cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id); 1385 return false; 1386 } 1387 1388 static bool lexicalError_SetProperty(JSContext* cx, HandleObject obj, 1389 HandleId id, HandleValue v, 1390 HandleValue receiver, 1391 ObjectOpResult& result) { 1392 ReportRuntimeLexicalErrorId( 1393 cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id); 1394 return false; 1395 } 1396 1397 static bool lexicalError_GetOwnPropertyDescriptor( 1398 JSContext* cx, HandleObject obj, HandleId id, 1399 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) { 1400 ReportRuntimeLexicalErrorId( 1401 cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id); 1402 return false; 1403 } 1404 1405 static bool lexicalError_DeleteProperty(JSContext* cx, HandleObject obj, 1406 HandleId id, ObjectOpResult& result) { 1407 ReportRuntimeLexicalErrorId( 1408 cx, obj->as<RuntimeLexicalErrorObject>().errorNumber(), id); 1409 return false; 1410 } 1411 1412 static const ObjectOps RuntimeLexicalErrorObjectObjectOps = { 1413 lexicalError_LookupProperty, // lookupProperty 1414 nullptr, // defineProperty 1415 lexicalError_HasProperty, // hasProperty 1416 lexicalError_GetProperty, // getProperty 1417 lexicalError_SetProperty, // setProperty 1418 lexicalError_GetOwnPropertyDescriptor, // getOwnPropertyDescriptor 1419 lexicalError_DeleteProperty, // deleteProperty 1420 nullptr, // getElements 1421 nullptr, // funToString 1422 }; 1423 1424 const JSClass RuntimeLexicalErrorObject::class_ = { 1425 "RuntimeLexicalError", 1426 JSCLASS_HAS_RESERVED_SLOTS(RuntimeLexicalErrorObject::RESERVED_SLOTS), 1427 JS_NULL_CLASS_OPS, 1428 JS_NULL_CLASS_SPEC, 1429 JS_NULL_CLASS_EXT, 1430 &RuntimeLexicalErrorObjectObjectOps, 1431 }; 1432 1433 /*****************************************************************************/ 1434 1435 EnvironmentIter::EnvironmentIter(JSContext* cx, const EnvironmentIter& ei) 1436 : si_(cx, ei.si_.get()), env_(cx, ei.env_), frame_(ei.frame_) {} 1437 1438 EnvironmentIter::EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope) 1439 : si_(cx, ScopeIter(scope)), env_(cx, env), frame_(NullFramePtr()) { 1440 settle(); 1441 } 1442 1443 EnvironmentIter::EnvironmentIter(JSContext* cx, AbstractFramePtr frame, 1444 const jsbytecode* pc) 1445 : si_(cx, frame.script()->innermostScope(pc)), 1446 env_(cx, frame.environmentChain()), 1447 frame_(frame) { 1448 cx->check(frame); 1449 settle(); 1450 } 1451 1452 EnvironmentIter::EnvironmentIter(JSContext* cx, JSObject* env, Scope* scope, 1453 AbstractFramePtr frame) 1454 : si_(cx, ScopeIter(scope)), env_(cx, env), frame_(frame) { 1455 cx->check(frame); 1456 settle(); 1457 } 1458 1459 void EnvironmentIter::incrementScopeIter() { 1460 if (si_.scope()->is<GlobalScope>()) { 1461 // GlobalScopes may be syntactic or non-syntactic. Non-syntactic 1462 // GlobalScopes correspond to zero or more non-syntactic 1463 // EnvironmentsObjects followed by the global lexical scope, then the 1464 // GlobalObject or another non-EnvironmentObject object. 1465 if (!env_->is<EnvironmentObject>()) { 1466 si_++; 1467 } 1468 } else { 1469 si_++; 1470 } 1471 } 1472 1473 void EnvironmentIter::settle() { 1474 // Check for trying to iterate a function or eval frame before the prologue 1475 // has created the CallObject, in which case we have to skip. 1476 if (frame_ && frame_.hasScript() && 1477 frame_.script()->initialEnvironmentShape() && 1478 !frame_.hasInitialEnvironment()) { 1479 // Skip until we're at the enclosing scope of the script. 1480 while (si_.scope() != frame_.script()->enclosingScope()) { 1481 if (env_->is<BlockLexicalEnvironmentObject>() && 1482 &env_->as<BlockLexicalEnvironmentObject>().scope() == si_.scope()) { 1483 MOZ_ASSERT(si_.kind() == ScopeKind::NamedLambda || 1484 si_.kind() == ScopeKind::StrictNamedLambda); 1485 env_ = 1486 &env_->as<BlockLexicalEnvironmentObject>().enclosingEnvironment(); 1487 } 1488 incrementScopeIter(); 1489 } 1490 } 1491 1492 // Check if we have left the extent of the initial frame after we've 1493 // settled on a static scope. 1494 if (frame_ && 1495 (!si_ || 1496 (frame_.hasScript() && 1497 si_.scope() == frame_.script()->enclosingScope()) || 1498 (frame_.isWasmDebugFrame() && !si_.scope()->is<WasmFunctionScope>()))) { 1499 frame_ = NullFramePtr(); 1500 } 1501 1502 #ifdef DEBUG 1503 if (si_) { 1504 if (hasSyntacticEnvironment()) { 1505 Scope* scope = si_.scope(); 1506 if (scope->is<LexicalScope>()) { 1507 MOZ_ASSERT(scope == &env_->as<BlockLexicalEnvironmentObject>().scope()); 1508 } else if (scope->is<FunctionScope>()) { 1509 MOZ_ASSERT(scope->as<FunctionScope>().script() == 1510 env_->as<CallObject>() 1511 .callee() 1512 .maybeCanonicalFunction() 1513 ->baseScript()); 1514 } else if (scope->is<VarScope>()) { 1515 MOZ_ASSERT(scope == &env_->as<VarEnvironmentObject>().scope()); 1516 } else if (scope->is<WithScope>()) { 1517 MOZ_ASSERT(scope == &env_->as<WithEnvironmentObject>().scope()); 1518 } else if (scope->is<EvalScope>()) { 1519 MOZ_ASSERT(scope == &env_->as<VarEnvironmentObject>().scope()); 1520 } else if (scope->is<GlobalScope>()) { 1521 MOZ_ASSERT(env_->is<GlobalObject>() || 1522 env_->is<GlobalLexicalEnvironmentObject>()); 1523 } 1524 } else if (hasNonSyntacticEnvironmentObject()) { 1525 if (env_->is<LexicalEnvironmentObject>()) { 1526 // The global lexical environment still encloses non-syntactic 1527 // environment objects. 1528 MOZ_ASSERT(env_->is<NonSyntacticLexicalEnvironmentObject>() || 1529 env_->is<GlobalLexicalEnvironmentObject>()); 1530 } else if (env_->is<WithEnvironmentObject>()) { 1531 MOZ_ASSERT(!env_->as<WithEnvironmentObject>().isSyntactic()); 1532 } else { 1533 MOZ_ASSERT(env_->is<NonSyntacticVariablesObject>()); 1534 } 1535 } 1536 } 1537 #endif 1538 } 1539 1540 JSObject& EnvironmentIter::enclosingEnvironment() const { 1541 // As an engine invariant (maintained internally and asserted by Execute), 1542 // EnvironmentObjects and non-EnvironmentObjects cannot be interleaved on 1543 // the scope chain; every scope chain must start with zero or more 1544 // EnvironmentObjects and terminate with one or more 1545 // non-EnvironmentObjects (viz., GlobalObject). 1546 MOZ_ASSERT(done()); 1547 MOZ_ASSERT(!env_->is<EnvironmentObject>()); 1548 return *env_; 1549 } 1550 1551 bool EnvironmentIter::hasNonSyntacticEnvironmentObject() const { 1552 // The case we're worrying about here is a NonSyntactic static scope which 1553 // has 0+ corresponding non-syntactic WithEnvironmentObject scopes, a 1554 // NonSyntacticVariablesObject, or a NonSyntacticLexicalEnvironmentObject. 1555 if (si_.kind() == ScopeKind::NonSyntactic) { 1556 MOZ_ASSERT_IF(env_->is<WithEnvironmentObject>(), 1557 !env_->as<WithEnvironmentObject>().isSyntactic()); 1558 return env_->is<EnvironmentObject>(); 1559 } 1560 return false; 1561 } 1562 1563 bool MissingEnvironmentKey::initFromEnvironmentIter(JSContext* cx, 1564 const EnvironmentIter& ei) { 1565 frame_ = ei.maybeInitialFrame(); 1566 scope_ = ei.maybeScope(); 1567 if (frame_) { 1568 nearestEnvId_ = 0; 1569 return true; 1570 } 1571 1572 EnvironmentObject* env = nullptr; 1573 EnvironmentIter copy(cx, ei); 1574 while (copy) { 1575 if (copy.hasAnyEnvironmentObject()) { 1576 env = ©.environment(); 1577 break; 1578 } 1579 ++copy; 1580 } 1581 1582 // In general, we should find an environment object for the global etc even 1583 // if we don't find anything else. 1584 // 1585 // In certain situation where OOM and too much recursion happens and the 1586 // debugger is trying to recover from it, we might not find anything, and in 1587 // that case, there's nothing we can do. (see bug 1976630). 1588 if (!env) { 1589 ReportOutOfMemory(cx); 1590 return false; 1591 } 1592 1593 if (!gc::GetOrCreateUniqueId(env, &nearestEnvId_)) { 1594 ReportOutOfMemory(cx); 1595 return false; 1596 } 1597 1598 return true; 1599 } 1600 1601 /* static */ 1602 HashNumber MissingEnvironmentKey::hash(MissingEnvironmentKey ek) { 1603 return mozilla::HashGeneric(ek.frame_.raw(), ek.nearestEnvId_, ek.scope_); 1604 } 1605 1606 /* static */ 1607 bool MissingEnvironmentKey::match(MissingEnvironmentKey ek1, 1608 MissingEnvironmentKey ek2) { 1609 return ek1.frame_ == ek2.frame_ && ek1.nearestEnvId_ == ek2.nearestEnvId_ && 1610 ek1.scope_ == ek2.scope_; 1611 } 1612 1613 bool LiveEnvironmentVal::traceWeak(JSTracer* trc) { 1614 return TraceWeakEdge(trc, &scope_, "LiveEnvironmentVal::scope_"); 1615 } 1616 1617 // Live EnvironmentIter values may be added to DebugEnvironments::liveEnvs, as 1618 // LiveEnvironmentVal instances. They need to have write barriers when they are 1619 // added to the hash table, but no barriers when rehashing inside GC. It's a 1620 // nasty hack, but the important thing is that LiveEnvironmentVal and 1621 // MissingEnvironmentKey need to alias each other. 1622 void LiveEnvironmentVal::staticAsserts() { 1623 static_assert( 1624 sizeof(LiveEnvironmentVal) == sizeof(MissingEnvironmentKey), 1625 "LiveEnvironmentVal must be same size of MissingEnvironmentKey"); 1626 static_assert( 1627 offsetof(LiveEnvironmentVal, scope_) == 1628 offsetof(MissingEnvironmentKey, scope_), 1629 "LiveEnvironmentVal.scope_ must alias MissingEnvironmentKey.scope_"); 1630 } 1631 1632 /*****************************************************************************/ 1633 1634 namespace { 1635 1636 /* 1637 * DebugEnvironmentProxy is the handler for DebugEnvironmentProxy proxy 1638 * objects. Having a custom handler (rather than trying to reuse js::Wrapper) 1639 * gives us several important abilities: 1640 * - We want to pass the EnvironmentObject as the receiver to forwarded scope 1641 * property ops on aliased variables so that Call/Block/With ops do not all 1642 * require a 'normalization' step. 1643 * - The debug scope proxy can directly manipulate the stack frame to allow 1644 * the debugger to read/write args/locals that were otherwise unaliased. 1645 * - The debug scope proxy can store unaliased variables after the stack frame 1646 * is popped so that they may still be read/written by the debugger. 1647 * - The engine has made certain assumptions about the possible reads/writes 1648 * in a scope. DebugEnvironmentProxy allows us to prevent the debugger from 1649 * breaking those assumptions. 1650 * - The engine makes optimizations that are observable to the debugger. The 1651 * proxy can either hide these optimizations or make the situation more 1652 * clear to the debugger. An example is 'arguments'. 1653 */ 1654 class DebugEnvironmentProxyHandler : public NurseryAllocableProxyHandler { 1655 enum Action { SET, GET }; 1656 1657 enum AccessResult { ACCESS_UNALIASED, ACCESS_GENERIC, ACCESS_LOST }; 1658 1659 /* 1660 * This function handles access to unaliased locals/formals. Since they 1661 * are unaliased, the values of these variables are not stored in the 1662 * slots of the normal CallObject and BlockLexicalEnvironmentObject 1663 * environments and thus must be recovered from somewhere else: 1664 * + if the invocation for which the env was created is still executing, 1665 * there is a JS frame live on the stack holding the values; 1666 * + if the invocation for which the env was created finished executing: 1667 * - and there was a DebugEnvironmentProxy associated with env, then 1668 * the DebugEnvironments::onPop(Call|Lexical) handler copied out the 1669 * unaliased variables. In both cases, a dense array is created in 1670 * onPop(Call|Lexical) to hold the unaliased values and attached to 1671 * the DebugEnvironmentProxy; 1672 * - and there was not a DebugEnvironmentProxy yet associated with the 1673 * scope, then the unaliased values are lost and not recoverable. 1674 * 1675 * Callers should check accessResult for non-failure results: 1676 * - ACCESS_UNALIASED if the access was unaliased and completed 1677 * - ACCESS_GENERIC if the access was aliased or the property not found 1678 * - ACCESS_LOST if the value has been lost to the debugger and the 1679 * action is GET; if the action is SET, we assign to the 1680 * name of the variable on the environment object 1681 */ 1682 bool handleUnaliasedAccess(JSContext* cx, 1683 Handle<DebugEnvironmentProxy*> debugEnv, 1684 Handle<EnvironmentObject*> env, HandleId id, 1685 Action action, MutableHandleValue vp, 1686 AccessResult* accessResult) const { 1687 MOZ_ASSERT(&debugEnv->environment() == env); 1688 MOZ_ASSERT_IF(action == SET, !debugEnv->isOptimizedOut()); 1689 *accessResult = ACCESS_GENERIC; 1690 LiveEnvironmentVal* maybeLiveEnv = 1691 DebugEnvironments::hasLiveEnvironment(*env); 1692 1693 // Handle unaliased formals, vars, lets, and consts at function or module 1694 // scope. 1695 if (env->is<CallObject>() || env->is<ModuleEnvironmentObject>()) { 1696 RootedScript script(cx); 1697 if (env->is<CallObject>()) { 1698 CallObject& callobj = env->as<CallObject>(); 1699 RootedFunction fun(cx, &callobj.callee()); 1700 script = JSFunction::getOrCreateScript(cx, fun); 1701 } else { 1702 script = env->as<ModuleEnvironmentObject>().module().maybeScript(); 1703 if (!script) { 1704 return true; 1705 } 1706 } 1707 1708 BindingIter bi(script); 1709 while (bi && NameToId(bi.name()->asPropertyName()) != id) { 1710 bi++; 1711 } 1712 if (!bi) { 1713 return true; 1714 } 1715 1716 if (action == SET && bi.kind() == BindingKind::Const) { 1717 ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, id); 1718 return false; 1719 } 1720 1721 if (bi.location().kind() == BindingLocation::Kind::Import) { 1722 return true; 1723 } 1724 1725 if (!bi.hasArgumentSlot()) { 1726 if (bi.closedOver()) { 1727 return true; 1728 } 1729 1730 uint32_t i = bi.location().slot(); 1731 if (maybeLiveEnv) { 1732 AbstractFramePtr frame = maybeLiveEnv->frame(); 1733 if (action == GET) { 1734 vp.set(frame.unaliasedLocal(i)); 1735 } else { 1736 frame.unaliasedLocal(i) = vp; 1737 } 1738 } else if (AbstractGeneratorObject* genObj = 1739 GetGeneratorObjectForEnvironment(cx, env); 1740 genObj && genObj->isSuspended() && 1741 genObj->hasStackStorage()) { 1742 if (action == GET) { 1743 vp.set(genObj->getUnaliasedLocal(i)); 1744 } else { 1745 genObj->setUnaliasedLocal(i, vp); 1746 } 1747 } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) { 1748 if (action == GET) { 1749 vp.set(snapshot->getDenseElement(script->numArgs() + i)); 1750 } else { 1751 snapshot->setDenseElement(script->numArgs() + i, vp); 1752 } 1753 } else { 1754 /* The unaliased value has been lost to the debugger. */ 1755 if (action == GET) { 1756 *accessResult = ACCESS_LOST; 1757 return true; 1758 } 1759 } 1760 } else { 1761 unsigned i = bi.argumentSlot(); 1762 if (bi.closedOver()) { 1763 return true; 1764 } 1765 1766 if (maybeLiveEnv) { 1767 AbstractFramePtr frame = maybeLiveEnv->frame(); 1768 if (script->argsObjAliasesFormals() && frame.hasArgsObj()) { 1769 if (action == GET) { 1770 vp.set(frame.argsObj().arg(i)); 1771 } else { 1772 frame.argsObj().setArg(i, vp); 1773 } 1774 } else { 1775 if (action == GET) { 1776 vp.set(frame.unaliasedFormal(i, DONT_CHECK_ALIASING)); 1777 } else { 1778 frame.unaliasedFormal(i, DONT_CHECK_ALIASING) = vp; 1779 } 1780 } 1781 } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) { 1782 if (action == GET) { 1783 vp.set(snapshot->getDenseElement(i)); 1784 } else { 1785 snapshot->setDenseElement(i, vp); 1786 } 1787 } else { 1788 /* The unaliased value has been lost to the debugger. */ 1789 if (action == GET) { 1790 *accessResult = ACCESS_LOST; 1791 return true; 1792 } 1793 } 1794 } 1795 1796 // It is possible that an optimized out value flows to this 1797 // location due to Debugger.Frame.prototype.eval operating on a 1798 // live bailed-out Baseline frame. In that case, treat the access 1799 // as lost. 1800 if (vp.isMagic() && vp.whyMagic() == JS_OPTIMIZED_OUT) { 1801 *accessResult = ACCESS_LOST; 1802 } else { 1803 *accessResult = ACCESS_UNALIASED; 1804 } 1805 1806 return true; 1807 } 1808 1809 /* 1810 * Handle unaliased vars in functions with parameter expressions and 1811 * lexical bindings at block scope. 1812 */ 1813 if (env->is<LexicalEnvironmentObject>() || 1814 env->is<VarEnvironmentObject>()) { 1815 // Currently consider all non-syntactic top-level lexical bindings to be 1816 // aliased. 1817 if (env->is<LexicalEnvironmentObject>() && 1818 !env->is<GlobalLexicalEnvironmentObject>() && 1819 env->as<LexicalEnvironmentObject>().isExtensible()) { 1820 MOZ_ASSERT(!IsSyntacticEnvironment(env)); 1821 return true; 1822 } 1823 1824 // Currently all vars inside non-strict eval var environments are aliased. 1825 if (env->is<VarEnvironmentObject>() && 1826 env->as<VarEnvironmentObject>().isForNonStrictEval()) { 1827 return true; 1828 } 1829 1830 Rooted<Scope*> scope(cx, getEnvironmentScope(*env)); 1831 uint32_t firstFrameSlot = scope->firstFrameSlot(); 1832 1833 BindingIter bi(scope); 1834 while (bi && NameToId(bi.name()->asPropertyName()) != id) { 1835 bi++; 1836 } 1837 if (!bi) { 1838 return true; 1839 } 1840 1841 if (action == SET && bi.kind() == BindingKind::Const) { 1842 ReportRuntimeLexicalError(cx, JSMSG_BAD_CONST_ASSIGN, id); 1843 return false; 1844 } 1845 1846 BindingLocation loc = bi.location(); 1847 if (loc.kind() == BindingLocation::Kind::Environment) { 1848 return true; 1849 } 1850 1851 // Named lambdas that are not closed over are lost. 1852 if (loc.kind() == BindingLocation::Kind::NamedLambdaCallee) { 1853 if (action == GET) { 1854 *accessResult = ACCESS_LOST; 1855 } 1856 return true; 1857 } 1858 1859 MOZ_ASSERT(loc.kind() == BindingLocation::Kind::Frame); 1860 1861 if (maybeLiveEnv) { 1862 AbstractFramePtr frame = maybeLiveEnv->frame(); 1863 uint32_t local = loc.slot(); 1864 MOZ_ASSERT(local < frame.script()->nfixed()); 1865 Value& localVal = frame.unaliasedLocal(local); 1866 if (action == GET) { 1867 vp.set(localVal); 1868 } else { 1869 // Note: localVal could also be JS_OPTIMIZED_OUT. 1870 if (localVal.isMagic() && 1871 localVal.whyMagic() == JS_UNINITIALIZED_LEXICAL) { 1872 ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id); 1873 return false; 1874 } 1875 1876 localVal = vp; 1877 } 1878 } else if (AbstractGeneratorObject* genObj = 1879 GetGeneratorObjectForEnvironment(cx, debugEnv); 1880 genObj && genObj->isSuspended() && genObj->hasStackStorage()) { 1881 if (action == GET) { 1882 vp.set(genObj->getUnaliasedLocal(loc.slot())); 1883 } else { 1884 genObj->setUnaliasedLocal(loc.slot(), vp); 1885 } 1886 } else if (NativeObject* snapshot = debugEnv->maybeSnapshot()) { 1887 // Indices in the frame snapshot are offset by the first frame 1888 // slot. See DebugEnvironments::takeFrameSnapshot. 1889 MOZ_ASSERT(loc.slot() >= firstFrameSlot); 1890 uint32_t snapshotIndex = loc.slot() - firstFrameSlot; 1891 if (action == GET) { 1892 vp.set(snapshot->getDenseElement(snapshotIndex)); 1893 } else { 1894 snapshot->setDenseElement(snapshotIndex, vp); 1895 } 1896 } else { 1897 if (action == GET) { 1898 // A {Lexical,Var}EnvironmentObject whose static scope 1899 // does not have an environment shape at all is a "hollow" 1900 // block object reflected for missing block scopes. Their 1901 // slot values are lost. 1902 if (!scope->hasEnvironment()) { 1903 *accessResult = ACCESS_LOST; 1904 return true; 1905 } 1906 1907 if (!GetProperty(cx, env, env, id, vp)) { 1908 return false; 1909 } 1910 } else { 1911 if (!SetProperty(cx, env, id, vp)) { 1912 return false; 1913 } 1914 } 1915 } 1916 1917 // See comment above in analogous CallObject case. 1918 if (vp.isMagic() && vp.whyMagic() == JS_OPTIMIZED_OUT) { 1919 *accessResult = ACCESS_LOST; 1920 } else { 1921 *accessResult = ACCESS_UNALIASED; 1922 } 1923 1924 return true; 1925 } 1926 1927 if (env->is<WasmFunctionCallObject>()) { 1928 if (maybeLiveEnv) { 1929 Rooted<Scope*> scope(cx, getEnvironmentScope(*env)); 1930 uint32_t index = 0; 1931 for (BindingIter bi(scope); bi; bi++) { 1932 if (id.isAtom(bi.name())) { 1933 break; 1934 } 1935 MOZ_ASSERT(!bi.isLast()); 1936 index++; 1937 } 1938 1939 AbstractFramePtr frame = maybeLiveEnv->frame(); 1940 MOZ_ASSERT(frame.isWasmDebugFrame()); 1941 wasm::DebugFrame* wasmFrame = frame.asWasmDebugFrame(); 1942 if (action == GET) { 1943 if (!wasmFrame->getLocal(index, vp)) { 1944 ReportOutOfMemory(cx); 1945 return false; 1946 } 1947 *accessResult = ACCESS_UNALIASED; 1948 } else { // if (action == SET) 1949 // TODO 1950 } 1951 } else { 1952 *accessResult = ACCESS_LOST; 1953 } 1954 return true; 1955 } 1956 1957 if (env->is<WasmInstanceEnvironmentObject>()) { 1958 Rooted<Scope*> scope(cx, getEnvironmentScope(*env)); 1959 MOZ_ASSERT(scope->is<WasmInstanceScope>()); 1960 uint32_t index = 0; 1961 for (BindingIter bi(scope); bi; bi++) { 1962 if (id.isAtom(bi.name())) { 1963 break; 1964 } 1965 MOZ_ASSERT(!bi.isLast()); 1966 index++; 1967 } 1968 Rooted<WasmInstanceScope*> instanceScope(cx, 1969 &scope->as<WasmInstanceScope>()); 1970 wasm::Instance& instance = instanceScope->instance()->instance(); 1971 1972 if (action == GET) { 1973 if (instanceScope->memoriesStart() <= index && 1974 index < instanceScope->globalsStart()) { 1975 vp.set(ObjectValue( 1976 *instance.memory(index - instanceScope->memoriesStart()))); 1977 } 1978 if (instanceScope->globalsStart() <= index) { 1979 MOZ_ASSERT(index < instanceScope->namesCount()); 1980 if (!instance.debug().getGlobal( 1981 instance, index - instanceScope->globalsStart(), vp)) { 1982 ReportOutOfMemory(cx); 1983 return false; 1984 } 1985 } 1986 *accessResult = ACCESS_UNALIASED; 1987 } else { // if (action == SET) 1988 // TODO 1989 } 1990 return true; 1991 } 1992 1993 /* The rest of the internal scopes do not have unaliased vars. */ 1994 MOZ_ASSERT(!IsSyntacticEnvironment(env) || 1995 env->is<WithEnvironmentObject>()); 1996 return true; 1997 } 1998 1999 static bool isArguments(JSContext* cx, jsid id) { 2000 return id == NameToId(cx->names().arguments); 2001 } 2002 static bool isThis(JSContext* cx, jsid id) { 2003 return id == NameToId(cx->names().dot_this_); 2004 } 2005 2006 static bool isFunctionEnvironment(const JSObject& env) { 2007 return env.is<CallObject>(); 2008 } 2009 2010 static bool isNonExtensibleLexicalEnvironment(const JSObject& env) { 2011 return env.is<ScopedLexicalEnvironmentObject>(); 2012 } 2013 2014 static Scope* getEnvironmentScope(const JSObject& env) { 2015 if (isFunctionEnvironment(env)) { 2016 return env.as<CallObject>().callee().nonLazyScript()->bodyScope(); 2017 } 2018 if (env.is<ModuleEnvironmentObject>()) { 2019 JSScript* script = 2020 env.as<ModuleEnvironmentObject>().module().maybeScript(); 2021 return script ? script->bodyScope() : nullptr; 2022 } 2023 if (isNonExtensibleLexicalEnvironment(env)) { 2024 return &env.as<ScopedLexicalEnvironmentObject>().scope(); 2025 } 2026 if (env.is<VarEnvironmentObject>()) { 2027 return &env.as<VarEnvironmentObject>().scope(); 2028 } 2029 if (env.is<WasmInstanceEnvironmentObject>()) { 2030 return &env.as<WasmInstanceEnvironmentObject>().scope(); 2031 } 2032 if (env.is<WasmFunctionCallObject>()) { 2033 return &env.as<WasmFunctionCallObject>().scope(); 2034 } 2035 if (env.is<GlobalLexicalEnvironmentObject>()) { 2036 return &env.as<GlobalLexicalEnvironmentObject>() 2037 .global() 2038 .emptyGlobalScope(); 2039 } 2040 return nullptr; 2041 } 2042 2043 friend Scope* js::GetEnvironmentScope(const JSObject& env); 2044 2045 /* 2046 * In theory, every non-arrow function scope contains an 'arguments' 2047 * bindings. However, the engine only adds a binding if 'arguments' is 2048 * used in the function body. Thus, from the debugger's perspective, 2049 * 'arguments' may be missing from the list of bindings. 2050 */ 2051 static bool isMissingArgumentsBinding(EnvironmentObject& env) { 2052 return isFunctionEnvironment(env) && 2053 !env.as<CallObject>().callee().baseScript()->needsArgsObj(); 2054 } 2055 2056 /* 2057 * Similar to 'arguments' above, we don't add a 'this' binding to 2058 * non-arrow functions if it's not used. 2059 */ 2060 static bool isMissingThisBinding(EnvironmentObject& env) { 2061 return isFunctionEnvironmentWithThis(env) && 2062 !env.as<CallObject>() 2063 .callee() 2064 .baseScript() 2065 ->functionHasThisBinding(); 2066 } 2067 2068 /* 2069 * This function checks if an arguments object needs to be created when 2070 * the debugger requests 'arguments' for a function scope where the 2071 * arguments object was not otherwise needed. 2072 */ 2073 static bool isMissingArguments(JSContext* cx, jsid id, 2074 EnvironmentObject& env) { 2075 return isArguments(cx, id) && isMissingArgumentsBinding(env); 2076 } 2077 static bool isMissingThis(JSContext* cx, jsid id, EnvironmentObject& env) { 2078 return isThis(cx, id) && isMissingThisBinding(env); 2079 } 2080 2081 /* 2082 * If the value of |this| is requested before the this-binding has been 2083 * initialized by JSOp::FunctionThis, the this-binding will be |undefined|. 2084 * In that case, we have to call createMissingThis to initialize the 2085 * this-binding. 2086 * 2087 * Note that an |undefined| this-binding is perfectly valid in strict-mode 2088 * code, but that's fine: createMissingThis will do the right thing in that 2089 * case. 2090 */ 2091 static bool isMaybeUninitializedThisValue(JSContext* cx, jsid id, 2092 const Value& v) { 2093 return isThis(cx, id) && v.isUndefined(); 2094 } 2095 2096 /* 2097 * Create a missing arguments object. If the function returns true but 2098 * argsObj is null, it means the env is dead. 2099 */ 2100 static bool createMissingArguments(JSContext* cx, EnvironmentObject& env, 2101 MutableHandle<ArgumentsObject*> argsObj) { 2102 argsObj.set(nullptr); 2103 2104 LiveEnvironmentVal* maybeEnv = DebugEnvironments::hasLiveEnvironment(env); 2105 if (!maybeEnv) { 2106 return true; 2107 } 2108 2109 argsObj.set(ArgumentsObject::createUnexpected(cx, maybeEnv->frame())); 2110 return !!argsObj; 2111 } 2112 2113 /* 2114 * Create a missing this Value. If the function returns true but 2115 * *success is false, it means the scope is dead. 2116 */ 2117 static bool createMissingThis(JSContext* cx, EnvironmentObject& env, 2118 MutableHandleValue thisv, bool* success) { 2119 *success = false; 2120 2121 LiveEnvironmentVal* maybeEnv = DebugEnvironments::hasLiveEnvironment(env); 2122 if (!maybeEnv) { 2123 return true; 2124 } 2125 2126 AbstractFramePtr frame = maybeEnv->frame(); 2127 if (!GetFunctionThis(cx, frame, thisv)) { 2128 return false; 2129 } 2130 2131 // Update the this-argument to avoid boxing primitive |this| more 2132 // than once. 2133 frame.thisArgument() = thisv; 2134 *success = true; 2135 return true; 2136 } 2137 2138 static void reportOptimizedOut(JSContext* cx, HandleId id) { 2139 if (isThis(cx, id)) { 2140 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2141 JSMSG_DEBUG_OPTIMIZED_OUT, "this"); 2142 return; 2143 } 2144 2145 if (UniqueChars printable = 2146 IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) { 2147 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2148 JSMSG_DEBUG_OPTIMIZED_OUT, printable.get()); 2149 } 2150 } 2151 2152 public: 2153 static const char family; 2154 static const DebugEnvironmentProxyHandler singleton; 2155 2156 constexpr DebugEnvironmentProxyHandler() 2157 : NurseryAllocableProxyHandler(&family) {} 2158 2159 static bool isFunctionEnvironmentWithThis(const JSObject& env) { 2160 // All functions except arrows should have their own this binding. 2161 return isFunctionEnvironment(env) && 2162 !env.as<CallObject>().callee().hasLexicalThis(); 2163 } 2164 2165 bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, 2166 bool* isOrdinary, 2167 MutableHandleObject protop) const override { 2168 MOZ_CRASH( 2169 "shouldn't be possible to access the prototype chain of a " 2170 "DebugEnvironmentProxyHandler"); 2171 } 2172 2173 bool preventExtensions(JSContext* cx, HandleObject proxy, 2174 ObjectOpResult& result) const override { 2175 // always [[Extensible]], can't be made non-[[Extensible]], like most 2176 // proxies 2177 return result.fail(JSMSG_CANT_CHANGE_EXTENSIBILITY); 2178 } 2179 2180 bool isExtensible(JSContext* cx, HandleObject proxy, 2181 bool* extensible) const override { 2182 // See above. 2183 *extensible = true; 2184 return true; 2185 } 2186 2187 bool getMissingArgumentsPropertyDescriptor( 2188 JSContext* cx, Handle<DebugEnvironmentProxy*> debugEnv, 2189 EnvironmentObject& env, 2190 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const { 2191 Rooted<ArgumentsObject*> argsObj(cx); 2192 if (!createMissingArguments(cx, env, &argsObj)) { 2193 return false; 2194 } 2195 2196 if (!argsObj) { 2197 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2198 JSMSG_DEBUG_NOT_ON_STACK, "Debugger scope"); 2199 return false; 2200 } 2201 2202 desc.set(mozilla::Some(PropertyDescriptor::Data( 2203 ObjectValue(*argsObj), {JS::PropertyAttribute::Enumerable}))); 2204 return true; 2205 } 2206 bool getMissingThisPropertyDescriptor( 2207 JSContext* cx, Handle<DebugEnvironmentProxy*> debugEnv, 2208 EnvironmentObject& env, 2209 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const { 2210 RootedValue thisv(cx); 2211 bool success; 2212 if (!createMissingThis(cx, env, &thisv, &success)) { 2213 return false; 2214 } 2215 2216 if (!success) { 2217 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2218 JSMSG_DEBUG_NOT_ON_STACK, "Debugger scope"); 2219 return false; 2220 } 2221 2222 desc.set(mozilla::Some( 2223 PropertyDescriptor::Data(thisv, {JS::PropertyAttribute::Enumerable}))); 2224 return true; 2225 } 2226 2227 bool getOwnPropertyDescriptor( 2228 JSContext* cx, HandleObject proxy, HandleId id, 2229 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) const override { 2230 Rooted<DebugEnvironmentProxy*> debugEnv( 2231 cx, &proxy->as<DebugEnvironmentProxy>()); 2232 Rooted<EnvironmentObject*> env(cx, &debugEnv->environment()); 2233 2234 if (isMissingArguments(cx, id, *env)) { 2235 return getMissingArgumentsPropertyDescriptor(cx, debugEnv, *env, desc); 2236 } 2237 2238 if (isMissingThis(cx, id, *env)) { 2239 return getMissingThisPropertyDescriptor(cx, debugEnv, *env, desc); 2240 } 2241 2242 RootedValue v(cx); 2243 AccessResult access; 2244 if (!handleUnaliasedAccess(cx, debugEnv, env, id, GET, &v, &access)) { 2245 return false; 2246 } 2247 2248 switch (access) { 2249 case ACCESS_UNALIASED: { 2250 desc.set(mozilla::Some( 2251 PropertyDescriptor::Data(v, {JS::PropertyAttribute::Enumerable}))); 2252 return true; 2253 } 2254 case ACCESS_GENERIC: 2255 return GetOwnPropertyDescriptor(cx, env, id, desc); 2256 case ACCESS_LOST: 2257 reportOptimizedOut(cx, id); 2258 return false; 2259 default: 2260 MOZ_CRASH("bad AccessResult"); 2261 } 2262 } 2263 2264 bool getMissingArguments(JSContext* cx, EnvironmentObject& env, 2265 MutableHandleValue vp) const { 2266 Rooted<ArgumentsObject*> argsObj(cx); 2267 if (!createMissingArguments(cx, env, &argsObj)) { 2268 return false; 2269 } 2270 2271 if (!argsObj) { 2272 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2273 JSMSG_DEBUG_NOT_ON_STACK, "Debugger env"); 2274 return false; 2275 } 2276 2277 vp.setObject(*argsObj); 2278 return true; 2279 } 2280 2281 bool getMissingThis(JSContext* cx, EnvironmentObject& env, 2282 MutableHandleValue vp) const { 2283 RootedValue thisv(cx); 2284 bool success; 2285 if (!createMissingThis(cx, env, &thisv, &success)) { 2286 return false; 2287 } 2288 2289 if (!success) { 2290 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2291 JSMSG_DEBUG_NOT_ON_STACK, "Debugger env"); 2292 return false; 2293 } 2294 2295 vp.set(thisv); 2296 return true; 2297 } 2298 2299 bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id, 2300 MutableHandleValue vp) const override { 2301 Rooted<DebugEnvironmentProxy*> debugEnv( 2302 cx, &proxy->as<DebugEnvironmentProxy>()); 2303 Rooted<EnvironmentObject*> env( 2304 cx, &proxy->as<DebugEnvironmentProxy>().environment()); 2305 2306 if (isMissingArguments(cx, id, *env)) { 2307 return getMissingArguments(cx, *env, vp); 2308 } 2309 2310 if (isMissingThis(cx, id, *env)) { 2311 return getMissingThis(cx, *env, vp); 2312 } 2313 2314 AccessResult access; 2315 if (!handleUnaliasedAccess(cx, debugEnv, env, id, GET, vp, &access)) { 2316 return false; 2317 } 2318 2319 switch (access) { 2320 case ACCESS_UNALIASED: 2321 if (isMaybeUninitializedThisValue(cx, id, vp)) { 2322 return getMissingThis(cx, *env, vp); 2323 } 2324 return true; 2325 case ACCESS_GENERIC: 2326 if (!GetProperty(cx, env, env, id, vp)) { 2327 return false; 2328 } 2329 if (isMaybeUninitializedThisValue(cx, id, vp)) { 2330 return getMissingThis(cx, *env, vp); 2331 } 2332 return true; 2333 case ACCESS_LOST: 2334 reportOptimizedOut(cx, id); 2335 return false; 2336 default: 2337 MOZ_CRASH("bad AccessResult"); 2338 } 2339 } 2340 2341 bool getMissingArgumentsMaybeSentinelValue(JSContext* cx, 2342 EnvironmentObject& env, 2343 MutableHandleValue vp) const { 2344 Rooted<ArgumentsObject*> argsObj(cx); 2345 if (!createMissingArguments(cx, env, &argsObj)) { 2346 return false; 2347 } 2348 vp.set(argsObj ? ObjectValue(*argsObj) : MagicValue(JS_MISSING_ARGUMENTS)); 2349 return true; 2350 } 2351 2352 bool getMissingThisMaybeSentinelValue(JSContext* cx, EnvironmentObject& env, 2353 MutableHandleValue vp) const { 2354 RootedValue thisv(cx); 2355 bool success; 2356 if (!createMissingThis(cx, env, &thisv, &success)) { 2357 return false; 2358 } 2359 vp.set(success ? thisv : MagicValue(JS_OPTIMIZED_OUT)); 2360 return true; 2361 } 2362 2363 /* 2364 * Like 'get', but returns sentinel values instead of throwing on 2365 * exceptional cases. 2366 */ 2367 bool getMaybeSentinelValue(JSContext* cx, 2368 Handle<DebugEnvironmentProxy*> debugEnv, 2369 HandleId id, MutableHandleValue vp) const { 2370 Rooted<EnvironmentObject*> env(cx, &debugEnv->environment()); 2371 2372 if (isMissingArguments(cx, id, *env)) { 2373 return getMissingArgumentsMaybeSentinelValue(cx, *env, vp); 2374 } 2375 if (isMissingThis(cx, id, *env)) { 2376 return getMissingThisMaybeSentinelValue(cx, *env, vp); 2377 } 2378 2379 AccessResult access; 2380 if (!handleUnaliasedAccess(cx, debugEnv, env, id, GET, vp, &access)) { 2381 return false; 2382 } 2383 2384 switch (access) { 2385 case ACCESS_UNALIASED: 2386 if (isMaybeUninitializedThisValue(cx, id, vp)) { 2387 return getMissingThisMaybeSentinelValue(cx, *env, vp); 2388 } 2389 return true; 2390 case ACCESS_GENERIC: 2391 if (!GetProperty(cx, env, env, id, vp)) { 2392 return false; 2393 } 2394 if (isMaybeUninitializedThisValue(cx, id, vp)) { 2395 return getMissingThisMaybeSentinelValue(cx, *env, vp); 2396 } 2397 return true; 2398 case ACCESS_LOST: 2399 vp.setMagic(JS_OPTIMIZED_OUT); 2400 return true; 2401 default: 2402 MOZ_CRASH("bad AccessResult"); 2403 } 2404 } 2405 2406 bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, 2407 HandleValue receiver, ObjectOpResult& result) const override { 2408 Rooted<DebugEnvironmentProxy*> debugEnv( 2409 cx, &proxy->as<DebugEnvironmentProxy>()); 2410 Rooted<EnvironmentObject*> env( 2411 cx, &proxy->as<DebugEnvironmentProxy>().environment()); 2412 2413 if (debugEnv->isOptimizedOut()) { 2414 return Throw(cx, id, JSMSG_DEBUG_CANT_SET_OPT_ENV); 2415 } 2416 2417 AccessResult access; 2418 RootedValue valCopy(cx, v); 2419 if (!handleUnaliasedAccess(cx, debugEnv, env, id, SET, &valCopy, &access)) { 2420 return false; 2421 } 2422 2423 switch (access) { 2424 case ACCESS_UNALIASED: 2425 return result.succeed(); 2426 case ACCESS_GENERIC: { 2427 RootedValue envVal(cx, ObjectValue(*env)); 2428 RootedValue initialVal(cx); 2429 if (!GetProperty(cx, env, env, id, &initialVal)) { 2430 return false; 2431 } 2432 // Note: initialVal could be JS_OPTIMIZED_OUT, which is why we don't use 2433 // .whyMagic(JS_UNINITALIZED_LEXICAL). 2434 if (initialVal.isMagic() && 2435 initialVal.whyMagic() == JS_UNINITIALIZED_LEXICAL) { 2436 ReportRuntimeLexicalErrorId(cx, JSMSG_UNINITIALIZED_LEXICAL, id); 2437 return false; 2438 } 2439 2440 return SetProperty(cx, env, id, v, envVal, result); 2441 } 2442 default: 2443 MOZ_CRASH("bad AccessResult"); 2444 } 2445 } 2446 2447 bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, 2448 Handle<PropertyDescriptor> desc, 2449 ObjectOpResult& result) const override { 2450 Rooted<EnvironmentObject*> env( 2451 cx, &proxy->as<DebugEnvironmentProxy>().environment()); 2452 2453 bool found; 2454 if (!has(cx, proxy, id, &found)) { 2455 return false; 2456 } 2457 if (found) { 2458 return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP); 2459 } 2460 2461 return JS_DefinePropertyById(cx, env, id, desc, result); 2462 } 2463 2464 bool ownPropertyKeys(JSContext* cx, HandleObject proxy, 2465 MutableHandleIdVector props) const override { 2466 Rooted<EnvironmentObject*> env( 2467 cx, &proxy->as<DebugEnvironmentProxy>().environment()); 2468 2469 if (isMissingArgumentsBinding(*env)) { 2470 if (!props.append(NameToId(cx->names().arguments))) { 2471 return false; 2472 } 2473 } 2474 if (isMissingThisBinding(*env)) { 2475 if (!props.append(NameToId(cx->names().dot_this_))) { 2476 return false; 2477 } 2478 } 2479 2480 // WithEnvironmentObject isn't a very good proxy. It doesn't have a 2481 // JSNewEnumerateOp implementation, because if it just delegated to the 2482 // target object, the object would indicate that native enumeration is 2483 // the thing to do, but native enumeration over the WithEnvironmentObject 2484 // wrapper yields no properties. So instead here we hack around the 2485 // issue: punch a hole through to the with object target, then manually 2486 // examine @@unscopables. 2487 RootedObject target(cx); 2488 bool isWith = env->is<WithEnvironmentObject>(); 2489 if (isWith) { 2490 target = &env->as<WithEnvironmentObject>().object(); 2491 } else { 2492 target = env; 2493 } 2494 if (!GetPropertyKeys(cx, target, JSITER_OWNONLY, props)) { 2495 return false; 2496 } 2497 2498 if (isWith) { 2499 size_t j = 0; 2500 bool supportUnscopables = 2501 env->as<WithEnvironmentObject>().supportUnscopables(); 2502 for (size_t i = 0; i < props.length(); i++) { 2503 bool inScope = true; 2504 if (supportUnscopables && 2505 !CheckUnscopables(cx, env, props[i], &inScope)) { 2506 return false; 2507 } 2508 if (inScope) { 2509 props[j++].set(props[i]); 2510 } 2511 } 2512 if (!props.resize(j)) { 2513 return false; 2514 } 2515 } 2516 2517 /* 2518 * Environments with Scopes are optimized to not contain unaliased 2519 * variables so they must be manually appended here. 2520 */ 2521 if (Scope* scope = getEnvironmentScope(*env)) { 2522 for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) { 2523 if (!bi.closedOver() && 2524 !props.append(NameToId(bi.name()->asPropertyName()))) { 2525 return false; 2526 } 2527 } 2528 } 2529 2530 return true; 2531 } 2532 2533 bool has(JSContext* cx, HandleObject proxy, HandleId id_, 2534 bool* bp) const override { 2535 RootedId id(cx, id_); 2536 EnvironmentObject& envObj = 2537 proxy->as<DebugEnvironmentProxy>().environment(); 2538 2539 if (isArguments(cx, id) && isFunctionEnvironment(envObj)) { 2540 *bp = true; 2541 return true; 2542 } 2543 2544 // Be careful not to look up '.this' as a normal binding below, it will 2545 // assert in with_HasProperty. 2546 if (isThis(cx, id)) { 2547 *bp = isFunctionEnvironmentWithThis(envObj); 2548 return true; 2549 } 2550 2551 bool found; 2552 RootedObject env(cx, &envObj); 2553 if (!JS_HasPropertyById(cx, env, id, &found)) { 2554 return false; 2555 } 2556 2557 if (!found) { 2558 if (Scope* scope = getEnvironmentScope(*env)) { 2559 for (BindingIter bi(scope); bi; bi++) { 2560 if (!bi.closedOver() && NameToId(bi.name()->asPropertyName()) == id) { 2561 found = true; 2562 break; 2563 } 2564 } 2565 } 2566 } 2567 2568 *bp = found; 2569 return true; 2570 } 2571 2572 bool delete_(JSContext* cx, HandleObject proxy, HandleId id, 2573 ObjectOpResult& result) const override { 2574 return result.fail(JSMSG_CANT_DELETE); 2575 } 2576 }; 2577 2578 } /* anonymous namespace */ 2579 2580 Scope* js::GetEnvironmentScope(const JSObject& env) { 2581 return DebugEnvironmentProxyHandler::getEnvironmentScope(env); 2582 } 2583 2584 template <> 2585 bool JSObject::is<js::DebugEnvironmentProxy>() const { 2586 return IsDerivedProxyObject(this, &DebugEnvironmentProxyHandler::singleton); 2587 } 2588 2589 const char DebugEnvironmentProxyHandler::family = 0; 2590 const DebugEnvironmentProxyHandler DebugEnvironmentProxyHandler::singleton; 2591 2592 /* static */ 2593 DebugEnvironmentProxy* DebugEnvironmentProxy::create(JSContext* cx, 2594 EnvironmentObject& env, 2595 HandleObject enclosing) { 2596 MOZ_ASSERT(env.realm() == cx->realm()); 2597 MOZ_ASSERT(!enclosing->is<EnvironmentObject>()); 2598 2599 RootedValue priv(cx, ObjectValue(env)); 2600 JSObject* obj = NewProxyObject(cx, &DebugEnvironmentProxyHandler::singleton, 2601 priv, nullptr /* proto */); 2602 if (!obj) { 2603 return nullptr; 2604 } 2605 2606 DebugEnvironmentProxy* debugEnv = &obj->as<DebugEnvironmentProxy>(); 2607 debugEnv->setReservedSlot(ENCLOSING_SLOT, ObjectValue(*enclosing)); 2608 debugEnv->setReservedSlot(SNAPSHOT_SLOT, NullValue()); 2609 2610 return debugEnv; 2611 } 2612 2613 EnvironmentObject& DebugEnvironmentProxy::environment() const { 2614 return target()->as<EnvironmentObject>(); 2615 } 2616 2617 JSObject& DebugEnvironmentProxy::enclosingEnvironment() const { 2618 return reservedSlot(ENCLOSING_SLOT).toObject(); 2619 } 2620 2621 ArrayObject* DebugEnvironmentProxy::maybeSnapshot() const { 2622 JSObject* obj = reservedSlot(SNAPSHOT_SLOT).toObjectOrNull(); 2623 return obj ? &obj->as<ArrayObject>() : nullptr; 2624 } 2625 2626 void DebugEnvironmentProxy::initSnapshot(ArrayObject& o) { 2627 #ifdef DEBUG 2628 if (maybeSnapshot()) { 2629 auto* callObj = CallObject::find(&environment()); 2630 if (callObj) { 2631 MOZ_ASSERT(callObj->callee().isGeneratorOrAsync()); 2632 } else { 2633 auto* moduleEnv = ModuleEnvironmentObject::find(&environment()); 2634 MOZ_ASSERT(moduleEnv); 2635 MOZ_ASSERT(moduleEnv->module().hasTopLevelAwait()); 2636 } 2637 } 2638 #endif 2639 2640 setReservedSlot(SNAPSHOT_SLOT, ObjectValue(o)); 2641 } 2642 2643 bool DebugEnvironmentProxy::isForDeclarative() const { 2644 EnvironmentObject& e = environment(); 2645 return e.is<CallObject>() || e.is<VarEnvironmentObject>() || 2646 e.is<ModuleEnvironmentObject>() || 2647 e.is<WasmInstanceEnvironmentObject>() || 2648 e.is<WasmFunctionCallObject>() || e.is<LexicalEnvironmentObject>(); 2649 } 2650 2651 /* static */ 2652 bool DebugEnvironmentProxy::getMaybeSentinelValue( 2653 JSContext* cx, Handle<DebugEnvironmentProxy*> env, HandleId id, 2654 MutableHandleValue vp) { 2655 return DebugEnvironmentProxyHandler::singleton.getMaybeSentinelValue(cx, env, 2656 id, vp); 2657 } 2658 2659 bool DebugEnvironmentProxy::isFunctionEnvironmentWithThis() { 2660 return DebugEnvironmentProxyHandler::isFunctionEnvironmentWithThis( 2661 environment()); 2662 } 2663 2664 bool DebugEnvironmentProxy::isOptimizedOut() const { 2665 EnvironmentObject& e = environment(); 2666 2667 if (DebugEnvironments::hasLiveEnvironment(e)) { 2668 return false; 2669 } 2670 2671 if (e.is<LexicalEnvironmentObject>()) { 2672 return e.is<BlockLexicalEnvironmentObject>() && 2673 !e.as<BlockLexicalEnvironmentObject>().scope().hasEnvironment(); 2674 } 2675 2676 if (e.is<CallObject>()) { 2677 return !e.as<CallObject>().callee().needsCallObject() && !maybeSnapshot(); 2678 } 2679 2680 return false; 2681 } 2682 2683 /*****************************************************************************/ 2684 2685 [[nodiscard]] static bool GetFrameEnvironmentAndScope( 2686 JSContext* cx, AbstractFramePtr frame, const jsbytecode* pc, 2687 MutableHandleObject env, MutableHandle<Scope*> scope); 2688 2689 DebugEnvironments::DebugEnvironments(JSContext* cx, Zone* zone) 2690 : zone_(zone), 2691 proxiedEnvs(cx), 2692 missingEnvs(cx->zone()), 2693 liveEnvs(cx->zone()) {} 2694 2695 DebugEnvironments::~DebugEnvironments() { MOZ_ASSERT(missingEnvs.empty()); } 2696 2697 void DebugEnvironments::trace(JSTracer* trc) { proxiedEnvs.trace(trc); } 2698 2699 void DebugEnvironments::traceWeak(JSTracer* trc) { 2700 /* 2701 * missingEnvs points to debug envs weakly so that debug envs can be 2702 * released more eagerly. 2703 */ 2704 for (MissingEnvironmentMap::Enum e(missingEnvs); !e.empty(); e.popFront()) { 2705 auto result = 2706 TraceWeakEdge(trc, &e.front().value(), "MissingEnvironmentMap value"); 2707 if (result.isDead()) { 2708 /* 2709 * Note that onPopCall, onPopVar, and onPopLexical rely on missingEnvs to 2710 * find environment objects that we synthesized for the debugger's sake, 2711 * and clean up the synthetic environment objects' entries in liveEnvs. 2712 * So if we remove an entry from missingEnvs here, we must also remove the 2713 * corresponding liveEnvs entry. 2714 * 2715 * Since the DebugEnvironmentProxy is the only thing using its environment 2716 * object, and the DSO is about to be finalized, you might assume that the 2717 * synthetic SO is also about to be finalized too, and thus the loop below 2718 * will take care of things. But complex GC behavior means that marks are 2719 * only conservative approximations of liveness; we should assume that 2720 * anything could be marked. 2721 * 2722 * Thus, we must explicitly remove the entries from both liveEnvs and 2723 * missingEnvs here. 2724 */ 2725 liveEnvs.remove(&result.initialTarget()->environment()); 2726 e.removeFront(); 2727 } else { 2728 MissingEnvironmentKey key = e.front().key(); 2729 Scope* scope = key.scope(); 2730 MOZ_ALWAYS_TRUE(TraceManuallyBarrieredWeakEdge( 2731 trc, &scope, "MissingEnvironmentKey scope")); 2732 if (scope != key.scope()) { 2733 key.updateScope(scope); 2734 e.rekeyFront(key); 2735 } 2736 } 2737 } 2738 2739 /* 2740 * Scopes can be finalized when a debugger-synthesized EnvironmentObject is 2741 * no longer reachable via its DebugEnvironmentProxy. 2742 */ 2743 liveEnvs.traceWeak(trc); 2744 } 2745 2746 void DebugEnvironments::finish() { proxiedEnvs.clear(); } 2747 2748 #ifdef JSGC_HASH_TABLE_CHECKS 2749 void DebugEnvironments::checkHashTablesAfterMovingGC() { 2750 /* 2751 * This is called at the end of StoreBuffer::mark() to check that our 2752 * postbarriers have worked and that no hashtable keys (or values) are left 2753 * pointing into the nursery. 2754 * 2755 * |proxiedEnvs| is checked automatically because it is a WeakMap. 2756 */ 2757 CheckTableAfterMovingGC(missingEnvs, [this](const auto& entry) { 2758 CheckGCThingAfterMovingGC(entry.key().scope(), zone()); 2759 // Use unbarrieredGet() to prevent triggering read barrier while collecting. 2760 CheckGCThingAfterMovingGC(entry.value().unbarrieredGet(), zone()); 2761 return entry.key(); 2762 }); 2763 CheckTableAfterMovingGC(liveEnvs, [this](const auto& entry) { 2764 CheckGCThingAfterMovingGC(entry.key(), zone()); 2765 CheckGCThingAfterMovingGC(entry.value().scope_.get(), zone()); 2766 return entry.key().unbarrieredGet(); 2767 }); 2768 } 2769 #endif 2770 2771 /* 2772 * Unfortunately, GetDebugEnvironmentForFrame needs to work even outside debug 2773 * mode (in particular, JS_GetFrameScopeChain does not require debug mode). 2774 * Since DebugEnvironments::onPop* are only called in debuggee frames, this 2775 * means we cannot use any of the maps in DebugEnvironments. This will produce 2776 * debug scope chains that do not obey the debugger invariants but that is just 2777 * fine. 2778 */ 2779 static bool CanUseDebugEnvironmentMaps(JSContext* cx) { 2780 return cx->realm()->isDebuggee(); 2781 } 2782 2783 DebugEnvironments* DebugEnvironments::ensureRealmData(JSContext* cx) { 2784 Realm* realm = cx->realm(); 2785 if (auto* debugEnvs = realm->debugEnvs()) { 2786 return debugEnvs; 2787 } 2788 2789 auto debugEnvs = cx->make_unique<DebugEnvironments>(cx, cx->zone()); 2790 if (!debugEnvs) { 2791 return nullptr; 2792 } 2793 2794 realm->debugEnvsRef() = std::move(debugEnvs); 2795 return realm->debugEnvs(); 2796 } 2797 2798 /* static */ 2799 DebugEnvironmentProxy* DebugEnvironments::hasDebugEnvironment( 2800 JSContext* cx, EnvironmentObject& env) { 2801 DebugEnvironments* envs = env.realm()->debugEnvs(); 2802 if (!envs) { 2803 return nullptr; 2804 } 2805 2806 if (JSObject* obj = envs->proxiedEnvs.get(&env)) { 2807 MOZ_ASSERT(CanUseDebugEnvironmentMaps(cx)); 2808 return &obj->as<DebugEnvironmentProxy>(); 2809 } 2810 2811 return nullptr; 2812 } 2813 2814 /* static */ 2815 bool DebugEnvironments::addDebugEnvironment( 2816 JSContext* cx, Handle<EnvironmentObject*> env, 2817 Handle<DebugEnvironmentProxy*> debugEnv) { 2818 MOZ_ASSERT(cx->realm() == env->realm()); 2819 MOZ_ASSERT(cx->realm() == debugEnv->nonCCWRealm()); 2820 2821 if (!CanUseDebugEnvironmentMaps(cx)) { 2822 return true; 2823 } 2824 2825 DebugEnvironments* envs = ensureRealmData(cx); 2826 if (!envs) { 2827 return false; 2828 } 2829 2830 if (!envs->proxiedEnvs.put(env, debugEnv)) { 2831 ReportOutOfMemory(cx); 2832 return false; 2833 } 2834 2835 return true; 2836 } 2837 2838 /* static */ 2839 bool DebugEnvironments::getExistingDebugEnvironment( 2840 JSContext* cx, const EnvironmentIter& ei, DebugEnvironmentProxy** out) { 2841 MOZ_ASSERT(!ei.hasSyntacticEnvironment()); 2842 2843 DebugEnvironments* envs = cx->realm()->debugEnvs(); 2844 if (!envs) { 2845 *out = nullptr; 2846 return true; 2847 } 2848 2849 MissingEnvironmentKey key; 2850 if (!key.initFromEnvironmentIter(cx, ei)) { 2851 return false; 2852 } 2853 2854 if (MissingEnvironmentMap::Ptr p = envs->missingEnvs.lookup(key)) { 2855 MOZ_ASSERT(CanUseDebugEnvironmentMaps(cx)); 2856 *out = p->value(); 2857 return true; 2858 } 2859 2860 *out = nullptr; 2861 return true; 2862 } 2863 2864 /* static */ 2865 bool DebugEnvironments::addDebugEnvironment( 2866 JSContext* cx, const EnvironmentIter& ei, 2867 Handle<DebugEnvironmentProxy*> debugEnv) { 2868 MOZ_ASSERT(!ei.hasSyntacticEnvironment()); 2869 MOZ_ASSERT(cx->realm() == debugEnv->nonCCWRealm()); 2870 2871 if (!CanUseDebugEnvironmentMaps(cx)) { 2872 return true; 2873 } 2874 2875 DebugEnvironments* envs = ensureRealmData(cx); 2876 if (!envs) { 2877 return false; 2878 } 2879 2880 MissingEnvironmentKey key; 2881 if (!key.initFromEnvironmentIter(cx, ei)) { 2882 return false; 2883 } 2884 MOZ_ASSERT(!envs->missingEnvs.has(key)); 2885 if (!envs->missingEnvs.put(key, 2886 WeakHeapPtr<DebugEnvironmentProxy*>(debugEnv))) { 2887 ReportOutOfMemory(cx); 2888 return false; 2889 } 2890 2891 // Only add to liveEnvs if we synthesized the debug env on a live 2892 // frame. 2893 if (ei.withinInitialFrame()) { 2894 MOZ_ASSERT(!envs->liveEnvs.has(&debugEnv->environment())); 2895 if (!envs->liveEnvs.put(&debugEnv->environment(), LiveEnvironmentVal(ei))) { 2896 ReportOutOfMemory(cx); 2897 return false; 2898 } 2899 } 2900 2901 return true; 2902 } 2903 2904 /* static */ 2905 void DebugEnvironments::takeFrameSnapshot( 2906 JSContext* cx, Handle<DebugEnvironmentProxy*> debugEnv, 2907 AbstractFramePtr frame) { 2908 /* 2909 * When the JS stack frame is popped, the values of unaliased variables 2910 * are lost. If there is any debug env referring to this environment, save a 2911 * copy of the unaliased variables' values in an array for later debugger 2912 * access via DebugEnvironmentProxy::handleUnaliasedAccess. 2913 * 2914 * Note: since it is simplest for this function to be infallible, failure 2915 * in this code will be silently ignored. This does not break any 2916 * invariants since DebugEnvironmentProxy::maybeSnapshot can already be 2917 * nullptr. 2918 */ 2919 2920 // Because this can be called during exception unwinding, save the exception 2921 // state and restore it when we're done. 2922 JS::AutoSaveExceptionState ases(cx); 2923 2924 JSScript* script = frame.script(); 2925 2926 // Act like no snapshot was taken if we run OOM while taking the snapshot. 2927 Rooted<GCVector<Value>> vec(cx, GCVector<Value>(cx)); 2928 if (debugEnv->environment().is<CallObject>()) { 2929 FunctionScope* scope = &script->bodyScope()->as<FunctionScope>(); 2930 uint32_t frameSlotCount = scope->nextFrameSlot(); 2931 MOZ_ASSERT(frameSlotCount <= script->nfixed()); 2932 2933 // For simplicity, copy all frame slots from 0 to the frameSlotCount, 2934 // even if we don't need all of them (like in the case of a defaults 2935 // parameter scope having frame slots). 2936 uint32_t numFormals = frame.numFormalArgs(); 2937 if (!vec.resize(numFormals + frameSlotCount)) { 2938 cx->recoverFromOutOfMemory(); 2939 return; 2940 } 2941 mozilla::PodCopy(vec.begin(), frame.argv(), numFormals); 2942 for (uint32_t slot = 0; slot < frameSlotCount; slot++) { 2943 vec[slot + frame.numFormalArgs()].set(frame.unaliasedLocal(slot)); 2944 } 2945 2946 /* 2947 * Copy in formals that are not aliased via the scope chain 2948 * but are aliased via the arguments object. 2949 */ 2950 if (script->needsArgsObj() && frame.hasArgsObj()) { 2951 for (unsigned i = 0; i < frame.numFormalArgs(); ++i) { 2952 if (script->formalLivesInArgumentsObject(i)) { 2953 vec[i].set(frame.argsObj().arg(i)); 2954 } 2955 } 2956 } 2957 } else { 2958 uint32_t frameSlotStart; 2959 uint32_t frameSlotEnd; 2960 2961 if (debugEnv->environment().is<BlockLexicalEnvironmentObject>()) { 2962 LexicalScope* scope = 2963 &debugEnv->environment().as<BlockLexicalEnvironmentObject>().scope(); 2964 frameSlotStart = scope->firstFrameSlot(); 2965 frameSlotEnd = scope->nextFrameSlot(); 2966 } else if (debugEnv->environment() 2967 .is<ClassBodyLexicalEnvironmentObject>()) { 2968 ClassBodyScope* scope = &debugEnv->environment() 2969 .as<ClassBodyLexicalEnvironmentObject>() 2970 .scope(); 2971 frameSlotStart = scope->firstFrameSlot(); 2972 frameSlotEnd = scope->nextFrameSlot(); 2973 } else if (debugEnv->environment().is<VarEnvironmentObject>()) { 2974 VarEnvironmentObject* env = 2975 &debugEnv->environment().as<VarEnvironmentObject>(); 2976 if (frame.isFunctionFrame()) { 2977 VarScope* scope = &env->scope().as<VarScope>(); 2978 frameSlotStart = scope->firstFrameSlot(); 2979 frameSlotEnd = scope->nextFrameSlot(); 2980 } else { 2981 EvalScope* scope = &env->scope().as<EvalScope>(); 2982 MOZ_ASSERT(scope == script->bodyScope()); 2983 frameSlotStart = 0; 2984 frameSlotEnd = scope->nextFrameSlot(); 2985 } 2986 } else { 2987 MOZ_ASSERT(&debugEnv->environment().as<ModuleEnvironmentObject>() == 2988 script->module()->environment()); 2989 ModuleScope* scope = &script->bodyScope()->as<ModuleScope>(); 2990 frameSlotStart = 0; 2991 frameSlotEnd = scope->nextFrameSlot(); 2992 } 2993 2994 uint32_t frameSlotCount = frameSlotEnd - frameSlotStart; 2995 MOZ_ASSERT(frameSlotCount <= script->nfixed()); 2996 2997 if (!vec.resize(frameSlotCount)) { 2998 cx->recoverFromOutOfMemory(); 2999 return; 3000 } 3001 for (uint32_t slot = frameSlotStart; slot < frameSlotCount; slot++) { 3002 vec[slot - frameSlotStart].set(frame.unaliasedLocal(slot)); 3003 } 3004 } 3005 3006 if (vec.length() == 0) { 3007 return; 3008 } 3009 3010 /* 3011 * Use a dense array as storage (since proxies do not have trace 3012 * hooks). This array must not escape into the wild. 3013 */ 3014 Rooted<ArrayObject*> snapshot( 3015 cx, NewDenseCopiedArray(cx, vec.length(), vec.begin())); 3016 if (!snapshot) { 3017 MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed()); 3018 cx->clearPendingException(); 3019 return; 3020 } 3021 3022 debugEnv->initSnapshot(*snapshot); 3023 } 3024 3025 /* static */ 3026 void DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame) { 3027 cx->check(frame); 3028 3029 DebugEnvironments* envs = cx->realm()->debugEnvs(); 3030 if (!envs) { 3031 return; 3032 } 3033 3034 Rooted<DebugEnvironmentProxy*> debugEnv(cx, nullptr); 3035 3036 FunctionScope* funScope = &frame.script()->bodyScope()->as<FunctionScope>(); 3037 if (funScope->hasEnvironment()) { 3038 MOZ_ASSERT(frame.callee()->needsCallObject()); 3039 3040 /* 3041 * The frame may be observed before the prologue has created the 3042 * CallObject. See EnvironmentIter::settle. 3043 */ 3044 if (!frame.environmentChain()->is<CallObject>()) { 3045 return; 3046 } 3047 3048 CallObject& callobj = frame.environmentChain()->as<CallObject>(); 3049 envs->liveEnvs.remove(&callobj); 3050 if (JSObject* obj = envs->proxiedEnvs.get(&callobj)) { 3051 debugEnv = &obj->as<DebugEnvironmentProxy>(); 3052 } 3053 } else { 3054 MissingEnvironmentKey key(frame, funScope); 3055 if (MissingEnvironmentMap::Ptr p = envs->missingEnvs.lookup(key)) { 3056 debugEnv = p->value(); 3057 envs->liveEnvs.remove(&debugEnv->environment().as<CallObject>()); 3058 envs->missingEnvs.remove(p); 3059 } 3060 } 3061 3062 if (debugEnv) { 3063 DebugEnvironments::takeFrameSnapshot(cx, debugEnv, frame); 3064 } 3065 } 3066 3067 void DebugEnvironments::onPopLexical(JSContext* cx, AbstractFramePtr frame, 3068 const jsbytecode* pc) { 3069 cx->check(frame); 3070 3071 DebugEnvironments* envs = cx->realm()->debugEnvs(); 3072 if (!envs) { 3073 return; 3074 } 3075 3076 EnvironmentIter ei(cx, frame, pc); 3077 onPopLexical(cx, ei); 3078 } 3079 3080 template <typename Environment, typename Scope> 3081 void DebugEnvironments::onPopGeneric(JSContext* cx, const EnvironmentIter& ei) { 3082 DebugEnvironments* envs = cx->realm()->debugEnvs(); 3083 if (!envs) { 3084 return; 3085 } 3086 3087 MOZ_ASSERT(ei.withinInitialFrame()); 3088 MOZ_ASSERT(ei.scope().is<Scope>()); 3089 3090 MissingEnvironmentKey key; 3091 { 3092 js::AutoEnterOOMUnsafeRegion oomUnsafe; 3093 if (!key.initFromEnvironmentIter(cx, ei)) { 3094 oomUnsafe.crash("OOM during onPopGeneric"); 3095 return; 3096 } 3097 } 3098 Rooted<Environment*> env(cx); 3099 if (MissingEnvironmentMap::Ptr p = envs->missingEnvs.lookup(key)) { 3100 env = &p->value()->environment().as<Environment>(); 3101 envs->missingEnvs.remove(p); 3102 } else if (ei.hasSyntacticEnvironment()) { 3103 env = &ei.environment().as<Environment>(); 3104 } 3105 3106 if (env) { 3107 envs->liveEnvs.remove(env); 3108 3109 if (JSObject* obj = envs->proxiedEnvs.get(env)) { 3110 Rooted<DebugEnvironmentProxy*> debugEnv( 3111 cx, &obj->as<DebugEnvironmentProxy>()); 3112 DebugEnvironments::takeFrameSnapshot(cx, debugEnv, ei.initialFrame()); 3113 } 3114 } 3115 } 3116 3117 void DebugEnvironments::onPopLexical(JSContext* cx, const EnvironmentIter& ei) { 3118 if (ei.scope().is<ClassBodyScope>()) { 3119 onPopGeneric<ScopedLexicalEnvironmentObject, ClassBodyScope>(cx, ei); 3120 } else { 3121 onPopGeneric<ScopedLexicalEnvironmentObject, LexicalScope>(cx, ei); 3122 } 3123 } 3124 3125 void DebugEnvironments::onPopVar(JSContext* cx, const EnvironmentIter& ei) { 3126 if (ei.scope().is<EvalScope>()) { 3127 onPopGeneric<VarEnvironmentObject, EvalScope>(cx, ei); 3128 } else { 3129 onPopGeneric<VarEnvironmentObject, VarScope>(cx, ei); 3130 } 3131 } 3132 3133 void DebugEnvironments::onPopWith(AbstractFramePtr frame) { 3134 Realm* realm = frame.realm(); 3135 if (DebugEnvironments* envs = realm->debugEnvs()) { 3136 envs->liveEnvs.remove( 3137 &frame.environmentChain()->as<WithEnvironmentObject>()); 3138 } 3139 } 3140 3141 void DebugEnvironments::onPopModule(JSContext* cx, const EnvironmentIter& ei) { 3142 onPopGeneric<ModuleEnvironmentObject, ModuleScope>(cx, ei); 3143 } 3144 3145 void DebugEnvironments::onRealmUnsetIsDebuggee(Realm* realm) { 3146 if (DebugEnvironments* envs = realm->debugEnvs()) { 3147 envs->proxiedEnvs.clear(); 3148 envs->missingEnvs.clear(); 3149 envs->liveEnvs.clear(); 3150 } 3151 } 3152 3153 bool DebugEnvironments::updateLiveEnvironments(JSContext* cx) { 3154 AutoCheckRecursionLimit recursion(cx); 3155 if (!recursion.check(cx)) { 3156 return false; 3157 } 3158 3159 /* 3160 * Note that we must always update the top frame's environment objects' 3161 * entries in liveEnvs because we can't be sure code hasn't run in that 3162 * frame to change the environment chain since we were last called. The 3163 * fp->prevUpToDate() flag indicates whether the environments of frames 3164 * older than fp are already included in liveEnvs. It might seem simpler 3165 * to have fp instead carry a flag indicating whether fp itself is 3166 * accurately described, but then we would need to clear that flag 3167 * whenever fp ran code. By storing the 'up to date' bit for fp->prev() in 3168 * fp, simply popping fp effectively clears the flag for us, at exactly 3169 * the time when execution resumes fp->prev(). 3170 */ 3171 for (AllFramesIter i(cx); !i.done(); ++i) { 3172 if (!i.hasUsableAbstractFramePtr()) { 3173 continue; 3174 } 3175 3176 AbstractFramePtr frame = i.abstractFramePtr(); 3177 if (frame.realm() != cx->realm()) { 3178 continue; 3179 } 3180 3181 if (!frame.isDebuggee()) { 3182 continue; 3183 } 3184 3185 RootedObject env(cx); 3186 Rooted<Scope*> scope(cx); 3187 if (!GetFrameEnvironmentAndScope(cx, frame, i.pc(), &env, &scope)) { 3188 return false; 3189 } 3190 3191 for (EnvironmentIter ei(cx, env, scope, frame); ei.withinInitialFrame(); 3192 ei++) { 3193 if (ei.hasSyntacticEnvironment() && !ei.scope().is<GlobalScope>()) { 3194 MOZ_ASSERT(ei.environment().realm() == cx->realm()); 3195 DebugEnvironments* envs = ensureRealmData(cx); 3196 if (!envs) { 3197 return false; 3198 } 3199 if (!envs->liveEnvs.put(&ei.environment(), LiveEnvironmentVal(ei))) { 3200 ReportOutOfMemory(cx); 3201 return false; 3202 } 3203 } 3204 } 3205 3206 if (frame.prevUpToDate()) { 3207 return true; 3208 } 3209 MOZ_ASSERT(frame.realm()->isDebuggee()); 3210 frame.setPrevUpToDate(); 3211 } 3212 3213 return true; 3214 } 3215 3216 LiveEnvironmentVal* DebugEnvironments::hasLiveEnvironment( 3217 EnvironmentObject& env) { 3218 DebugEnvironments* envs = env.realm()->debugEnvs(); 3219 if (!envs) { 3220 return nullptr; 3221 } 3222 3223 if (LiveEnvironmentMap::Ptr p = envs->liveEnvs.lookup(&env)) { 3224 return &p->value(); 3225 } 3226 3227 return nullptr; 3228 } 3229 3230 /* static */ 3231 void DebugEnvironments::unsetPrevUpToDateUntil(JSContext* cx, 3232 AbstractFramePtr until) { 3233 // This are two exceptions where fp->prevUpToDate() is cleared without 3234 // popping the frame. When a frame is rematerialized or has its 3235 // debuggeeness toggled off->on, all frames younger than the frame must 3236 // have their prevUpToDate set to false. This is because unrematerialized 3237 // Ion frames and non-debuggee frames are skipped by updateLiveEnvironments. 3238 // If in the future a frame suddenly gains a usable AbstractFramePtr via 3239 // rematerialization or becomes a debuggee, the prevUpToDate invariant 3240 // will no longer hold for older frames on its stack. 3241 for (AllFramesIter i(cx); !i.done(); ++i) { 3242 if (!i.hasUsableAbstractFramePtr()) { 3243 continue; 3244 } 3245 3246 AbstractFramePtr frame = i.abstractFramePtr(); 3247 if (frame == until) { 3248 return; 3249 } 3250 3251 if (frame.realm() != cx->realm()) { 3252 continue; 3253 } 3254 3255 frame.unsetPrevUpToDate(); 3256 } 3257 } 3258 3259 /* static */ 3260 void DebugEnvironments::forwardLiveFrame(JSContext* cx, AbstractFramePtr from, 3261 AbstractFramePtr to) { 3262 DebugEnvironments* envs = cx->realm()->debugEnvs(); 3263 if (!envs) { 3264 return; 3265 } 3266 3267 for (MissingEnvironmentMap::Enum e(envs->missingEnvs); !e.empty(); 3268 e.popFront()) { 3269 MissingEnvironmentKey key = e.front().key(); 3270 if (key.frame() == from) { 3271 key.updateFrame(to); 3272 e.rekeyFront(key); 3273 } 3274 } 3275 3276 for (LiveEnvironmentMap::Enum e(envs->liveEnvs); !e.empty(); e.popFront()) { 3277 LiveEnvironmentVal& val = e.front().value(); 3278 if (val.frame() == from) { 3279 val.updateFrame(to); 3280 } 3281 } 3282 } 3283 3284 /* static */ 3285 void DebugEnvironments::traceLiveFrame(JSTracer* trc, AbstractFramePtr frame) { 3286 for (MissingEnvironmentMap::Enum e(missingEnvs); !e.empty(); e.popFront()) { 3287 if (e.front().key().frame() == frame) { 3288 TraceEdge(trc, &e.front().value(), "debug-env-live-frame-missing-env"); 3289 } 3290 } 3291 } 3292 3293 /*****************************************************************************/ 3294 3295 static JSObject* GetDebugEnvironment(JSContext* cx, const EnvironmentIter& ei); 3296 3297 static DebugEnvironmentProxy* GetDebugEnvironmentForEnvironmentObject( 3298 JSContext* cx, const EnvironmentIter& ei) { 3299 Rooted<EnvironmentObject*> env(cx, &ei.environment()); 3300 if (DebugEnvironmentProxy* debugEnv = 3301 DebugEnvironments::hasDebugEnvironment(cx, *env)) { 3302 return debugEnv; 3303 } 3304 3305 EnvironmentIter copy(cx, ei); 3306 RootedObject enclosingDebug(cx, GetDebugEnvironment(cx, ++copy)); 3307 if (!enclosingDebug) { 3308 return nullptr; 3309 } 3310 3311 Rooted<DebugEnvironmentProxy*> debugEnv( 3312 cx, DebugEnvironmentProxy::create(cx, *env, enclosingDebug)); 3313 if (!debugEnv) { 3314 return nullptr; 3315 } 3316 3317 if (!DebugEnvironments::addDebugEnvironment(cx, env, debugEnv)) { 3318 return nullptr; 3319 } 3320 3321 return debugEnv; 3322 } 3323 3324 static DebugEnvironmentProxy* GetDebugEnvironmentForMissing( 3325 JSContext* cx, const EnvironmentIter& ei) { 3326 MOZ_ASSERT(!ei.hasSyntacticEnvironment() && 3327 (ei.scope().is<FunctionScope>() || ei.scope().is<LexicalScope>() || 3328 ei.scope().is<WasmInstanceScope>() || 3329 ei.scope().is<WasmFunctionScope>() || ei.scope().is<VarScope>() || 3330 ei.scope().kind() == ScopeKind::StrictEval)); 3331 3332 DebugEnvironmentProxy* maybeDebugEnv; 3333 if (!DebugEnvironments::getExistingDebugEnvironment(cx, ei, &maybeDebugEnv)) { 3334 return nullptr; 3335 } 3336 if (maybeDebugEnv) { 3337 return maybeDebugEnv; 3338 } 3339 3340 EnvironmentIter copy(cx, ei); 3341 RootedObject enclosingDebug(cx, GetDebugEnvironment(cx, ++copy)); 3342 if (!enclosingDebug) { 3343 return nullptr; 3344 } 3345 3346 /* 3347 * Create the missing environment object. For lexical environment objects, 3348 * this takes care of storing variable values after the stack frame has 3349 * been popped. For call objects, we only use the pretend call object to 3350 * access callee, bindings and to receive dynamically added 3351 * properties. Together, this provides the nice invariant that every 3352 * DebugEnvironmentProxy has a EnvironmentObject. 3353 * 3354 * Note: to preserve envChain depth invariants, these lazily-reified 3355 * envs must not be put on the frame's environment chain; instead, they are 3356 * maintained via DebugEnvironments hooks. 3357 */ 3358 Rooted<DebugEnvironmentProxy*> debugEnv(cx); 3359 if (ei.scope().is<FunctionScope>()) { 3360 RootedFunction callee(cx, 3361 ei.scope().as<FunctionScope>().canonicalFunction()); 3362 3363 JS::ExposeObjectToActiveJS(callee); 3364 Rooted<CallObject*> callobj(cx, 3365 CallObject::createHollowForDebug(cx, callee)); 3366 if (!callobj) { 3367 return nullptr; 3368 } 3369 3370 debugEnv = DebugEnvironmentProxy::create(cx, *callobj, enclosingDebug); 3371 } else if (ei.scope().is<LexicalScope>()) { 3372 Rooted<LexicalScope*> lexicalScope(cx, &ei.scope().as<LexicalScope>()); 3373 Rooted<BlockLexicalEnvironmentObject*> env( 3374 cx, 3375 BlockLexicalEnvironmentObject::createHollowForDebug(cx, lexicalScope)); 3376 if (!env) { 3377 return nullptr; 3378 } 3379 3380 debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug); 3381 } else if (ei.scope().is<WasmInstanceScope>()) { 3382 Rooted<WasmInstanceScope*> wasmInstanceScope( 3383 cx, &ei.scope().as<WasmInstanceScope>()); 3384 Rooted<WasmInstanceEnvironmentObject*> env( 3385 cx, WasmInstanceEnvironmentObject::createHollowForDebug( 3386 cx, wasmInstanceScope)); 3387 if (!env) { 3388 return nullptr; 3389 } 3390 3391 debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug); 3392 } else if (ei.scope().is<WasmFunctionScope>()) { 3393 Rooted<WasmFunctionScope*> wasmFunctionScope( 3394 cx, &ei.scope().as<WasmFunctionScope>()); 3395 RootedObject enclosing( 3396 cx, &enclosingDebug->as<DebugEnvironmentProxy>().environment()); 3397 Rooted<WasmFunctionCallObject*> callobj( 3398 cx, WasmFunctionCallObject::createHollowForDebug(cx, enclosing, 3399 wasmFunctionScope)); 3400 if (!callobj) { 3401 return nullptr; 3402 } 3403 3404 debugEnv = DebugEnvironmentProxy::create(cx, *callobj, enclosingDebug); 3405 } else { 3406 Rooted<Scope*> scope(cx, &ei.scope()); 3407 MOZ_ASSERT(scope->is<VarScope>() || scope->kind() == ScopeKind::StrictEval); 3408 3409 Rooted<VarEnvironmentObject*> env( 3410 cx, VarEnvironmentObject::createHollowForDebug(cx, scope)); 3411 if (!env) { 3412 return nullptr; 3413 } 3414 3415 debugEnv = DebugEnvironmentProxy::create(cx, *env, enclosingDebug); 3416 } 3417 3418 if (!debugEnv) { 3419 return nullptr; 3420 } 3421 3422 if (!DebugEnvironments::addDebugEnvironment(cx, ei, debugEnv)) { 3423 return nullptr; 3424 } 3425 3426 return debugEnv; 3427 } 3428 3429 static JSObject* GetDebugEnvironmentForNonEnvironmentObject( 3430 const EnvironmentIter& ei) { 3431 JSObject& enclosing = ei.enclosingEnvironment(); 3432 #ifdef DEBUG 3433 JSObject* o = &enclosing; 3434 while ((o = o->enclosingEnvironment())) { 3435 MOZ_ASSERT(!o->is<EnvironmentObject>()); 3436 } 3437 #endif 3438 return &enclosing; 3439 } 3440 3441 static JSObject* GetDebugEnvironment(JSContext* cx, const EnvironmentIter& ei) { 3442 AutoCheckRecursionLimit recursion(cx); 3443 if (!recursion.check(cx)) { 3444 return nullptr; 3445 } 3446 3447 if (ei.done()) { 3448 return GetDebugEnvironmentForNonEnvironmentObject(ei); 3449 } 3450 3451 if (ei.hasAnyEnvironmentObject()) { 3452 return GetDebugEnvironmentForEnvironmentObject(cx, ei); 3453 } 3454 3455 if (ei.scope().is<FunctionScope>() || ei.scope().is<LexicalScope>() || 3456 ei.scope().is<WasmInstanceScope>() || 3457 ei.scope().is<WasmFunctionScope>() || ei.scope().is<VarScope>() || 3458 ei.scope().kind() == ScopeKind::StrictEval) { 3459 return GetDebugEnvironmentForMissing(cx, ei); 3460 } 3461 3462 EnvironmentIter copy(cx, ei); 3463 return GetDebugEnvironment(cx, ++copy); 3464 } 3465 3466 JSObject* js::GetDebugEnvironmentForFunction(JSContext* cx, 3467 HandleFunction fun) { 3468 cx->check(fun); 3469 MOZ_ASSERT(CanUseDebugEnvironmentMaps(cx)); 3470 if (!DebugEnvironments::updateLiveEnvironments(cx)) { 3471 return nullptr; 3472 } 3473 JSScript* script = JSFunction::getOrCreateScript(cx, fun); 3474 if (!script) { 3475 return nullptr; 3476 } 3477 EnvironmentIter ei(cx, fun->environment(), script->enclosingScope()); 3478 return GetDebugEnvironment(cx, ei); 3479 } 3480 3481 static auto GetSuspendedGeneratorEnvironmentAndScope( 3482 AbstractGeneratorObject& genObj, JSScript* script) { 3483 jsbytecode* pc = 3484 script->offsetToPC(script->resumeOffsets()[genObj.resumeIndex()]); 3485 return std::pair{&genObj.environmentChain(), script->innermostScope(pc)}; 3486 } 3487 3488 JSObject* js::GetDebugEnvironmentForSuspendedGenerator( 3489 JSContext* cx, JSScript* script, AbstractGeneratorObject& genObj) { 3490 auto [env, scope] = GetSuspendedGeneratorEnvironmentAndScope(genObj, script); 3491 3492 EnvironmentIter ei(cx, env, scope); 3493 return GetDebugEnvironment(cx, ei); 3494 } 3495 3496 JSObject* js::GetDebugEnvironmentForFrame(JSContext* cx, AbstractFramePtr frame, 3497 jsbytecode* pc) { 3498 cx->check(frame); 3499 if (CanUseDebugEnvironmentMaps(cx) && 3500 !DebugEnvironments::updateLiveEnvironments(cx)) { 3501 return nullptr; 3502 } 3503 3504 RootedObject env(cx); 3505 Rooted<Scope*> scope(cx); 3506 if (!GetFrameEnvironmentAndScope(cx, frame, pc, &env, &scope)) { 3507 return nullptr; 3508 } 3509 3510 EnvironmentIter ei(cx, env, scope, frame); 3511 return GetDebugEnvironment(cx, ei); 3512 } 3513 3514 JSObject* js::GetDebugEnvironmentForGlobalLexicalEnvironment(JSContext* cx) { 3515 EnvironmentIter ei(cx, &cx->global()->lexicalEnvironment(), 3516 &cx->global()->emptyGlobalScope()); 3517 return GetDebugEnvironment(cx, ei); 3518 } 3519 3520 WithEnvironmentObject* js::CreateObjectsForEnvironmentChain( 3521 JSContext* cx, const JS::EnvironmentChain& chain, 3522 HandleObject terminatingEnv) { 3523 MOZ_ASSERT(!chain.empty()); 3524 3525 #ifdef DEBUG 3526 for (size_t i = 0; i < chain.length(); ++i) { 3527 cx->check(chain.chain()[i]); 3528 MOZ_ASSERT(!chain.chain()[i]->isUnqualifiedVarObj()); 3529 } 3530 #endif 3531 3532 // Construct With object wrappers for the things on this environment chain 3533 // and use the result as the thing to scope the function to. 3534 Rooted<WithEnvironmentObject*> withEnv(cx); 3535 RootedObject enclosingEnv(cx, terminatingEnv); 3536 for (size_t i = chain.length(); i > 0;) { 3537 withEnv = WithEnvironmentObject::createNonSyntactic( 3538 cx, chain.chain()[--i], enclosingEnv, chain.supportUnscopables()); 3539 if (!withEnv) { 3540 return nullptr; 3541 } 3542 enclosingEnv = withEnv; 3543 } 3544 3545 return withEnv; 3546 } 3547 3548 JSObject& WithEnvironmentObject::object() const { 3549 return getReservedSlot(OBJECT_SLOT).toObject(); 3550 } 3551 3552 JSObject* WithEnvironmentObject::withThis() const { 3553 JSObject* obj = &getReservedSlot(THIS_SLOT).toObject(); 3554 3555 // Windows must never be exposed to script. WithEnvironmentObject::create 3556 // should have set this to the WindowProxy. 3557 MOZ_ASSERT(!IsWindow(obj)); 3558 3559 return obj; 3560 } 3561 3562 bool WithEnvironmentObject::isSyntactic() const { 3563 Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT); 3564 MOZ_ASSERT(v.isPrivateGCThing() || v.isBoolean()); 3565 return v.isPrivateGCThing(); 3566 } 3567 3568 bool WithEnvironmentObject::supportUnscopables() const { 3569 if (isSyntactic()) { 3570 return true; 3571 } 3572 Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT); 3573 MOZ_ASSERT(v.isBoolean()); 3574 return v.isTrue(); 3575 } 3576 3577 WithScope& WithEnvironmentObject::scope() const { 3578 MOZ_ASSERT(isSyntactic()); 3579 Value v = getReservedSlot(SCOPE_OR_SUPPORT_UNSCOPABLES_SLOT); 3580 return *static_cast<WithScope*>(v.toGCThing()); 3581 } 3582 3583 ModuleEnvironmentObject* js::GetModuleEnvironmentForScript(JSScript* script) { 3584 return GetModuleObjectForScript(script)->environment(); 3585 } 3586 3587 ModuleObject* js::GetModuleObjectForScript(JSScript* script) { 3588 for (ScopeIter si(script); si; si++) { 3589 if (si.kind() == ScopeKind::Module) { 3590 return si.scope()->as<ModuleScope>().module(); 3591 } 3592 } 3593 3594 MOZ_CRASH("No module scope found for script"); 3595 } 3596 3597 static bool GetThisValueForDebuggerEnvironmentIterMaybeOptimizedOut( 3598 JSContext* cx, const EnvironmentIter& originalIter, HandleObject envChain, 3599 const jsbytecode* pc, MutableHandleValue res) { 3600 for (EnvironmentIter ei(cx, originalIter); ei; ei++) { 3601 if (ei.scope().kind() == ScopeKind::Module) { 3602 res.setUndefined(); 3603 return true; 3604 } 3605 3606 if (!ei.scope().is<FunctionScope>() || 3607 ei.scope().as<FunctionScope>().canonicalFunction()->hasLexicalThis()) { 3608 continue; 3609 } 3610 3611 RootedScript script(cx, ei.scope().as<FunctionScope>().script()); 3612 3613 if (ei.withinInitialFrame()) { 3614 MOZ_ASSERT(pc, "must have PC if there is an initial frame"); 3615 3616 // Figure out if we executed JSOp::FunctionThis and set it. 3617 bool executedInitThisOp = false; 3618 if (script->functionHasThisBinding()) { 3619 for (const BytecodeLocation& loc : js::AllBytecodesIterable(script)) { 3620 if (loc.getOp() == JSOp::FunctionThis) { 3621 // The next op after JSOp::FunctionThis always sets it. 3622 executedInitThisOp = pc > GetNextPc(loc.toRawBytecode()); 3623 break; 3624 } 3625 } 3626 } 3627 3628 if (!executedInitThisOp) { 3629 AbstractFramePtr initialFrame = ei.initialFrame(); 3630 // Either we're yet to initialize the this-binding 3631 // (JSOp::FunctionThis), or the script does not have a this-binding 3632 // (because it doesn't use |this|). 3633 3634 // If our this-argument is an object, or we're in strict mode, 3635 // the this-binding is always the same as our this-argument. 3636 if (initialFrame.thisArgument().isObject() || script->strict()) { 3637 res.set(initialFrame.thisArgument()); 3638 return true; 3639 } 3640 3641 // We didn't initialize the this-binding yet. Determine the 3642 // correct |this| value for this frame (box primitives if not 3643 // in strict mode), and assign it to the this-argument slot so 3644 // JSOp::FunctionThis will use it and not box a second time. 3645 if (!GetFunctionThis(cx, initialFrame, res)) { 3646 return false; 3647 } 3648 initialFrame.thisArgument() = res; 3649 return true; 3650 } 3651 } 3652 3653 if (!script->functionHasThisBinding()) { 3654 res.setMagic(JS_OPTIMIZED_OUT); 3655 return true; 3656 } 3657 3658 for (Rooted<BindingIter> bi(cx, BindingIter(script)); bi; bi++) { 3659 if (bi.name() != cx->names().dot_this_) { 3660 continue; 3661 } 3662 3663 BindingLocation loc = bi.location(); 3664 if (loc.kind() == BindingLocation::Kind::Environment) { 3665 RootedObject callObj(cx, &ei.environment().as<CallObject>()); 3666 return GetProperty(cx, callObj, callObj, bi.name()->asPropertyName(), 3667 res); 3668 } 3669 3670 if (loc.kind() == BindingLocation::Kind::Frame) { 3671 if (ei.withinInitialFrame()) { 3672 res.set(ei.initialFrame().unaliasedLocal(loc.slot())); 3673 return true; 3674 } 3675 3676 if (ei.hasAnyEnvironmentObject()) { 3677 RootedObject env(cx, &ei.environment()); 3678 AbstractGeneratorObject* genObj = 3679 GetGeneratorObjectForEnvironment(cx, env); 3680 if (genObj && genObj->isSuspended() && genObj->hasStackStorage()) { 3681 res.set(genObj->getUnaliasedLocal(loc.slot())); 3682 return true; 3683 } 3684 } 3685 } 3686 3687 res.setMagic(JS_OPTIMIZED_OUT); 3688 return true; 3689 } 3690 3691 MOZ_CRASH("'this' binding must be found"); 3692 } 3693 3694 GetNonSyntacticGlobalThis(cx, envChain, res); 3695 return true; 3696 } 3697 3698 bool js::GetThisValueForDebuggerFrameMaybeOptimizedOut(JSContext* cx, 3699 AbstractFramePtr frame, 3700 const jsbytecode* pc, 3701 MutableHandleValue res) { 3702 RootedObject envChain(cx); 3703 Rooted<Scope*> scope(cx); 3704 if (!GetFrameEnvironmentAndScope(cx, frame, pc, &envChain, &scope)) { 3705 return false; 3706 } 3707 3708 EnvironmentIter ei(cx, envChain, scope, frame); 3709 return GetThisValueForDebuggerEnvironmentIterMaybeOptimizedOut( 3710 cx, ei, envChain, pc, res); 3711 } 3712 3713 bool js::GetThisValueForDebuggerSuspendedGeneratorMaybeOptimizedOut( 3714 JSContext* cx, AbstractGeneratorObject& genObj, JSScript* script, 3715 MutableHandleValue res) { 3716 auto [env, scope] = GetSuspendedGeneratorEnvironmentAndScope(genObj, script); 3717 Rooted<JSObject*> envChain(cx, env); 3718 3719 EnvironmentIter ei(cx, envChain, scope); 3720 return GetThisValueForDebuggerEnvironmentIterMaybeOptimizedOut( 3721 cx, ei, envChain, nullptr, res); 3722 } 3723 3724 static void ReportRuntimeRedeclaration(JSContext* cx, 3725 Handle<PropertyName*> name, 3726 const char* redeclKind) { 3727 if (UniqueChars printable = AtomToPrintableString(cx, name)) { 3728 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3729 JSMSG_REDECLARED_VAR, redeclKind, 3730 printable.get()); 3731 } 3732 } 3733 3734 [[nodiscard]] static bool CheckLexicalNameConflict( 3735 JSContext* cx, Handle<ExtensibleLexicalEnvironmentObject*> lexicalEnv, 3736 HandleObject varObj, Handle<PropertyName*> name) { 3737 const char* redeclKind = nullptr; 3738 RootedId id(cx, NameToId(name)); 3739 mozilla::Maybe<PropertyInfo> prop, shadowedExistingProp; 3740 3741 if ((prop = lexicalEnv->lookup(cx, name))) { 3742 // ES 15.1.11 step 5.b 3743 redeclKind = prop->writable() ? "let" : "const"; 3744 } else if (varObj->is<NativeObject>() && 3745 (prop = varObj->as<NativeObject>().lookup(cx, name))) { 3746 // Faster path for ES 15.1.11 step 5.c-d when the shape can be found 3747 // without going through a resolve hook. 3748 if (!prop->configurable()) { 3749 redeclKind = "non-configurable global property"; 3750 } else { 3751 shadowedExistingProp = prop; 3752 } 3753 } else { 3754 // ES 15.1.11 step 5.c-d 3755 Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx); 3756 if (!GetOwnPropertyDescriptor(cx, varObj, id, &desc)) { 3757 return false; 3758 } 3759 if (desc.isSome()) { 3760 if (!desc->configurable()) { 3761 redeclKind = "non-configurable global property"; 3762 } else { 3763 // Note: we don't have to set |shadowedExistingProp| here because if 3764 // |varObj| is a global object, the NativeObject::lookup call above 3765 // ensures this wasn't an existing property (that might require JIT/IC 3766 // invalidation) but a new property defined by a resolve hook. 3767 MOZ_ASSERT_IF(varObj->is<GlobalObject>(), 3768 varObj->getClass()->getResolve()); 3769 MOZ_ASSERT_IF(varObj->is<GlobalObject>(), 3770 varObj->as<GlobalObject>().containsPure(name)); 3771 } 3772 } 3773 } 3774 3775 if (redeclKind) { 3776 ReportRuntimeRedeclaration(cx, name, redeclKind); 3777 return false; 3778 } 3779 if (shadowedExistingProp && varObj->is<GlobalObject>()) { 3780 // Shadowing a configurable global property with a new lexical is one 3781 // of the rare ways to invalidate a GetGName stub. 3782 auto* global = &varObj->as<GlobalObject>(); 3783 global->bumpGenerationCount(); 3784 3785 // Also invalidate GetGName stubs and Ion code relying on an object fuse 3786 // guard to bake in the property's value. 3787 if (global->hasObjectFuse()) { 3788 if (auto* objFuse = cx->zone()->objectFuses.get(global)) { 3789 objFuse->handleShadowedGlobalProperty(cx, *shadowedExistingProp); 3790 } 3791 } 3792 } 3793 3794 return true; 3795 } 3796 3797 [[nodiscard]] static bool CheckVarNameConflict( 3798 JSContext* cx, Handle<LexicalEnvironmentObject*> lexicalEnv, 3799 Handle<PropertyName*> name) { 3800 mozilla::Maybe<PropertyInfo> prop = lexicalEnv->lookup(cx, name); 3801 if (prop.isSome()) { 3802 ReportRuntimeRedeclaration(cx, name, prop->writable() ? "let" : "const"); 3803 return false; 3804 } 3805 return true; 3806 } 3807 3808 static void ReportCannotDeclareGlobalBinding(JSContext* cx, 3809 Handle<PropertyName*> name, 3810 const char* reason) { 3811 if (UniqueChars printable = AtomToPrintableString(cx, name)) { 3812 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3813 JSMSG_CANT_DECLARE_GLOBAL_BINDING, 3814 printable.get(), reason); 3815 } 3816 } 3817 3818 [[nodiscard]] static bool CheckCanDeclareGlobalBinding( 3819 JSContext* cx, Handle<GlobalObject*> global, Handle<PropertyName*> name, 3820 bool isFunction) { 3821 RootedId id(cx, NameToId(name)); 3822 Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx); 3823 if (!GetOwnPropertyDescriptor(cx, global, id, &desc)) { 3824 return false; 3825 } 3826 3827 // ES 8.1.1.4.15 CanDeclareGlobalVar 3828 // ES 8.1.1.4.16 CanDeclareGlobalFunction 3829 3830 // Step 4. 3831 if (desc.isNothing()) { 3832 // 8.1.14.15 step 6. 3833 // 8.1.14.16 step 5. 3834 if (global->isExtensible()) { 3835 return true; 3836 } 3837 3838 ReportCannotDeclareGlobalBinding(cx, name, "global is non-extensible"); 3839 return false; 3840 } 3841 3842 // Global functions have additional restrictions. 3843 if (isFunction) { 3844 // 8.1.14.16 step 6. 3845 if (desc->configurable()) { 3846 return true; 3847 } 3848 3849 // 8.1.14.16 step 7. 3850 if (desc->isDataDescriptor() && desc->writable() && desc->enumerable()) { 3851 return true; 3852 } 3853 3854 ReportCannotDeclareGlobalBinding(cx, name, 3855 "property must be configurable or " 3856 "both writable and enumerable"); 3857 return false; 3858 } 3859 3860 return true; 3861 } 3862 3863 // Add the var/let/const bindings to the variables environment of a global or 3864 // sloppy-eval script. The redeclaration checks should already have been 3865 // performed. 3866 static bool InitGlobalOrEvalDeclarations( 3867 JSContext* cx, HandleScript script, 3868 Handle<ExtensibleLexicalEnvironmentObject*> lexicalEnv, 3869 HandleObject varObj) { 3870 Rooted<BindingIter> bi(cx, BindingIter(script)); 3871 for (; bi; bi++) { 3872 if (bi.isTopLevelFunction()) { 3873 continue; 3874 } 3875 3876 Rooted<PropertyName*> name(cx, bi.name()->asPropertyName()); 3877 unsigned attrs = script->isForEval() ? JSPROP_ENUMERATE 3878 : JSPROP_ENUMERATE | JSPROP_PERMANENT; 3879 3880 switch (bi.kind()) { 3881 case BindingKind::Var: { 3882 PropertyResult prop; 3883 RootedObject obj2(cx); 3884 if (!LookupProperty(cx, varObj, name, &obj2, &prop)) { 3885 return false; 3886 } 3887 3888 if (prop.isNotFound() || 3889 (obj2 != varObj && varObj->is<GlobalObject>())) { 3890 if (!DefineDataProperty(cx, varObj, name, UndefinedHandleValue, 3891 attrs)) { 3892 return false; 3893 } 3894 } 3895 3896 break; 3897 } 3898 3899 case BindingKind::Const: 3900 attrs |= JSPROP_READONLY; 3901 [[fallthrough]]; 3902 3903 case BindingKind::Let: { 3904 RootedId id(cx, NameToId(name)); 3905 RootedValue uninitialized(cx, MagicValue(JS_UNINITIALIZED_LEXICAL)); 3906 if (!NativeDefineDataProperty(cx, lexicalEnv, id, uninitialized, 3907 attrs)) { 3908 return false; 3909 } 3910 3911 break; 3912 } 3913 3914 default: 3915 MOZ_CRASH("Expected binding kind"); 3916 return false; 3917 } 3918 } 3919 3920 return true; 3921 } 3922 3923 // Define the hoisted top-level functions on the variables environment of a 3924 // global or sloppy-eval script. Redeclaration checks must already have been 3925 // performed. 3926 static bool InitHoistedFunctionDeclarations(JSContext* cx, HandleScript script, 3927 HandleObject envChain, 3928 HandleObject varObj, 3929 GCThingIndex lastFun) { 3930 // The inner-functions up to `lastFun` are the hoisted function declarations 3931 // of the script. We must clone and bind them now. 3932 for (size_t i = 0; i <= lastFun; ++i) { 3933 JS::GCCellPtr thing = script->gcthings()[i]; 3934 3935 // Skip the initial scopes. In practice, there is at most one variables and 3936 // one lexical scope. 3937 if (thing.is<js::Scope>()) { 3938 MOZ_ASSERT(i < 2); 3939 continue; 3940 } 3941 3942 RootedFunction fun(cx, &thing.as<JSObject>().as<JSFunction>()); 3943 Rooted<PropertyName*> name(cx, fun->fullExplicitName()->asPropertyName()); 3944 3945 // Clone the function before exposing to script as a binding. 3946 JSObject* clone = Lambda(cx, fun, envChain); 3947 if (!clone) { 3948 return false; 3949 } 3950 RootedValue rval(cx, ObjectValue(*clone)); 3951 3952 PropertyResult prop; 3953 RootedObject pobj(cx); 3954 if (!LookupProperty(cx, varObj, name, &pobj, &prop)) { 3955 return false; 3956 } 3957 3958 // ECMA requires functions defined when entering Eval code to be 3959 // impermanent. 3960 unsigned attrs = script->isForEval() ? JSPROP_ENUMERATE 3961 : JSPROP_ENUMERATE | JSPROP_PERMANENT; 3962 3963 if (prop.isNotFound() || pobj != varObj) { 3964 if (!DefineDataProperty(cx, varObj, name, rval, attrs)) { 3965 return false; 3966 } 3967 3968 // Done processing this function. 3969 continue; 3970 } 3971 3972 /* 3973 * A DebugEnvironmentProxy is okay here, and sometimes necessary. If 3974 * Debugger.Frame.prototype.eval defines a function with the same name as an 3975 * extant variable in the frame, the DebugEnvironmentProxy takes care of 3976 * storing the function in the stack frame (for non-aliased variables) or on 3977 * the scope object (for aliased). 3978 */ 3979 MOZ_ASSERT(varObj->is<NativeObject>() || 3980 varObj->is<DebugEnvironmentProxy>()); 3981 if (varObj->is<GlobalObject>()) { 3982 PropertyInfo propInfo = prop.propertyInfo(); 3983 if (propInfo.configurable()) { 3984 if (!DefineDataProperty(cx, varObj, name, rval, attrs)) { 3985 return false; 3986 } 3987 } else { 3988 MOZ_ASSERT(propInfo.isDataProperty()); 3989 MOZ_ASSERT(propInfo.writable()); 3990 MOZ_ASSERT(propInfo.enumerable()); 3991 } 3992 } 3993 3994 /* 3995 * Non-global properties, and global properties which we aren't simply 3996 * redefining, must be set. First, this preserves their attributes. 3997 * Second, this will produce warnings and/or errors as necessary if the 3998 * specified Call object property is not writable (const). 3999 */ 4000 4001 RootedId id(cx, NameToId(name)); 4002 if (!PutProperty(cx, varObj, id, rval, script->strict())) { 4003 return false; 4004 } 4005 } 4006 4007 return true; 4008 } 4009 4010 [[nodiscard]] static bool CheckGlobalDeclarationConflicts( 4011 JSContext* cx, HandleScript script, 4012 Handle<ExtensibleLexicalEnvironmentObject*> lexicalEnv, 4013 HandleObject varObj) { 4014 // Due to the extensibility of the global lexical environment, we must 4015 // check for redeclaring a binding. 4016 // 4017 // In the case of non-syntactic environment chains, we are checking 4018 // redeclarations against the non-syntactic lexical environment and the 4019 // variables object that the lexical environment corresponds to. 4020 Rooted<PropertyName*> name(cx); 4021 Rooted<BindingIter> bi(cx, BindingIter(script)); 4022 4023 // ES 15.1.11 GlobalDeclarationInstantiation 4024 4025 // Step 6. 4026 // 4027 // Check 'var' declarations do not conflict with existing bindings in the 4028 // global lexical environment. 4029 for (; bi; bi++) { 4030 if (bi.kind() != BindingKind::Var) { 4031 break; 4032 } 4033 name = bi.name()->asPropertyName(); 4034 if (!CheckVarNameConflict(cx, lexicalEnv, name)) { 4035 return false; 4036 } 4037 4038 // Step 10 and 12. 4039 // 4040 // Check that global functions and vars may be declared. 4041 if (varObj->is<GlobalObject>()) { 4042 Handle<GlobalObject*> global = varObj.as<GlobalObject>(); 4043 if (!CheckCanDeclareGlobalBinding(cx, global, name, 4044 bi.isTopLevelFunction())) { 4045 return false; 4046 } 4047 } 4048 } 4049 4050 // Step 5. 4051 // 4052 // Check that lexical bindings do not conflict. 4053 for (; bi; bi++) { 4054 name = bi.name()->asPropertyName(); 4055 if (!CheckLexicalNameConflict(cx, lexicalEnv, varObj, name)) { 4056 return false; 4057 } 4058 } 4059 4060 return true; 4061 } 4062 4063 [[nodiscard]] static bool CheckVarNameConflictsInEnv(JSContext* cx, 4064 HandleScript script, 4065 HandleObject obj) { 4066 Rooted<LexicalEnvironmentObject*> env(cx); 4067 4068 if (obj->is<LexicalEnvironmentObject>()) { 4069 env = &obj->as<LexicalEnvironmentObject>(); 4070 } else if (obj->is<DebugEnvironmentProxy>() && 4071 obj->as<DebugEnvironmentProxy>() 4072 .environment() 4073 .is<LexicalEnvironmentObject>()) { 4074 env = &obj->as<DebugEnvironmentProxy>() 4075 .environment() 4076 .as<LexicalEnvironmentObject>(); 4077 } else { 4078 // Environment cannot contain lexical bindings. 4079 return true; 4080 } 4081 4082 if (env->is<BlockLexicalEnvironmentObject>() && 4083 env->as<BlockLexicalEnvironmentObject>().scope().kind() == 4084 ScopeKind::SimpleCatch) { 4085 // Annex B.3.5 allows redeclaring simple (non-destructured) catch parameters 4086 // with var declarations. 4087 return true; 4088 } 4089 4090 Rooted<PropertyName*> name(cx); 4091 for (BindingIter bi(script); bi; bi++) { 4092 name = bi.name()->asPropertyName(); 4093 if (!CheckVarNameConflict(cx, env, name)) { 4094 return false; 4095 } 4096 } 4097 4098 return true; 4099 } 4100 4101 static bool CheckArgumentsRedeclaration(JSContext* cx, HandleScript script) { 4102 for (BindingIter bi(script); bi; bi++) { 4103 if (bi.name() == cx->names().arguments) { 4104 ReportRuntimeRedeclaration(cx, cx->names().arguments, "let"); 4105 return false; 4106 } 4107 } 4108 4109 return true; 4110 } 4111 4112 static bool CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script, 4113 HandleObject envChain, 4114 HandleObject varObj) { 4115 // Strict eval has its own call objects and we shouldn't end up here. 4116 // 4117 // Non-strict eval may introduce 'var' bindings that conflict with lexical 4118 // bindings in an enclosing lexical scope. 4119 MOZ_ASSERT(!script->bodyScope()->hasEnvironment()); 4120 MOZ_ASSERT(!script->strict()); 4121 4122 MOZ_ASSERT(script->bodyScope()->as<EvalScope>().hasBindings()); 4123 4124 RootedObject env(cx, envChain); 4125 4126 // ES 18.2.1.3. 4127 4128 // Step 5. 4129 // 4130 // Check that a direct eval will not hoist 'var' bindings over lexical 4131 // bindings with the same name. 4132 while (env != varObj) { 4133 if (!CheckVarNameConflictsInEnv(cx, script, env)) { 4134 return false; 4135 } 4136 env = env->enclosingEnvironment(); 4137 } 4138 4139 // Check for redeclared "arguments" in function parameter expressions. 4140 // 4141 // Direct eval in function parameter expressions isn't allowed to redeclare 4142 // the implicit "arguments" bindings: 4143 // function f(a = eval("var arguments;")) {} 4144 // 4145 // |varObj| isn't a CallObject when the direct eval occurs in the function 4146 // body and the extra function body var scope is present. The extra var scope 4147 // is present iff the function has parameter expressions. So when we test 4148 // that |varObj| is a CallObject and function parameter expressions are 4149 // present, we can pinpoint the direct eval location to be in a function 4150 // parameter expression. Additionally we must ensure the function isn't an 4151 // arrow function, because arrow functions don't have an implicit "arguments" 4152 // binding. 4153 if (script->isDirectEvalInFunction() && varObj->is<CallObject>()) { 4154 JSFunction* fun = &varObj->as<CallObject>().callee(); 4155 JSScript* funScript = fun->nonLazyScript(); 4156 if (funScript->functionHasParameterExprs() && !fun->isArrow()) { 4157 if (!CheckArgumentsRedeclaration(cx, script)) { 4158 return false; 4159 } 4160 } 4161 } 4162 4163 // Step 8. 4164 // 4165 // Check that global functions may be declared. 4166 if (varObj->is<GlobalObject>()) { 4167 Handle<GlobalObject*> global = varObj.as<GlobalObject>(); 4168 Rooted<PropertyName*> name(cx); 4169 for (Rooted<BindingIter> bi(cx, BindingIter(script)); bi; bi++) { 4170 name = bi.name()->asPropertyName(); 4171 if (!CheckCanDeclareGlobalBinding(cx, global, name, 4172 bi.isTopLevelFunction())) { 4173 return false; 4174 } 4175 } 4176 } 4177 4178 return true; 4179 } 4180 4181 bool js::GlobalOrEvalDeclInstantiation(JSContext* cx, HandleObject envChain, 4182 HandleScript script, 4183 GCThingIndex lastFun) { 4184 MOZ_ASSERT(script->isGlobalCode() || script->isForEval()); 4185 MOZ_ASSERT(!script->selfHosted()); 4186 4187 RootedObject varObj(cx, &GetVariablesObject(envChain)); 4188 Rooted<ExtensibleLexicalEnvironmentObject*> lexicalEnv(cx); 4189 4190 if (script->isForEval()) { 4191 if (!CheckEvalDeclarationConflicts(cx, script, envChain, varObj)) { 4192 return false; 4193 } 4194 } else { 4195 lexicalEnv = &NearestEnclosingExtensibleLexicalEnvironment(envChain); 4196 if (!CheckGlobalDeclarationConflicts(cx, script, lexicalEnv, varObj)) { 4197 return false; 4198 } 4199 } 4200 4201 if (!InitGlobalOrEvalDeclarations(cx, script, lexicalEnv, varObj)) { 4202 return false; 4203 } 4204 4205 return InitHoistedFunctionDeclarations(cx, script, envChain, varObj, lastFun); 4206 } 4207 4208 bool js::InitFunctionEnvironmentObjects(JSContext* cx, AbstractFramePtr frame) { 4209 MOZ_ASSERT(frame.isFunctionFrame()); 4210 MOZ_ASSERT(frame.callee()->needsFunctionEnvironmentObjects()); 4211 4212 RootedFunction callee(cx, frame.callee()); 4213 4214 gc::AllocSite* site = nullptr; 4215 if (frame.isBaselineFrame()) { 4216 site = frame.asBaselineFrame()->icScript()->maybeEnvAllocSite(); 4217 } 4218 4219 // Named lambdas may have an environment that holds itself for recursion. 4220 if (callee->needsNamedLambdaEnvironment()) { 4221 NamedLambdaObject* declEnv = 4222 NamedLambdaObject::createForFrame(cx, frame, site); 4223 if (!declEnv) { 4224 return false; 4225 } 4226 frame.pushOnEnvironmentChain(*declEnv); 4227 } 4228 4229 // If the function has parameter default expressions, there may be an 4230 // extra environment to hold the parameters. 4231 if (callee->needsCallObject()) { 4232 CallObject* callObj = CallObject::createForFrame(cx, frame, site); 4233 if (!callObj) { 4234 return false; 4235 } 4236 frame.pushOnEnvironmentChain(*callObj); 4237 } 4238 4239 return true; 4240 } 4241 4242 bool js::PushVarEnvironmentObject(JSContext* cx, Handle<Scope*> scope, 4243 AbstractFramePtr frame) { 4244 auto* env = VarEnvironmentObject::createForFrame(cx, scope, frame); 4245 if (!env) { 4246 return false; 4247 } 4248 frame.pushOnEnvironmentChain(*env); 4249 return true; 4250 } 4251 4252 static bool GetFrameEnvironmentAndScope(JSContext* cx, AbstractFramePtr frame, 4253 const jsbytecode* pc, 4254 MutableHandleObject env, 4255 MutableHandle<Scope*> scope) { 4256 env.set(frame.environmentChain()); 4257 4258 if (frame.isWasmDebugFrame()) { 4259 Rooted<WasmInstanceObject*> instance(cx, frame.wasmInstance()->object()); 4260 uint32_t funcIndex = frame.asWasmDebugFrame()->funcIndex(); 4261 scope.set(WasmInstanceObject::getFunctionScope(cx, instance, funcIndex)); 4262 if (!scope) { 4263 return false; 4264 } 4265 } else { 4266 scope.set(frame.script()->innermostScope(pc)); 4267 } 4268 return true; 4269 } 4270 4271 #ifdef DEBUG 4272 4273 using PropertyNameSet = HashSet<PropertyName*>; 4274 4275 static bool RemoveReferencedNames(JSContext* cx, HandleScript script, 4276 PropertyNameSet& remainingNames) { 4277 // Remove from remainingNames --- the closure variables in some outer 4278 // script --- any free variables in this script. This analysis isn't perfect: 4279 // 4280 // - It will not account for free variables in an inner script which are 4281 // actually accessing some name in an intermediate script between the 4282 // inner and outer scripts. This can cause remainingNames to be an 4283 // underapproximation. 4284 // 4285 // - It will not account for new names introduced via eval. This can cause 4286 // remainingNames to be an overapproximation. This would be easy to fix 4287 // but is nice to have as the eval will probably not access these 4288 // these names and putting eval in an inner script is bad news if you 4289 // care about entraining variables unnecessarily. 4290 4291 AllBytecodesIterable iter(script); 4292 for (BytecodeLocation loc : iter) { 4293 PropertyName* name; 4294 4295 switch (loc.getOp()) { 4296 case JSOp::GetName: 4297 case JSOp::SetName: 4298 case JSOp::StrictSetName: 4299 name = script->getName(loc.toRawBytecode()); 4300 break; 4301 4302 case JSOp::GetAliasedVar: 4303 case JSOp::SetAliasedVar: 4304 name = EnvironmentCoordinateNameSlow(script, loc.toRawBytecode()); 4305 break; 4306 4307 default: 4308 name = nullptr; 4309 break; 4310 } 4311 4312 if (name) { 4313 remainingNames.remove(name); 4314 } 4315 } 4316 4317 RootedFunction fun(cx); 4318 RootedScript innerScript(cx); 4319 for (JS::GCCellPtr gcThing : script->gcthings()) { 4320 if (!gcThing.is<JSObject>()) { 4321 continue; 4322 } 4323 JSObject* obj = &gcThing.as<JSObject>(); 4324 4325 if (!obj->is<JSFunction>()) { 4326 continue; 4327 } 4328 fun = &obj->as<JSFunction>(); 4329 4330 if (!fun->isInterpreted()) { 4331 continue; 4332 } 4333 4334 innerScript = JSFunction::getOrCreateScript(cx, fun); 4335 if (!innerScript) { 4336 return false; 4337 } 4338 4339 if (!RemoveReferencedNames(cx, innerScript, remainingNames)) { 4340 return false; 4341 } 4342 } 4343 4344 return true; 4345 } 4346 4347 static bool AnalyzeEntrainedVariablesInScript(JSContext* cx, 4348 HandleScript script, 4349 HandleScript innerScript) { 4350 PropertyNameSet remainingNames(cx); 4351 4352 for (BindingIter bi(script); bi; bi++) { 4353 if (bi.closedOver()) { 4354 PropertyName* name = bi.name()->asPropertyName(); 4355 PropertyNameSet::AddPtr p = remainingNames.lookupForAdd(name); 4356 if (!p && !remainingNames.add(p, name)) { 4357 return false; 4358 } 4359 } 4360 } 4361 4362 if (!RemoveReferencedNames(cx, innerScript, remainingNames)) { 4363 return false; 4364 } 4365 4366 if (!remainingNames.empty()) { 4367 Sprinter buf(cx); 4368 if (!buf.init()) { 4369 return false; 4370 } 4371 4372 buf.printf("Script "); 4373 4374 if (JSAtom* name = script->function()->fullDisplayAtom()) { 4375 buf.putString(cx, name); 4376 buf.printf(" "); 4377 } 4378 4379 buf.printf("(%s:%u) has variables entrained by ", script->filename(), 4380 script->lineno()); 4381 4382 if (JSAtom* name = innerScript->function()->fullDisplayAtom()) { 4383 buf.putString(cx, name); 4384 buf.printf(" "); 4385 } 4386 4387 buf.printf("(%s:%u) ::", innerScript->filename(), innerScript->lineno()); 4388 4389 for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); 4390 r.popFront()) { 4391 buf.printf(" "); 4392 buf.putString(cx, r.front()); 4393 } 4394 4395 JS::UniqueChars str = buf.release(); 4396 if (!str) { 4397 return false; 4398 } 4399 printf("%s\n", str.get()); 4400 } 4401 4402 RootedFunction fun(cx); 4403 RootedScript innerInnerScript(cx); 4404 for (JS::GCCellPtr gcThing : script->gcthings()) { 4405 if (!gcThing.is<JSObject>()) { 4406 continue; 4407 } 4408 JSObject* obj = &gcThing.as<JSObject>(); 4409 4410 if (!obj->is<JSFunction>()) { 4411 continue; 4412 } 4413 fun = &obj->as<JSFunction>(); 4414 4415 if (!fun->isInterpreted()) { 4416 continue; 4417 } 4418 4419 innerInnerScript = JSFunction::getOrCreateScript(cx, fun); 4420 if (!innerInnerScript) { 4421 return false; 4422 } 4423 4424 if (!AnalyzeEntrainedVariablesInScript(cx, script, innerInnerScript)) { 4425 return false; 4426 } 4427 } 4428 4429 return true; 4430 } 4431 4432 // Look for local variables in script or any other script inner to it, which are 4433 // part of the script's call object and are unnecessarily entrained by their own 4434 // inner scripts which do not refer to those variables. An example is: 4435 // 4436 // function foo() { 4437 // var a, b; 4438 // function bar() { return a; } 4439 // function baz() { return b; } 4440 // } 4441 // 4442 // |bar| unnecessarily entrains |b|, and |baz| unnecessarily entrains |a|. 4443 bool js::AnalyzeEntrainedVariables(JSContext* cx, HandleScript script) { 4444 RootedFunction fun(cx); 4445 RootedScript innerScript(cx); 4446 for (JS::GCCellPtr gcThing : script->gcthings()) { 4447 if (!gcThing.is<JSObject>()) { 4448 continue; 4449 } 4450 JSObject* obj = &gcThing.as<JSObject>(); 4451 4452 if (!obj->is<JSFunction>()) { 4453 continue; 4454 } 4455 fun = &obj->as<JSFunction>(); 4456 4457 if (!fun->isInterpreted()) { 4458 continue; 4459 } 4460 4461 innerScript = JSFunction::getOrCreateScript(cx, fun); 4462 if (!innerScript) { 4463 return false; 4464 } 4465 4466 if (fun->needsCallObject()) { 4467 if (!AnalyzeEntrainedVariablesInScript(cx, script, innerScript)) { 4468 return false; 4469 } 4470 } 4471 4472 if (!AnalyzeEntrainedVariables(cx, innerScript)) { 4473 return false; 4474 } 4475 } 4476 4477 return true; 4478 } 4479 #endif 4480 4481 JSObject* js::MaybeOptimizeBindUnqualifiedGlobalName(GlobalObject* global, 4482 PropertyName* name) { 4483 // We can bind name to the global lexical scope if the binding already 4484 // exists, is initialized, and is writable (i.e., an initialized 4485 // 'let') at compile time. 4486 GlobalLexicalEnvironmentObject* env = &global->lexicalEnvironment(); 4487 mozilla::Maybe<PropertyInfo> prop = env->lookupPure(name); 4488 if (prop.isSome()) { 4489 if (prop->writable() && 4490 !env->getSlot(prop->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) { 4491 return env; 4492 } 4493 return nullptr; 4494 } 4495 4496 prop = global->lookupPure(name); 4497 if (prop.isSome()) { 4498 // If the property does not currently exist on the global lexical 4499 // scope, we can bind name to the global object if the property 4500 // exists on the global and is non-configurable, as then it cannot 4501 // be shadowed. 4502 if (!prop->configurable()) { 4503 return global; 4504 } 4505 } 4506 4507 return nullptr; 4508 } 4509 4510 const char* EnvironmentObject::typeString() const { 4511 if (is<CallObject>()) { 4512 return "CallObject"; 4513 } 4514 if (is<VarEnvironmentObject>()) { 4515 return "VarEnvironmentObject"; 4516 } 4517 if (is<ModuleEnvironmentObject>()) { 4518 return "ModuleEnvironmentObject"; 4519 } 4520 if (is<WasmInstanceEnvironmentObject>()) { 4521 return "WasmInstanceEnvironmentObject"; 4522 } 4523 if (is<WasmFunctionCallObject>()) { 4524 return "WasmFunctionCallObject"; 4525 } 4526 if (is<LexicalEnvironmentObject>()) { 4527 if (is<ScopedLexicalEnvironmentObject>()) { 4528 if (is<BlockLexicalEnvironmentObject>()) { 4529 if (is<NamedLambdaObject>()) { 4530 return "NamedLambdaObject"; 4531 } 4532 return "BlockLexicalEnvironmentObject"; 4533 } 4534 if (is<ClassBodyLexicalEnvironmentObject>()) { 4535 return "ClassBodyLexicalEnvironmentObject"; 4536 } 4537 return "ScopedLexicalEnvironmentObject"; 4538 } 4539 4540 if (is<ExtensibleLexicalEnvironmentObject>()) { 4541 if (is<GlobalLexicalEnvironmentObject>()) { 4542 return "GlobalLexicalEnvironmentObject"; 4543 } 4544 if (is<NonSyntacticLexicalEnvironmentObject>()) { 4545 return "NonSyntacticLexicalEnvironmentObject"; 4546 } 4547 return "ExtensibleLexicalEnvironmentObject"; 4548 } 4549 4550 return "LexicalEnvironmentObject"; 4551 } 4552 if (is<NonSyntacticVariablesObject>()) { 4553 return "NonSyntacticVariablesObject"; 4554 } 4555 if (is<WithEnvironmentObject>()) { 4556 return "WithEnvironmentObject"; 4557 } 4558 if (is<RuntimeLexicalErrorObject>()) { 4559 return "RuntimeLexicalErrorObject"; 4560 } 4561 4562 return "EnvironmentObject"; 4563 } 4564 4565 #if defined(DEBUG) || defined(JS_JITSPEW) 4566 static void DumpEnvironmentObject(JSObject* unrootedEnvObj) { 4567 JSContext* cx = TlsContext.get(); 4568 if (!cx) { 4569 fprintf(stderr, "*** can't get JSContext for current thread\n"); 4570 return; 4571 } 4572 4573 Rooted<JSObject*> envObj(cx, unrootedEnvObj); 4574 while (envObj) { 4575 Rooted<EnvironmentObject*> env(cx); 4576 if (envObj->is<EnvironmentObject>()) { 4577 env = &envObj->as<EnvironmentObject>(); 4578 } else if (envObj->is<DebugEnvironmentProxy>()) { 4579 fprintf(stderr, "[DebugProxy] "); 4580 env = &envObj->as<DebugEnvironmentProxy>().environment(); 4581 } else { 4582 MOZ_ASSERT(envObj->is<GlobalObject>()); 4583 fprintf(stderr, "global\n"); 4584 break; 4585 } 4586 4587 fprintf(stderr, "%s (%p)", env->typeString(), env.get()); 4588 4589 Rooted<Scope*> scope(cx); 4590 if (env->is<VarEnvironmentObject>()) { 4591 scope = &env->as<VarEnvironmentObject>().scope(); 4592 } 4593 if (env->is<WasmInstanceEnvironmentObject>()) { 4594 scope = &env->as<WasmInstanceEnvironmentObject>().scope(); 4595 } else if (env->is<WasmFunctionCallObject>()) { 4596 scope = &env->as<WasmFunctionCallObject>().scope(); 4597 } else if (env->is<ScopedLexicalEnvironmentObject>()) { 4598 scope = &env->as<ScopedLexicalEnvironmentObject>().scope(); 4599 } 4600 4601 bool hadProp = false; 4602 4603 // Set of names in the scope. 4604 // This is used for filtering out those names from properties bwlow. 4605 Rooted<GCHashSet<JSAtom*>> names(cx, GCHashSet<JSAtom*>(cx, 0)); 4606 4607 if (scope) { 4608 if (!hadProp) { 4609 fprintf(stderr, " {\n"); 4610 } 4611 hadProp = true; 4612 for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) { 4613 fprintf(stderr, " "); 4614 4615 switch (bi.location().kind()) { 4616 case BindingLocation::Kind::Global: 4617 if (bi.isTopLevelFunction()) { 4618 fprintf(stderr, "global function: "); 4619 } else { 4620 fprintf(stderr, "global: "); 4621 } 4622 break; 4623 case BindingLocation::Kind::Argument: 4624 fprintf(stderr, "arg slot %u: ", bi.location().argumentSlot()); 4625 break; 4626 case BindingLocation::Kind::Frame: 4627 fprintf(stderr, "frame slot %u: ", bi.location().slot()); 4628 break; 4629 case BindingLocation::Kind::Environment: 4630 fprintf(stderr, "env slot %u: %s ", bi.location().slot(), 4631 BindingKindString(bi.kind())); 4632 break; 4633 case BindingLocation::Kind::NamedLambdaCallee: 4634 fprintf(stderr, "named lambda callee: "); 4635 break; 4636 case BindingLocation::Kind::Import: 4637 fprintf(stderr, "import: "); 4638 break; 4639 } 4640 4641 JSAtom* name = bi.name(); 4642 if (!names.put(name)) { 4643 fprintf(stderr, " *** out of memory\n"); 4644 return; 4645 } 4646 4647 UniqueChars bytes = AtomToPrintableString(cx, name); 4648 if (!bytes) { 4649 fprintf(stderr, " *** out of memory\n"); 4650 return; 4651 } 4652 fprintf(stderr, "%s\n", bytes.get()); 4653 } 4654 } 4655 4656 // The environment object can have random properties that can be found in 4657 // the name lookup. Show them as well, excluding the properties which 4658 // are already shown above for the scope. 4659 if (PropMap* map = env->shape()->propMap()) { 4660 Vector<PropMap*, 8, SystemAllocPolicy> maps; 4661 while (true) { 4662 if (!maps.append(map)) { 4663 fprintf(stderr, " *** out of memory\n"); 4664 return; 4665 } 4666 if (!map->hasPrevious()) { 4667 break; 4668 } 4669 map = map->asLinked()->previous(); 4670 } 4671 4672 for (size_t i = maps.length(); i > 0; i--) { 4673 size_t index = i - 1; 4674 PropMap* map = maps[index]; 4675 uint32_t len = (index == 0) ? env->shape()->asNative().propMapLength() 4676 : PropMap::Capacity; 4677 for (uint32_t j = 0; j < len; j++) { 4678 if (!map->hasKey(j)) { 4679 MOZ_ASSERT(map->isDictionary()); 4680 continue; 4681 } 4682 4683 PropertyKey propKey = map->getKey(j); 4684 if (propKey.isAtom()) { 4685 JSAtom* name = propKey.toAtom(); 4686 if (names.has(name)) { 4687 continue; 4688 } 4689 } 4690 4691 JS::UniqueChars propChars = map->getPropertyNameAt(j); 4692 if (!propChars) { 4693 fprintf(stderr, " *** out of memory\n"); 4694 return; 4695 } 4696 4697 if (!hadProp) { 4698 fprintf(stderr, " {\n"); 4699 } 4700 hadProp = true; 4701 4702 PropertyInfo prop = map->getPropertyInfo(j); 4703 if (prop.hasSlot()) { 4704 fprintf(stderr, " prop %u: %s\n", prop.slot(), propChars.get()); 4705 } else { 4706 fprintf(stderr, " prop: %s\n", propChars.get()); 4707 } 4708 } 4709 } 4710 } 4711 4712 if (hadProp) { 4713 fprintf(stderr, "}"); 4714 } 4715 4716 fprintf(stderr, "\n"); 4717 4718 if (envObj->is<DebugEnvironmentProxy>()) { 4719 envObj = &envObj->as<DebugEnvironmentProxy>().enclosingEnvironment(); 4720 } else { 4721 envObj = &env->enclosingEnvironment(); 4722 } 4723 4724 if (envObj) { 4725 fprintf(stderr, "-> "); 4726 } 4727 } 4728 } 4729 4730 void EnvironmentObject::dump() { DumpEnvironmentObject(this); } 4731 4732 void DebugEnvironmentProxy::dump() { DumpEnvironmentObject(this); } 4733 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */