JSObject.cpp (111128B)
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 /* 8 * JS object implementation. 9 */ 10 11 #include "vm/JSObject-inl.h" 12 13 #include "mozilla/MemoryReporting.h" 14 #include "mozilla/Try.h" 15 16 #include <string.h> 17 18 #include "jsapi.h" 19 #include "jsdate.h" 20 #include "jsexn.h" 21 #include "jsfriendapi.h" 22 #include "jsnum.h" 23 #include "jstypes.h" 24 25 #include "builtin/BigInt.h" 26 #include "builtin/MapObject.h" 27 #include "builtin/Object.h" 28 #include "builtin/String.h" 29 #include "builtin/Symbol.h" 30 #include "builtin/WeakSetObject.h" 31 #include "gc/AllocKind.h" 32 #include "gc/GC.h" 33 #include "js/CharacterEncoding.h" 34 #include "js/friend/DumpFunctions.h" // js::DumpObject 35 #include "js/friend/ErrorMessages.h" // JSErrNum, js::GetErrorMessage, JSMSG_* 36 #include "js/friend/WindowProxy.h" // js::IsWindow, js::ToWindowProxyIfWindow 37 #include "js/MemoryMetrics.h" 38 #include "js/Prefs.h" // JS::Prefs 39 #include "js/Printer.h" // js::GenericPrinter, js::Fprinter 40 #include "js/PropertyDescriptor.h" // JS::FromPropertyDescriptor 41 #include "js/PropertySpec.h" // JSPropertySpec 42 #include "js/Proxy.h" 43 #include "js/Result.h" 44 #include "js/UbiNode.h" 45 #include "js/Wrapper.h" 46 #include "proxy/DeadObjectProxy.h" 47 #include "util/Memory.h" 48 #include "util/Text.h" 49 #include "util/WindowsWrapper.h" 50 #include "vm/ArgumentsObject.h" 51 #include "vm/ArrayBufferObject.h" 52 #include "vm/ArrayBufferViewObject.h" 53 #include "vm/BytecodeUtil.h" 54 #include "vm/Compartment.h" 55 #include "vm/DateObject.h" 56 #include "vm/Interpreter.h" 57 #include "vm/Iteration.h" 58 #include "vm/JSAtomUtils.h" // Atomize 59 #include "vm/JSContext.h" 60 #include "vm/JSFunction.h" 61 #include "vm/JSONPrinter.h" // js::JSONPrinter 62 #include "vm/JSScript.h" 63 #include "vm/PromiseObject.h" 64 #include "vm/ProxyObject.h" 65 #include "vm/RegExpObject.h" 66 #include "vm/SelfHosting.h" 67 #include "vm/Shape.h" 68 #include "vm/TypedArrayObject.h" 69 #include "vm/Watchtower.h" 70 #include "vm/WrapperObject.h" 71 #include "gc/StableCellHasher-inl.h" 72 #include "vm/BooleanObject-inl.h" 73 #include "vm/EnvironmentObject-inl.h" 74 #include "vm/Interpreter-inl.h" 75 #include "vm/JSAtomUtils-inl.h" // AtomToId, PrimitiveValueToId, IndexToId 76 #include "vm/JSContext-inl.h" 77 #include "vm/NativeObject-inl.h" 78 #include "vm/NumberObject-inl.h" 79 #include "vm/ObjectFlags-inl.h" 80 #include "vm/Realm-inl.h" 81 #include "vm/StringObject-inl.h" 82 #include "vm/TypedArrayObject-inl.h" 83 #include "wasm/WasmGcObject-inl.h" 84 85 using namespace js; 86 87 using mozilla::Maybe; 88 89 void js::ReportNotObject(JSContext* cx, JSErrNum err, int spindex, 90 HandleValue v) { 91 MOZ_ASSERT(!v.isObject()); 92 ReportValueError(cx, err, spindex, v, nullptr); 93 } 94 95 void js::ReportNotObject(JSContext* cx, JSErrNum err, HandleValue v) { 96 ReportNotObject(cx, err, JSDVG_SEARCH_STACK, v); 97 } 98 99 void js::ReportNotObject(JSContext* cx, const Value& v) { 100 RootedValue value(cx, v); 101 ReportNotObject(cx, JSMSG_OBJECT_REQUIRED, value); 102 } 103 104 void js::ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, 105 HandleValue v) { 106 MOZ_ASSERT(!v.isObject()); 107 108 UniqueChars bytes; 109 const char* chars = ValueToSourceForError(cx, v, bytes); 110 MOZ_ASSERT(chars); 111 JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, 112 JSMSG_OBJECT_REQUIRED_ARG, nth, fun, chars); 113 } 114 115 JS_PUBLIC_API const char* JS::InformalValueTypeName(const Value& v) { 116 switch (v.type()) { 117 case ValueType::Double: 118 case ValueType::Int32: 119 return "number"; 120 case ValueType::Boolean: 121 return "boolean"; 122 case ValueType::Undefined: 123 return "undefined"; 124 case ValueType::Null: 125 return "null"; 126 case ValueType::String: 127 return "string"; 128 case ValueType::Symbol: 129 return "symbol"; 130 case ValueType::BigInt: 131 return "bigint"; 132 case ValueType::Object: 133 return v.toObject().getClass()->name; 134 case ValueType::Magic: 135 return "magic"; 136 case ValueType::PrivateGCThing: 137 break; 138 } 139 140 MOZ_CRASH("unexpected type"); 141 } 142 143 // ES6 draft rev37 6.2.4.4 FromPropertyDescriptor 144 JS_PUBLIC_API bool JS::FromPropertyDescriptor( 145 JSContext* cx, Handle<Maybe<PropertyDescriptor>> desc_, 146 MutableHandleValue vp) { 147 AssertHeapIsIdle(); 148 CHECK_THREAD(cx); 149 cx->check(desc_); 150 151 // Step 1. 152 if (desc_.isNothing()) { 153 vp.setUndefined(); 154 return true; 155 } 156 157 Rooted<PropertyDescriptor> desc(cx, *desc_); 158 return FromPropertyDescriptorToObject(cx, desc, vp); 159 } 160 161 bool js::FromPropertyDescriptorToObject(JSContext* cx, 162 Handle<PropertyDescriptor> desc, 163 MutableHandleValue vp) { 164 // Step 2-3. 165 RootedObject obj(cx, NewPlainObject(cx)); 166 if (!obj) { 167 return false; 168 } 169 170 const JSAtomState& names = cx->names(); 171 172 // Step 4. 173 if (desc.hasValue()) { 174 if (!DefineDataProperty(cx, obj, names.value, desc.value())) { 175 return false; 176 } 177 } 178 179 // Step 5. 180 RootedValue v(cx); 181 if (desc.hasWritable()) { 182 v.setBoolean(desc.writable()); 183 if (!DefineDataProperty(cx, obj, names.writable, v)) { 184 return false; 185 } 186 } 187 188 // Step 6. 189 if (desc.hasGetter()) { 190 if (JSObject* get = desc.getter()) { 191 v.setObject(*get); 192 } else { 193 v.setUndefined(); 194 } 195 if (!DefineDataProperty(cx, obj, names.get, v)) { 196 return false; 197 } 198 } 199 200 // Step 7. 201 if (desc.hasSetter()) { 202 if (JSObject* set = desc.setter()) { 203 v.setObject(*set); 204 } else { 205 v.setUndefined(); 206 } 207 if (!DefineDataProperty(cx, obj, names.set, v)) { 208 return false; 209 } 210 } 211 212 // Step 8. 213 if (desc.hasEnumerable()) { 214 v.setBoolean(desc.enumerable()); 215 if (!DefineDataProperty(cx, obj, names.enumerable, v)) { 216 return false; 217 } 218 } 219 220 // Step 9. 221 if (desc.hasConfigurable()) { 222 v.setBoolean(desc.configurable()); 223 if (!DefineDataProperty(cx, obj, names.configurable, v)) { 224 return false; 225 } 226 } 227 228 vp.setObject(*obj); 229 return true; 230 } 231 232 bool js::GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, 233 const char* method, 234 MutableHandleObject objp) { 235 if (!args.requireAtLeast(cx, method, 1)) { 236 return false; 237 } 238 239 HandleValue v = args[0]; 240 if (!v.isObject()) { 241 UniqueChars bytes = 242 DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, nullptr); 243 if (!bytes) { 244 return false; 245 } 246 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 247 JSMSG_UNEXPECTED_TYPE, bytes.get(), 248 "not an object"); 249 return false; 250 } 251 252 objp.set(&v.toObject()); 253 return true; 254 } 255 256 static bool GetPropertyIfPresent(JSContext* cx, HandleObject obj, HandleId id, 257 MutableHandleValue vp, bool* foundp) { 258 if (!HasProperty(cx, obj, id, foundp)) { 259 return false; 260 } 261 if (!*foundp) { 262 vp.setUndefined(); 263 return true; 264 } 265 266 return GetProperty(cx, obj, obj, id, vp); 267 } 268 269 bool js::Throw(JSContext* cx, HandleId id, unsigned errorNumber, 270 const char* details) { 271 MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == (details ? 2 : 1)); 272 MOZ_ASSERT_IF(details, JS::StringIsASCII(details)); 273 274 UniqueChars bytes = 275 IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsPropertyKey); 276 if (!bytes) { 277 return false; 278 } 279 280 if (details) { 281 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, 282 bytes.get(), details); 283 } else { 284 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber, 285 bytes.get()); 286 } 287 288 return false; 289 } 290 291 /*** PropertyDescriptor operations and DefineProperties *********************/ 292 293 static Result<> CheckCallable(JSContext* cx, JSObject* obj, 294 const char* fieldName) { 295 if (obj && !obj->isCallable()) { 296 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 297 JSMSG_BAD_GET_SET_FIELD, fieldName); 298 return cx->alreadyReportedError(); 299 } 300 return Ok(); 301 } 302 303 // 6.2.5.5 ToPropertyDescriptor(Obj) 304 bool js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, 305 bool checkAccessors, 306 MutableHandle<PropertyDescriptor> desc_) { 307 // Step 1. 308 RootedObject obj(cx, 309 RequireObject(cx, JSMSG_OBJECT_REQUIRED_PROP_DESC, descval)); 310 if (!obj) { 311 return false; 312 } 313 314 // Step 2. 315 Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Empty()); 316 317 RootedId id(cx); 318 RootedValue v(cx); 319 320 // Steps 3-4. 321 id = NameToId(cx->names().enumerable); 322 bool hasEnumerable = false; 323 if (!GetPropertyIfPresent(cx, obj, id, &v, &hasEnumerable)) { 324 return false; 325 } 326 if (hasEnumerable) { 327 desc.setEnumerable(ToBoolean(v)); 328 } 329 330 // Steps 5-6. 331 id = NameToId(cx->names().configurable); 332 bool hasConfigurable = false; 333 if (!GetPropertyIfPresent(cx, obj, id, &v, &hasConfigurable)) { 334 return false; 335 } 336 if (hasConfigurable) { 337 desc.setConfigurable(ToBoolean(v)); 338 } 339 340 // Steps 7-8. 341 id = NameToId(cx->names().value); 342 bool hasValue = false; 343 if (!GetPropertyIfPresent(cx, obj, id, &v, &hasValue)) { 344 return false; 345 } 346 if (hasValue) { 347 desc.setValue(v); 348 } 349 350 // Steps 9-10. 351 id = NameToId(cx->names().writable); 352 bool hasWritable = false; 353 if (!GetPropertyIfPresent(cx, obj, id, &v, &hasWritable)) { 354 return false; 355 } 356 if (hasWritable) { 357 desc.setWritable(ToBoolean(v)); 358 } 359 360 // Steps 11-12. 361 id = NameToId(cx->names().get); 362 bool hasGet = false; 363 if (!GetPropertyIfPresent(cx, obj, id, &v, &hasGet)) { 364 return false; 365 } 366 RootedObject getter(cx); 367 if (hasGet) { 368 if (v.isObject()) { 369 if (checkAccessors) { 370 JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), "getter")); 371 } 372 getter = &v.toObject(); 373 } else if (v.isUndefined()) { 374 getter = nullptr; 375 } else { 376 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 377 JSMSG_BAD_GET_SET_FIELD, "getter"); 378 return false; 379 } 380 } 381 382 // Steps 13-14. 383 id = NameToId(cx->names().set); 384 bool hasSet = false; 385 if (!GetPropertyIfPresent(cx, obj, id, &v, &hasSet)) { 386 return false; 387 } 388 RootedObject setter(cx); 389 if (hasSet) { 390 if (v.isObject()) { 391 if (checkAccessors) { 392 JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), "setter")); 393 } 394 setter = &v.toObject(); 395 } else if (v.isUndefined()) { 396 setter = nullptr; 397 } else { 398 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 399 JSMSG_BAD_GET_SET_FIELD, "setter"); 400 return false; 401 } 402 } 403 404 // Step 15. 405 if (hasGet || hasSet) { 406 if (hasValue || hasWritable) { 407 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 408 JSMSG_INVALID_DESCRIPTOR); 409 return false; 410 } 411 412 // We delay setGetter/setSetter after the previous check, 413 // because otherwise we would assert. 414 if (hasGet) { 415 desc.setGetter(getter); 416 } 417 if (hasSet) { 418 desc.setSetter(setter); 419 } 420 } 421 422 desc.assertValid(); 423 desc_.set(desc); 424 return true; 425 } 426 427 Result<> js::CheckPropertyDescriptorAccessors(JSContext* cx, 428 Handle<PropertyDescriptor> desc) { 429 if (desc.hasGetter()) { 430 MOZ_TRY(CheckCallable(cx, desc.getter(), "getter")); 431 } 432 433 if (desc.hasSetter()) { 434 MOZ_TRY(CheckCallable(cx, desc.setter(), "setter")); 435 } 436 437 return Ok(); 438 } 439 440 // 6.2.5.6 CompletePropertyDescriptor(Desc) 441 void js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc) { 442 // Step 1. 443 desc.assertValid(); 444 445 // Step 2. 446 // Let like be the Record { [[Value]]: undefined, [[Writable]]: false, 447 // [[Get]]: undefined, [[Set]]: undefined, 448 // [[Enumerable]]: false, [[Configurable]]: false }. 449 450 // Step 3. 451 if (desc.isGenericDescriptor() || desc.isDataDescriptor()) { 452 // Step 3.a. 453 if (!desc.hasValue()) { 454 desc.setValue(UndefinedHandleValue); 455 } 456 // Step 3.b. 457 if (!desc.hasWritable()) { 458 desc.setWritable(false); 459 } 460 } else { 461 // Step 4.a. 462 if (!desc.hasGetter()) { 463 desc.setGetter(nullptr); 464 } 465 // Step 4.b. 466 if (!desc.hasSetter()) { 467 desc.setSetter(nullptr); 468 } 469 } 470 471 // Step 5. 472 if (!desc.hasEnumerable()) { 473 desc.setEnumerable(false); 474 } 475 476 // Step 6. 477 if (!desc.hasConfigurable()) { 478 desc.setConfigurable(false); 479 } 480 481 desc.assertComplete(); 482 } 483 484 bool js::ReadPropertyDescriptors( 485 JSContext* cx, HandleObject props, bool checkAccessors, 486 MutableHandleIdVector ids, MutableHandle<PropertyDescriptorVector> descs) { 487 if (!GetPropertyKeys(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids)) { 488 return false; 489 } 490 491 RootedId id(cx); 492 for (size_t i = 0, len = ids.length(); i < len; i++) { 493 id = ids[i]; 494 Rooted<PropertyDescriptor> desc(cx); 495 RootedValue v(cx); 496 if (!GetProperty(cx, props, props, id, &v) || 497 !ToPropertyDescriptor(cx, v, checkAccessors, &desc) || 498 !descs.append(desc)) { 499 return false; 500 } 501 } 502 return true; 503 } 504 505 /*** Seal and freeze ********************************************************/ 506 507 /* ES6 draft rev 29 (6 Dec 2014) 7.3.13. */ 508 bool js::SetIntegrityLevel(JSContext* cx, HandleObject obj, 509 IntegrityLevel level) { 510 cx->check(obj); 511 512 // Steps 3-5. (Steps 1-2 are redundant assertions.) 513 if (!PreventExtensions(cx, obj)) { 514 return false; 515 } 516 517 // Steps 6-9, loosely interpreted. 518 if (obj->is<NativeObject>() && !obj->is<TypedArrayObject>() && 519 !obj->is<MappedArgumentsObject>()) { 520 Handle<NativeObject*> nobj = obj.as<NativeObject>(); 521 522 // Use a fast path to seal/freeze properties. This has the benefit of 523 // creating shared property maps if possible, whereas the slower/generic 524 // implementation below ends up converting non-empty objects to dictionary 525 // mode. 526 if (nobj->shape()->propMapLength() > 0) { 527 if (!NativeObject::freezeOrSealProperties(cx, nobj, level)) { 528 return false; 529 } 530 } 531 532 // Ordinarily ArraySetLength handles this, but we're going behind its back 533 // right now, so we must do this manually. 534 if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) { 535 obj->as<ArrayObject>().setNonWritableLength(cx); 536 } 537 } else { 538 // Steps 6-7. 539 RootedIdVector keys(cx); 540 if (!GetPropertyKeys( 541 cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys)) { 542 return false; 543 } 544 545 RootedId id(cx); 546 Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Empty()); 547 548 // 8.a/9.a. The two different loops are merged here. 549 for (size_t i = 0; i < keys.length(); i++) { 550 id = keys[i]; 551 552 if (level == IntegrityLevel::Sealed) { 553 // 8.a.i. 554 desc.setConfigurable(false); 555 } else { 556 // 9.a.i-ii. 557 Rooted<Maybe<PropertyDescriptor>> currentDesc(cx); 558 if (!GetOwnPropertyDescriptor(cx, obj, id, ¤tDesc)) { 559 return false; 560 } 561 562 // 9.a.iii. 563 if (currentDesc.isNothing()) { 564 continue; 565 } 566 567 // 9.a.iii.1-2 568 desc = PropertyDescriptor::Empty(); 569 if (currentDesc->isAccessorDescriptor()) { 570 desc.setConfigurable(false); 571 } else { 572 desc.setConfigurable(false); 573 desc.setWritable(false); 574 } 575 } 576 577 // 8.a.i-ii. / 9.a.iii.3-4 578 if (!DefineProperty(cx, obj, id, desc)) { 579 return false; 580 } 581 } 582 } 583 584 // Finally, freeze or seal the dense elements. 585 if (obj->is<NativeObject>()) { 586 if (!ObjectElements::FreezeOrSeal(cx, obj.as<NativeObject>(), level)) { 587 return false; 588 } 589 } 590 591 return true; 592 } 593 594 static bool ResolveLazyProperties(JSContext* cx, Handle<NativeObject*> obj) { 595 const JSClass* clasp = obj->getClass(); 596 if (JSEnumerateOp enumerate = clasp->getEnumerate()) { 597 if (!enumerate(cx, obj)) { 598 return false; 599 } 600 } 601 if (clasp->getNewEnumerate() && clasp->getResolve()) { 602 RootedIdVector properties(cx); 603 if (!clasp->getNewEnumerate()(cx, obj, &properties, 604 /* enumerableOnly = */ false)) { 605 return false; 606 } 607 608 RootedId id(cx); 609 for (size_t i = 0; i < properties.length(); i++) { 610 id = properties[i]; 611 bool found; 612 if (!HasOwnProperty(cx, obj, id, &found)) { 613 return false; 614 } 615 } 616 } 617 return true; 618 } 619 620 // ES6 draft rev33 (12 Feb 2015) 7.3.15 621 bool js::TestIntegrityLevel(JSContext* cx, HandleObject obj, 622 IntegrityLevel level, bool* result) { 623 // Steps 3-6. (Steps 1-2 are redundant assertions.) 624 bool status; 625 if (!IsExtensible(cx, obj, &status)) { 626 return false; 627 } 628 if (status) { 629 *result = false; 630 return true; 631 } 632 633 // Fast path for native objects. 634 if (obj->is<NativeObject>()) { 635 Handle<NativeObject*> nobj = obj.as<NativeObject>(); 636 637 // Force lazy properties to be resolved. 638 if (!ResolveLazyProperties(cx, nobj)) { 639 return false; 640 } 641 642 // Typed array elements are configurable, writable properties if the backing 643 // buffer is mutable, so if any elements are present, the typed array can 644 // neither be sealed nor frozen. 645 if (nobj->is<TypedArrayObject>() && 646 !nobj->is<ImmutableTypedArrayObject>() && 647 nobj->as<TypedArrayObject>().length().valueOr(0) > 0) { 648 *result = false; 649 return true; 650 } 651 652 bool hasDenseElements = false; 653 for (size_t i = 0; i < nobj->getDenseInitializedLength(); i++) { 654 if (nobj->containsDenseElement(i)) { 655 hasDenseElements = true; 656 break; 657 } 658 } 659 660 if (hasDenseElements) { 661 // Unless the sealed flag is set, dense elements are configurable. 662 if (!nobj->denseElementsAreSealed()) { 663 *result = false; 664 return true; 665 } 666 667 // Unless the frozen flag is set, dense elements are writable. 668 if (level == IntegrityLevel::Frozen && !nobj->denseElementsAreFrozen()) { 669 *result = false; 670 return true; 671 } 672 } 673 674 // Steps 7-9. 675 for (ShapePropertyIter<NoGC> iter(nobj->shape()); !iter.done(); iter++) { 676 // Steps 9.c.i-ii. 677 if (iter->configurable() || 678 (level == IntegrityLevel::Frozen && iter->isDataDescriptor() && 679 iter->writable())) { 680 // Private fields on objects don't participate in the frozen state, and 681 // so should be elided from checking for frozen state. 682 if (iter->key().isPrivateName()) { 683 continue; 684 } 685 686 *result = false; 687 return true; 688 } 689 } 690 } else { 691 // Steps 7-8. 692 RootedIdVector props(cx); 693 if (!GetPropertyKeys( 694 cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props)) { 695 return false; 696 } 697 698 // Step 9. 699 RootedId id(cx); 700 Rooted<Maybe<PropertyDescriptor>> desc(cx); 701 for (size_t i = 0, len = props.length(); i < len; i++) { 702 id = props[i]; 703 704 // Steps 9.a-b. 705 if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) { 706 return false; 707 } 708 709 // Step 9.c. 710 if (desc.isNothing()) { 711 continue; 712 } 713 714 // Steps 9.c.i-ii. 715 if (desc->configurable() || 716 (level == IntegrityLevel::Frozen && desc->isDataDescriptor() && 717 desc->writable())) { 718 // Since we don't request JSITER_PRIVATE in GetPropertyKeys above, we 719 // should never see a private name here. 720 MOZ_ASSERT(!id.isPrivateName()); 721 *result = false; 722 return true; 723 } 724 } 725 } 726 727 // Step 10. 728 *result = true; 729 return true; 730 } 731 732 /* * */ 733 734 static MOZ_ALWAYS_INLINE NativeObject* NewObject( 735 JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto, 736 gc::AllocKind kind, NewObjectKind newKind, ObjectFlags objFlags, 737 gc::AllocSite* allocSite = nullptr) { 738 MOZ_ASSERT(clasp->isNativeObject()); 739 740 // Some classes have specialized allocation functions and shouldn't end up 741 // here. 742 MOZ_ASSERT(clasp != &ArrayObject::class_); 743 MOZ_ASSERT(clasp != &PlainObject::class_); 744 MOZ_ASSERT(!clasp->isJSFunction()); 745 746 MOZ_ASSERT_IF(allocSite, allocSite->zone() == cx->zone()); 747 748 // Computing nfixed based on the AllocKind isn't right for objects which can 749 // store fixed data inline (TypedArrays and ArrayBuffers) so for simplicity 750 // and performance reasons we don't support such objects here. 751 MOZ_ASSERT(!ClassCanHaveFixedData(clasp)); 752 size_t nfixed = GetGCKindSlots(kind); 753 754 kind = gc::GetFinalizedAllocKindForClass(kind, clasp); 755 756 Rooted<SharedShape*> shape( 757 cx, SharedShape::getInitialShape(cx, clasp, cx->realm(), proto, nfixed, 758 objFlags)); 759 if (!shape) { 760 return nullptr; 761 } 762 763 gc::Heap heap = GetInitialHeap(newKind, clasp, allocSite); 764 NativeObject* obj = NativeObject::create(cx, kind, heap, shape, allocSite); 765 if (!obj) { 766 return nullptr; 767 } 768 769 probes::CreateObject(cx, obj); 770 return obj; 771 } 772 773 NativeObject* js::NewObjectWithGivenTaggedProto( 774 JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto, 775 gc::AllocKind allocKind, NewObjectKind newKind, ObjectFlags objFlags) { 776 return NewObject(cx, clasp, proto, allocKind, newKind, objFlags); 777 } 778 779 NativeObject* js::NewObjectWithGivenTaggedProtoAndAllocSite( 780 JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto, 781 gc::AllocKind allocKind, NewObjectKind newKind, ObjectFlags objFlags, 782 gc::AllocSite* site) { 783 return NewObject(cx, clasp, proto, allocKind, newKind, objFlags, site); 784 } 785 786 NativeObject* js::NewObjectWithClassProto(JSContext* cx, const JSClass* clasp, 787 HandleObject protoArg, 788 gc::AllocKind allocKind, 789 NewObjectKind newKind, 790 ObjectFlags objFlags) { 791 if (protoArg) { 792 return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(protoArg), 793 allocKind, newKind, objFlags); 794 } 795 796 // Find the appropriate proto for clasp. Built-in classes have a cached 797 // proto on cx->global(); all others get %ObjectPrototype%. 798 JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); 799 if (protoKey == JSProto_Null) { 800 protoKey = JSProto_Object; 801 } 802 803 JSObject* proto = GlobalObject::getOrCreatePrototype(cx, protoKey); 804 if (!proto) { 805 return nullptr; 806 } 807 808 Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto)); 809 return NewObject(cx, clasp, taggedProto, allocKind, newKind, objFlags); 810 } 811 812 bool js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, 813 JSProtoKey intrinsicDefaultProto, 814 MutableHandleObject proto) { 815 RootedValue protov(cx); 816 if (!GetProperty(cx, newTarget, newTarget, cx->names().prototype, &protov)) { 817 return false; 818 } 819 if (protov.isObject()) { 820 proto.set(&protov.toObject()); 821 } else if (newTarget->is<JSFunction>() && 822 newTarget->as<JSFunction>().realm() == cx->realm()) { 823 // Steps 4.a-b fetch the builtin prototype of the current realm, which we 824 // represent as nullptr. 825 proto.set(nullptr); 826 } else if (intrinsicDefaultProto == JSProto_Null) { 827 // Bug 1317416. The caller did not pass a reasonable JSProtoKey, so let the 828 // caller select a prototype object. Most likely they will choose one from 829 // the wrong realm. 830 proto.set(nullptr); 831 } else { 832 // Step 4.a: Let realm be ? GetFunctionRealm(constructor); 833 Realm* realm = JS::GetFunctionRealm(cx, newTarget); 834 if (!realm) { 835 return false; 836 } 837 838 // Step 4.b: Set proto to realm's intrinsic object named 839 // intrinsicDefaultProto. 840 { 841 Maybe<AutoRealm> ar; 842 if (cx->realm() != realm) { 843 ar.emplace(cx, realm->maybeGlobal()); 844 } 845 proto.set(GlobalObject::getOrCreatePrototype(cx, intrinsicDefaultProto)); 846 } 847 if (!proto) { 848 return false; 849 } 850 if (!cx->compartment()->wrap(cx, proto)) { 851 return false; 852 } 853 } 854 return true; 855 } 856 857 /* static */ 858 bool JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj, 859 HandleId id, HandleValue v, 860 HandleValue receiver, 861 ObjectOpResult& result) { 862 return obj->getOpsSetProperty()(cx, obj, id, v, receiver, result); 863 } 864 865 /* static */ 866 bool JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj, 867 uint32_t index, HandleValue v, 868 HandleValue receiver, 869 ObjectOpResult& result) { 870 RootedId id(cx); 871 if (!IndexToId(cx, index, &id)) { 872 return false; 873 } 874 return nonNativeSetProperty(cx, obj, id, v, receiver, result); 875 } 876 877 static bool CopyPropertyFrom(JSContext* cx, HandleId id, HandleObject target, 878 HandleObject obj) { 879 // |target| must not be a CCW because we need to enter its realm below and 880 // CCWs are not associated with a single realm. 881 MOZ_ASSERT(!IsCrossCompartmentWrapper(target)); 882 883 // |obj| and |cx| are generally not same-compartment with |target| here. 884 cx->check(obj, id); 885 Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx); 886 887 if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) { 888 return false; 889 } 890 MOZ_ASSERT(desc.isSome()); 891 892 JSAutoRealm ar(cx, target); 893 cx->markId(id); 894 RootedId wrappedId(cx, id); 895 if (!cx->compartment()->wrap(cx, &desc)) { 896 return false; 897 } 898 899 Rooted<PropertyDescriptor> desc_(cx, *desc); 900 return DefineProperty(cx, target, wrappedId, desc_); 901 } 902 903 JS_PUBLIC_API bool JS_CopyOwnPropertiesAndPrivateFields(JSContext* cx, 904 HandleObject target, 905 HandleObject obj) { 906 // Both |obj| and |target| must not be CCWs because we need to enter their 907 // realms below and CCWs are not associated with a single realm. 908 MOZ_ASSERT(!IsCrossCompartmentWrapper(obj)); 909 MOZ_ASSERT(!IsCrossCompartmentWrapper(target)); 910 911 JSAutoRealm ar(cx, obj); 912 913 RootedIdVector props(cx); 914 if (!GetPropertyKeys( 915 cx, obj, 916 JSITER_PRIVATE | JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, 917 &props)) { 918 return false; 919 } 920 921 for (size_t i = 0; i < props.length(); ++i) { 922 if (!CopyPropertyFrom(cx, props[i], target, obj)) { 923 return false; 924 } 925 } 926 927 return true; 928 } 929 930 static bool InitializePropertiesFromCompatibleNativeObject( 931 JSContext* cx, Handle<NativeObject*> dst, Handle<NativeObject*> src) { 932 cx->check(src, dst); 933 MOZ_ASSERT(src->getClass() == dst->getClass()); 934 MOZ_ASSERT(dst->shape()->objectFlags().isEmpty()); 935 MOZ_ASSERT(src->numFixedSlots() == dst->numFixedSlots()); 936 MOZ_ASSERT(!src->inDictionaryMode()); 937 MOZ_ASSERT(!dst->inDictionaryMode()); 938 939 if (!dst->ensureElements(cx, src->getDenseInitializedLength())) { 940 return false; 941 } 942 943 uint32_t initialized = src->getDenseInitializedLength(); 944 for (uint32_t i = 0; i < initialized; ++i) { 945 dst->setDenseInitializedLength(i + 1); 946 dst->initDenseElement(i, src->getDenseElement(i)); 947 } 948 949 // If there are no properties to copy, we're done. 950 if (!src->sharedShape()->propMap()) { 951 return true; 952 } 953 954 Rooted<SharedShape*> shape(cx); 955 if (src->staticPrototype() == dst->staticPrototype()) { 956 shape = src->sharedShape(); 957 } else { 958 // We need to generate a new shape for dst that has dst's proto but all 959 // the property information from src. Note that we asserted above that 960 // dst's object flags are empty. 961 SharedShape* srcShape = src->sharedShape(); 962 ObjectFlags objFlags; 963 objFlags = CopyPropMapObjectFlags(objFlags, srcShape->objectFlags()); 964 Rooted<SharedPropMap*> map(cx, srcShape->propMap()); 965 uint32_t mapLength = srcShape->propMapLength(); 966 shape = SharedShape::getPropMapShape(cx, dst->shape()->base(), 967 dst->numFixedSlots(), map, mapLength, 968 objFlags); 969 if (!shape) { 970 return false; 971 } 972 } 973 974 uint32_t oldSpan = dst->sharedShape()->slotSpan(); 975 uint32_t newSpan = shape->slotSpan(); 976 if (!dst->setShapeAndAddNewSlots(cx, shape, oldSpan, newSpan)) { 977 return false; 978 } 979 for (size_t i = JSCLASS_RESERVED_SLOTS(src->getClass()); i < newSpan; i++) { 980 dst->setSlot(i, src->getSlot(i)); 981 } 982 983 return true; 984 } 985 986 JS_PUBLIC_API bool JS_InitializePropertiesFromCompatibleNativeObject( 987 JSContext* cx, HandleObject dst, HandleObject src) { 988 return InitializePropertiesFromCompatibleNativeObject( 989 cx, dst.as<NativeObject>(), src.as<NativeObject>()); 990 } 991 992 bool js::ObjectMayBeSwapped(const JSObject* obj) { 993 const JSClass* clasp = obj->getClass(); 994 995 // We want to optimize Window/globals and Gecko doesn't require transplanting 996 // them (only the WindowProxy around them). A Window may be a DOMClass, so we 997 // explicitly check if this is a global. 998 if (clasp->isGlobal()) { 999 return false; 1000 } 1001 1002 // WindowProxy, Wrapper, DeadProxyObject, DOMProxy, and DOMClass (non-global) 1003 // types may be swapped. It is hard to detect DOMProxy from shell, so target 1004 // proxies in general. 1005 return clasp->isProxyObject() || clasp->isDOMClass(); 1006 } 1007 1008 bool NativeObject::prepareForSwap(JSContext* cx, JSObject* other, 1009 MutableHandleValueVector slotValuesOut) { 1010 MOZ_ASSERT(slotValuesOut.empty()); 1011 1012 for (size_t i = 0; i < slotSpan(); i++) { 1013 if (!slotValuesOut.append(getSlot(i))) { 1014 return false; 1015 } 1016 } 1017 1018 if (hasDynamicSlots()) { 1019 setEmptyDynamicSlots(0); 1020 } 1021 1022 // Copy elements if we're swapping between tenured and nursery objects to 1023 // prevent: 1024 // 1. Tenured objects with pointers to direct nursery allocations 1025 // 2. Tenured objects with pointers to buffers marked as nursery-owned 1026 if (hasDynamicElements() && IsInsideNursery(this) != IsInsideNursery(other)) { 1027 ObjectElements* elements = getElementsHeader(); 1028 size_t count = elements->numAllocatedElements(); 1029 size_t size = count * sizeof(HeapSlot); 1030 void* buffer = AllocBuffer(cx->zone(), size, IsInsideNursery(other)); 1031 if (!buffer) { 1032 return false; 1033 } 1034 1035 memmove(buffer, getUnshiftedElementsHeader(), size); 1036 1037 uint32_t numShifted = elements->numShiftedElements(); 1038 auto* newElements = reinterpret_cast<ObjectElements*>( 1039 reinterpret_cast<HeapSlot*>(buffer) + numShifted); 1040 1041 elements_ = newElements->elements(); 1042 1043 MOZ_ASSERT(hasDynamicElements()); 1044 } 1045 1046 return true; 1047 } 1048 1049 /* static */ 1050 bool NativeObject::fixupAfterSwap(JSContext* cx, Handle<NativeObject*> obj, 1051 gc::AllocKind kind, 1052 HandleValueVector slotValues) { 1053 // This object has just been swapped with some other object, and its shape 1054 // no longer reflects its allocated size. Correct this information and 1055 // fill the slots in with the specified values. 1056 MOZ_ASSERT_IF(!obj->inDictionaryMode(), 1057 obj->slotSpan() == slotValues.length()); 1058 1059 // Make sure the shape's numFixedSlots() is correct. 1060 size_t nfixed = gc::GetGCKindSlots(kind); 1061 if (nfixed != obj->shape()->numFixedSlots()) { 1062 if (!NativeObject::changeNumFixedSlotsAfterSwap(cx, obj, nfixed)) { 1063 return false; 1064 } 1065 MOZ_ASSERT(obj->shape()->numFixedSlots() == nfixed); 1066 } 1067 1068 uint32_t oldDictionarySlotSpan = 1069 obj->inDictionaryMode() ? slotValues.length() : 0; 1070 1071 MOZ_ASSERT(!obj->hasUniqueId()); 1072 size_t ndynamic = 1073 calculateDynamicSlots(nfixed, slotValues.length(), obj->getClass()); 1074 size_t currentSlots = obj->getSlotsHeader()->capacity(); 1075 MOZ_ASSERT(ndynamic >= currentSlots); 1076 if (ndynamic > currentSlots) { 1077 if (!obj->growSlots(cx, currentSlots, ndynamic)) { 1078 return false; 1079 } 1080 } 1081 1082 if (obj->inDictionaryMode()) { 1083 obj->setDictionaryModeSlotSpan(oldDictionarySlotSpan); 1084 } 1085 1086 for (size_t i = 0, len = slotValues.length(); i < len; i++) { 1087 obj->initSlotUnchecked(i, slotValues[i]); 1088 } 1089 1090 #ifdef DEBUG 1091 Zone* zone = obj->zone(); 1092 if (obj->hasDynamicSlots() && gc::IsBufferAlloc(obj->getSlotsHeader())) { 1093 MOZ_ASSERT(gc::IsNurseryOwned(zone, obj->getSlotsHeader()) == 1094 IsInsideNursery(obj)); 1095 } 1096 if (obj->hasDynamicElements() && 1097 gc::IsBufferAlloc(obj->getUnshiftedElementsHeader())) { 1098 MOZ_ASSERT(gc::IsNurseryOwned(zone, obj->getUnshiftedElementsHeader()) == 1099 IsInsideNursery(obj)); 1100 } 1101 #endif 1102 1103 return true; 1104 } 1105 1106 [[nodiscard]] bool ProxyObject::prepareForSwap( 1107 JSContext* cx, MutableHandleValueVector valuesOut) { 1108 MOZ_ASSERT(valuesOut.empty()); 1109 1110 // Remove the GCPtr<Value>s we're about to swap from the store buffer, to 1111 // ensure we don't trace bogus values. 1112 gc::StoreBuffer& sb = cx->runtime()->gc.storeBuffer(); 1113 1114 // Reserve space for the expando, private slot and the reserved slots. 1115 if (!valuesOut.reserve(2 + numReservedSlots())) { 1116 return false; 1117 } 1118 1119 js::detail::ProxyValueArray* valArray = data.values(); 1120 sb.unputValue(&valArray->expandoSlot); 1121 sb.unputValue(&valArray->privateSlot); 1122 valuesOut.infallibleAppend(valArray->expandoSlot); 1123 valuesOut.infallibleAppend(valArray->privateSlot); 1124 1125 for (size_t i = 0; i < numReservedSlots(); i++) { 1126 sb.unputValue(&valArray->reservedSlots.slots[i]); 1127 valuesOut.infallibleAppend(valArray->reservedSlots.slots[i]); 1128 } 1129 1130 if (isTenured() && !usingInlineValueArray()) { 1131 size_t count = detail::ProxyValueArray::allocCount(numReservedSlots()); 1132 RemoveCellMemory(this, count * sizeof(Value), 1133 MemoryUse::ProxyExternalValueArray); 1134 js_free(valArray); 1135 data.reservedSlots = nullptr; 1136 } 1137 1138 return true; 1139 } 1140 1141 bool ProxyObject::fixupAfterSwap(JSContext* cx, 1142 const HandleValueVector values) { 1143 MOZ_ASSERT(getClass()->isProxyObject()); 1144 1145 size_t nreserved = numReservedSlots(); 1146 1147 // |values| contains the expando slot, private slot and the reserved slots. 1148 MOZ_ASSERT(values.length() == 2 + nreserved); 1149 1150 // Allocate the external value array in malloc memory, even for nursery 1151 // proxies. 1152 size_t count = detail::ProxyValueArray::allocCount(nreserved); 1153 auto* allocation = js_pod_malloc<JS::Value>(count); 1154 if (!allocation) { 1155 return false; 1156 } 1157 1158 size_t size = count * sizeof(Value); 1159 if (isTenured()) { 1160 AddCellMemory(&asTenured(), size, MemoryUse::ProxyExternalValueArray); 1161 } else if (!cx->nursery().registerMallocedBuffer(allocation, size)) { 1162 js_free(allocation); 1163 return false; 1164 } 1165 1166 auto* valArray = reinterpret_cast<js::detail::ProxyValueArray*>(allocation); 1167 1168 valArray->expandoSlot = values[0]; 1169 valArray->privateSlot = values[1]; 1170 1171 for (size_t i = 0; i < nreserved; i++) { 1172 valArray->reservedSlots.slots[i] = values[i + 2]; 1173 } 1174 1175 data.reservedSlots = &valArray->reservedSlots; 1176 MOZ_ASSERT(!usingInlineValueArray()); 1177 return true; 1178 } 1179 1180 static gc::AllocKind SwappableObjectAllocKind(JSObject* obj) { 1181 MOZ_ASSERT(ObjectMayBeSwapped(obj)); 1182 1183 if (obj->isTenured()) { 1184 return obj->asTenured().getAllocKind(); 1185 } 1186 1187 if (obj->is<NativeObject>()) { 1188 return obj->as<NativeObject>().allocKindForTenure(); 1189 } 1190 1191 return obj->as<ProxyObject>().allocKindForTenure(); 1192 } 1193 1194 /* Use this method with extreme caution. It trades the guts of two objects. */ 1195 void JSObject::swap(JSContext* cx, HandleObject a, HandleObject b, 1196 AutoEnterOOMUnsafeRegion& oomUnsafe) { 1197 // Ensure swap doesn't cause a finalizer to be run at the wrong time. 1198 MOZ_ASSERT(gc::GetFinalizeKind(a->allocKind()) == 1199 gc::GetFinalizeKind(b->allocKind())); 1200 1201 MOZ_ASSERT(a->compartment() == b->compartment()); 1202 1203 // You must have entered the objects' compartment before calling this. 1204 MOZ_ASSERT(cx->compartment() == a->compartment()); 1205 1206 // Only certain types of objects are allowed to be swapped. This allows the 1207 // JITs to better optimize objects that can never swap and rules out most 1208 // builtin objects that have special behaviour. 1209 MOZ_RELEASE_ASSERT(js::ObjectMayBeSwapped(a)); 1210 MOZ_RELEASE_ASSERT(js::ObjectMayBeSwapped(b)); 1211 1212 // Don't allow a GC which may observe intermediate state or run before we 1213 // execute all necessary barriers. 1214 gc::AutoSuppressGC nogc(cx); 1215 1216 if (!Watchtower::watchObjectSwap(cx, a, b)) { 1217 oomUnsafe.crash("watchObjectSwap"); 1218 } 1219 1220 // Ensure we update any embedded nursery pointers in either object. 1221 gc::StoreBuffer& storeBuffer = cx->runtime()->gc.storeBuffer(); 1222 if (a->isTenured()) { 1223 storeBuffer.putWholeCell(a); 1224 } 1225 if (b->isTenured()) { 1226 storeBuffer.putWholeCell(b); 1227 } 1228 if (a->isTenured() || b->isTenured()) { 1229 if (a->zone()->wasGCStarted()) { 1230 storeBuffer.setMayHavePointersToDeadCells(); 1231 } 1232 } 1233 1234 unsigned r = NotifyGCPreSwap(a, b); 1235 1236 ProxyObject* pa = a->is<ProxyObject>() ? &a->as<ProxyObject>() : nullptr; 1237 ProxyObject* pb = b->is<ProxyObject>() ? &b->as<ProxyObject>() : nullptr; 1238 bool aIsProxyWithInlineValues = pa && pa->usingInlineValueArray(); 1239 bool bIsProxyWithInlineValues = pb && pb->usingInlineValueArray(); 1240 1241 bool aIsUsedAsPrototype = a->isUsedAsPrototype(); 1242 bool bIsUsedAsPrototype = b->isUsedAsPrototype(); 1243 1244 // Swap element associations. 1245 Zone* zone = a->zone(); 1246 1247 // Record any associated unique IDs and prepare for swap. 1248 // 1249 // Note that unique IDs are NOT swapped but remain associated with the 1250 // original address. 1251 uint64_t aid = 0; 1252 uint64_t bid = 0; 1253 (void)gc::MaybeGetUniqueId(a, &aid); 1254 (void)gc::MaybeGetUniqueId(b, &bid); 1255 NativeObject* na = a->is<NativeObject>() ? &a->as<NativeObject>() : nullptr; 1256 NativeObject* nb = b->is<NativeObject>() ? &b->as<NativeObject>() : nullptr; 1257 if ((aid || bid) && (na || nb)) { 1258 // We can't remove unique IDs from native objects when they are swapped with 1259 // objects without an ID. Instead ensure they both have IDs so we always 1260 // have something to overwrite the old ID with. 1261 if (!gc::GetOrCreateUniqueId(a, &aid) || 1262 !gc::GetOrCreateUniqueId(b, &bid)) { 1263 oomUnsafe.crash("Failed to create unique ID during swap"); 1264 } 1265 1266 // IDs stored in NativeObjects could shadow those stored in the zone 1267 // table. Remove any zone table IDs first. 1268 if (pa && aid) { 1269 gc::RemoveUniqueId(a); 1270 } 1271 if (pb && bid) { 1272 gc::RemoveUniqueId(b); 1273 } 1274 } 1275 1276 gc::AllocKind ka = SwappableObjectAllocKind(a); 1277 gc::AllocKind kb = SwappableObjectAllocKind(b); 1278 1279 size_t sa = gc::Arena::thingSize(ka); 1280 size_t sb = gc::Arena::thingSize(kb); 1281 if (sa == sb && a->isTenured() == b->isTenured()) { 1282 // When both objects are the same size and in the same heap, just do a plain 1283 // swap of their contents. 1284 1285 size_t size = sa; 1286 char tmp[sizeof(JSObject_Slots16)]; 1287 MOZ_ASSERT(size <= sizeof(tmp)); 1288 1289 js_memcpy(tmp, a, size); 1290 js_memcpy(a, b, size); 1291 js_memcpy(b, tmp, size); 1292 1293 zone->swapCellMemory(a, b, MemoryUse::ProxyExternalValueArray); 1294 1295 if (aIsProxyWithInlineValues) { 1296 b->as<ProxyObject>().setInlineValueArray(); 1297 } 1298 if (bIsProxyWithInlineValues) { 1299 a->as<ProxyObject>().setInlineValueArray(); 1300 } 1301 } else { 1302 // When the objects have different sizes, they will have different numbers 1303 // of fixed slots before and after the swap, so the slots for native objects 1304 // will need to be rearranged. Remember the original values from the 1305 // objects. 1306 RootedValueVector avals(cx); 1307 RootedValueVector bvals(cx); 1308 if (na && !na->prepareForSwap(cx, b, &avals)) { 1309 oomUnsafe.crash("NativeObject::prepareForSwap"); 1310 } 1311 if (nb && !nb->prepareForSwap(cx, a, &bvals)) { 1312 oomUnsafe.crash("NativeObject::prepareForSwap"); 1313 } 1314 1315 // Do the same for proxy value arrays. 1316 if (pa && !pa->prepareForSwap(cx, &avals)) { 1317 oomUnsafe.crash("ProxyObject::prepareForSwap"); 1318 } 1319 if (pb && !pb->prepareForSwap(cx, &bvals)) { 1320 oomUnsafe.crash("ProxyObject::prepareForSwap"); 1321 } 1322 1323 // Swap the main fields of the objects, whether they are native objects or 1324 // proxies. 1325 char tmp[sizeof(JSObject_Slots0)]; 1326 js_memcpy(&tmp, a, sizeof tmp); 1327 js_memcpy(a, b, sizeof tmp); 1328 js_memcpy(b, &tmp, sizeof tmp); 1329 1330 if (na && 1331 !NativeObject::fixupAfterSwap(cx, b.as<NativeObject>(), kb, avals)) { 1332 oomUnsafe.crash("NativeObject::fixupAfterSwap"); 1333 } 1334 if (nb && 1335 !NativeObject::fixupAfterSwap(cx, a.as<NativeObject>(), ka, bvals)) { 1336 oomUnsafe.crash("NativeObject::fixupAfterSwap"); 1337 } 1338 1339 if (pa && !b->as<ProxyObject>().fixupAfterSwap(cx, avals)) { 1340 oomUnsafe.crash("ProxyObject::fixupAfterSwap"); 1341 } 1342 if (pb && !a->as<ProxyObject>().fixupAfterSwap(cx, bvals)) { 1343 oomUnsafe.crash("ProxyObject::fixupAfterSwap"); 1344 } 1345 } 1346 1347 // Restore original unique IDs. 1348 if ((aid || bid) && (na || nb)) { 1349 if ((aid && !gc::SetOrUpdateUniqueId(cx, a, aid)) || 1350 (bid && !gc::SetOrUpdateUniqueId(cx, b, bid))) { 1351 oomUnsafe.crash("Failed to set unique ID after swap"); 1352 } 1353 } 1354 MOZ_ASSERT_IF(aid, gc::GetUniqueIdInfallible(a) == aid); 1355 MOZ_ASSERT_IF(bid, gc::GetUniqueIdInfallible(b) == bid); 1356 1357 // Preserve the IsUsedAsPrototype flag on the objects. 1358 if (aIsUsedAsPrototype) { 1359 if (!JSObject::setIsUsedAsPrototype(cx, a)) { 1360 oomUnsafe.crash("setIsUsedAsPrototype"); 1361 } 1362 } 1363 if (bIsUsedAsPrototype) { 1364 if (!JSObject::setIsUsedAsPrototype(cx, b)) { 1365 oomUnsafe.crash("setIsUsedAsPrototype"); 1366 } 1367 } 1368 1369 /* 1370 * We need a write barrier here. If |a| was marked and |b| was not, then 1371 * after the swap, |b|'s guts would never be marked. The write barrier 1372 * solves this. 1373 * 1374 * Normally write barriers happen before the write. However, that's not 1375 * necessary here because nothing is being destroyed. We're just swapping. 1376 */ 1377 PreWriteBarrier(zone, a.get(), [](JSTracer* trc, JSObject* obj) { 1378 obj->traceChildren(trc); 1379 }); 1380 PreWriteBarrier(zone, b.get(), [](JSTracer* trc, JSObject* obj) { 1381 obj->traceChildren(trc); 1382 }); 1383 1384 NotifyGCPostSwap(a, b, r); 1385 } 1386 1387 static NativeObject* DefineConstructorAndPrototype( 1388 JSContext* cx, HandleObject obj, Handle<JSAtom*> atom, 1389 HandleObject protoProto, const JSClass* clasp, Native constructor, 1390 unsigned nargs, const JSPropertySpec* ps, const JSFunctionSpec* fs, 1391 const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs, 1392 NativeObject** ctorp) { 1393 // Create the prototype object. 1394 Rooted<NativeObject*> proto( 1395 cx, GlobalObject::createBlankPrototypeInheriting(cx, clasp, protoProto)); 1396 if (!proto) { 1397 return nullptr; 1398 } 1399 1400 Rooted<NativeObject*> ctor(cx); 1401 if (!constructor) { 1402 ctor = proto; 1403 } else { 1404 ctor = NewNativeConstructor(cx, constructor, nargs, atom); 1405 if (!ctor) { 1406 return nullptr; 1407 } 1408 1409 if (!LinkConstructorAndPrototype(cx, ctor, proto)) { 1410 return nullptr; 1411 } 1412 } 1413 1414 if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) || 1415 (ctor != proto && 1416 !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs))) { 1417 return nullptr; 1418 } 1419 1420 if (clasp->specShouldDefineConstructor()) { 1421 RootedId id(cx, AtomToId(atom)); 1422 RootedValue value(cx, ObjectValue(*ctor)); 1423 if (!DefineDataProperty(cx, obj, id, value, 0)) { 1424 return nullptr; 1425 } 1426 } 1427 1428 if (ctorp) { 1429 *ctorp = ctor; 1430 } 1431 return proto; 1432 } 1433 1434 NativeObject* js::InitClass(JSContext* cx, HandleObject obj, 1435 const JSClass* protoClass, HandleObject protoProto_, 1436 const char* name, Native constructor, 1437 unsigned nargs, const JSPropertySpec* ps, 1438 const JSFunctionSpec* fs, 1439 const JSPropertySpec* static_ps, 1440 const JSFunctionSpec* static_fs, 1441 NativeObject** ctorp) { 1442 Rooted<JSAtom*> atom(cx, Atomize(cx, name, strlen(name))); 1443 if (!atom) { 1444 return nullptr; 1445 } 1446 1447 /* 1448 * All instances of the class will inherit properties from the prototype 1449 * object we are about to create (in DefineConstructorAndPrototype), which 1450 * in turn will inherit from protoProto. 1451 * 1452 * If protoProto is nullptr, default to Object.prototype. 1453 * If protoClass is nullptr, default to PlainObject. 1454 */ 1455 RootedObject protoProto(cx, protoProto_); 1456 if (!protoProto) { 1457 protoProto = &cx->global()->getObjectPrototype(); 1458 } 1459 if (!protoClass) { 1460 protoClass = &PlainObject::class_; 1461 } 1462 1463 return DefineConstructorAndPrototype(cx, obj, atom, protoProto, protoClass, 1464 constructor, nargs, ps, fs, static_ps, 1465 static_fs, ctorp); 1466 } 1467 1468 /** 1469 * Returns the original Object.prototype from the embedding-provided incumbent 1470 * global. 1471 * 1472 * Really, we want the incumbent global itself so we can pass it to other 1473 * embedding hooks which need it. Specifically, the enqueue promise hook 1474 * takes an incumbent global so it can set that on the PromiseCallbackJob 1475 * it creates. 1476 * 1477 * The reason for not just returning the global itself is that we'd need to 1478 * wrap it into the current compartment, and later unwrap it. Unwrapping 1479 * globals is tricky, though: we might accidentally unwrap through an inner 1480 * to its outer window and end up with the wrong global. Plain objects don't 1481 * have this problem, so we use the global's Object.prototype. The code using 1482 * it - e.g. EnqueuePromiseReactionJob - can then unwrap the object and get 1483 * its global without fear of unwrapping too far. 1484 */ 1485 bool js::GetObjectFromHostDefinedData(JSContext* cx, MutableHandleObject obj) { 1486 if (!cx->runtime()->getHostDefinedData(cx, obj)) { 1487 return false; 1488 } 1489 1490 // The object might be from a different compartment, so wrap it. 1491 if (obj && !cx->compartment()->wrap(cx, obj)) { 1492 return false; 1493 } 1494 1495 return true; 1496 } 1497 1498 static bool IsStandardPrototype(JSObject* obj, JSProtoKey key) { 1499 return obj->nonCCWGlobal().maybeGetPrototype(key) == obj; 1500 } 1501 1502 JSProtoKey JS::IdentifyStandardInstance(JSObject* obj) { 1503 // Note: The prototype shares its JSClass with instances. 1504 MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>()); 1505 JSProtoKey key = StandardProtoKeyOrNull(obj); 1506 if (key != JSProto_Null && !IsStandardPrototype(obj, key)) { 1507 return key; 1508 } 1509 return JSProto_Null; 1510 } 1511 1512 JSProtoKey JS::IdentifyStandardPrototype(JSObject* obj) { 1513 // Note: The prototype shares its JSClass with instances. 1514 MOZ_ASSERT(!obj->is<CrossCompartmentWrapperObject>()); 1515 JSProtoKey key = StandardProtoKeyOrNull(obj); 1516 if (key != JSProto_Null && IsStandardPrototype(obj, key)) { 1517 return key; 1518 } 1519 return JSProto_Null; 1520 } 1521 1522 JSProtoKey JS::IdentifyStandardInstanceOrPrototype(JSObject* obj) { 1523 return StandardProtoKeyOrNull(obj); 1524 } 1525 1526 JSProtoKey JS::IdentifyStandardConstructor(JSObject* obj) { 1527 // Note that isNativeConstructor does not imply that we are a standard 1528 // constructor, but the converse is true (at least until we start having 1529 // self-hosted constructors for standard classes). This lets us avoid a costly 1530 // loop for many functions (which, depending on the call site, may be the 1531 // common case). 1532 if (!obj->is<JSFunction>() || 1533 !(obj->as<JSFunction>().flags().isNativeConstructor())) { 1534 return JSProto_Null; 1535 } 1536 1537 static_assert(JSProto_Null == 0, 1538 "Loop below can start at 1 to skip JSProto_Null"); 1539 1540 GlobalObject& global = obj->as<JSFunction>().global(); 1541 for (size_t k = 1; k < JSProto_LIMIT; ++k) { 1542 JSProtoKey key = static_cast<JSProtoKey>(k); 1543 if (global.maybeGetConstructor(key) == obj) { 1544 return key; 1545 } 1546 } 1547 1548 return JSProto_Null; 1549 } 1550 1551 bool js::LookupProperty(JSContext* cx, HandleObject obj, js::HandleId id, 1552 MutableHandleObject objp, PropertyResult* propp) { 1553 if (LookupPropertyOp op = obj->getOpsLookupProperty()) { 1554 return op(cx, obj, id, objp, propp); 1555 } 1556 return NativeLookupPropertyInline<CanGC>(cx, obj.as<NativeObject>(), id, objp, 1557 propp); 1558 } 1559 1560 bool js::LookupName(JSContext* cx, Handle<PropertyName*> name, 1561 HandleObject envChain, MutableHandleObject objp, 1562 MutableHandleObject pobjp, PropertyResult* propp) { 1563 RootedId id(cx, NameToId(name)); 1564 1565 for (RootedObject env(cx, envChain); env; env = env->enclosingEnvironment()) { 1566 if (!LookupProperty(cx, env, id, pobjp, propp)) { 1567 return false; 1568 } 1569 if (propp->isFound()) { 1570 objp.set(env); 1571 return true; 1572 } 1573 } 1574 1575 objp.set(nullptr); 1576 pobjp.set(nullptr); 1577 propp->setNotFound(); 1578 return true; 1579 } 1580 1581 bool js::LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* envChain, 1582 NativeObject** pobjp, PropertyResult* propp) { 1583 AutoAssertNoPendingException nogc(cx); 1584 1585 MOZ_ASSERT(!*pobjp && propp->isNotFound()); 1586 1587 for (JSObject* env = envChain; env; env = env->enclosingEnvironment()) { 1588 if (env->getOpsLookupProperty()) { 1589 return false; 1590 } 1591 if (!NativeLookupPropertyInline<NoGC>(cx, &env->as<NativeObject>(), 1592 NameToId(name), pobjp, propp)) { 1593 return false; 1594 } 1595 if (propp->isFound()) { 1596 return true; 1597 } 1598 } 1599 1600 return true; 1601 } 1602 1603 static bool IsTemporalDeadZone(JSContext* cx, HandleObject env, HandleId id, 1604 const PropertyResult& prop, bool* isTDZ) { 1605 MOZ_ASSERT(prop.isFound()); 1606 1607 // We do our own explicit checking for |this| 1608 if (id.isAtom(cx->names().dot_this_)) { 1609 *isTDZ = false; 1610 return true; 1611 } 1612 1613 // Treat Debugger environments specially for TDZ checks, as they 1614 // look like non-native environments but in fact wrap native 1615 // environments. 1616 if (env->is<DebugEnvironmentProxy>()) { 1617 RootedValue v(cx); 1618 auto envProxy = env.as<DebugEnvironmentProxy>(); 1619 if (!DebugEnvironmentProxy::getMaybeSentinelValue(cx, envProxy, id, &v)) { 1620 return false; 1621 } 1622 *isTDZ = IsUninitializedLexical(v); 1623 return true; 1624 } 1625 1626 *isTDZ = IsUninitializedLexicalSlot(env, prop); 1627 return true; 1628 } 1629 1630 JSObject* js::LookupNameWithGlobalDefault(JSContext* cx, 1631 Handle<PropertyName*> name, 1632 HandleObject envChain) { 1633 RootedId id(cx, NameToId(name)); 1634 1635 RootedObject pobj(cx); 1636 PropertyResult prop; 1637 1638 RootedObject env(cx, envChain); 1639 for (; !env->is<GlobalObject>(); env = env->enclosingEnvironment()) { 1640 if (!LookupProperty(cx, env, id, &pobj, &prop)) { 1641 return nullptr; 1642 } 1643 if (prop.isFound()) { 1644 break; 1645 } 1646 } 1647 1648 // Uninitialized lexicals can't appear on the prototype chain, so only check 1649 // for TDZ when |pobj == env|. 1650 // 1651 // JSOp::BindName is always directly followed by JSOp::GetBoundName, so don't 1652 // bother to create a RuntimeLexicalErrorObject. 1653 if (pobj == env) { 1654 MOZ_ASSERT(prop.isFound()); 1655 1656 bool isTDZ; 1657 if (!IsTemporalDeadZone(cx, env, id, prop, &isTDZ)) { 1658 return nullptr; 1659 } 1660 if (isTDZ) { 1661 ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, name); 1662 return nullptr; 1663 } 1664 } 1665 1666 return env; 1667 } 1668 1669 JSObject* js::LookupNameUnqualified(JSContext* cx, Handle<PropertyName*> name, 1670 HandleObject envChain) { 1671 RootedId id(cx, NameToId(name)); 1672 1673 RootedObject pobj(cx); 1674 PropertyResult prop; 1675 1676 RootedObject env(cx, envChain); 1677 for (; !env->isUnqualifiedVarObj(); env = env->enclosingEnvironment()) { 1678 if (!LookupProperty(cx, env, id, &pobj, &prop)) { 1679 return nullptr; 1680 } 1681 if (prop.isFound()) { 1682 break; 1683 } 1684 } 1685 1686 // Uninitialized lexicals can't appear on the prototype chain, so only check 1687 // for TDZ and `const` bindings when |pobj == env|. 1688 // 1689 // See note above RuntimeLexicalErrorObject. 1690 if (pobj == env) { 1691 MOZ_ASSERT(prop.isFound()); 1692 1693 bool isTDZ; 1694 if (!IsTemporalDeadZone(cx, env, id, prop, &isTDZ)) { 1695 return nullptr; 1696 } 1697 if (isTDZ) { 1698 return RuntimeLexicalErrorObject::create(cx, env, 1699 JSMSG_UNINITIALIZED_LEXICAL); 1700 } 1701 1702 if (env->is<LexicalEnvironmentObject>() && 1703 !prop.propertyInfo().writable()) { 1704 // Assigning to a named lambda callee name is a no-op in sloppy mode. 1705 if (!(env->is<BlockLexicalEnvironmentObject>() && 1706 env->as<BlockLexicalEnvironmentObject>().scope().kind() == 1707 ScopeKind::NamedLambda)) { 1708 MOZ_ASSERT(name != cx->names().dot_this_); 1709 return RuntimeLexicalErrorObject::create(cx, env, 1710 JSMSG_BAD_CONST_ASSIGN); 1711 } 1712 } 1713 } 1714 1715 return env; 1716 } 1717 1718 bool js::HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id, 1719 bool* result) { 1720 if (obj->is<ProxyObject>()) { 1721 return Proxy::hasOwn(cx, obj, id, result); 1722 } 1723 1724 if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) { 1725 Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx); 1726 if (!op(cx, obj, id, &desc)) { 1727 return false; 1728 } 1729 *result = desc.isSome(); 1730 return true; 1731 } 1732 1733 PropertyResult prop; 1734 if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &prop)) { 1735 return false; 1736 } 1737 *result = prop.isFound(); 1738 return true; 1739 } 1740 1741 bool js::LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id, 1742 NativeObject** objp, PropertyResult* propp) { 1743 if (obj->getOpsLookupProperty()) { 1744 return false; 1745 } 1746 return NativeLookupPropertyInline<NoGC, LookupResolveMode::CheckMayResolve>( 1747 cx, &obj->as<NativeObject>(), id, objp, propp); 1748 } 1749 1750 bool js::LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, 1751 PropertyResult* propp) { 1752 if (obj->getOpsLookupProperty()) { 1753 return false; 1754 } 1755 return NativeLookupOwnPropertyInline<NoGC, 1756 LookupResolveMode::CheckMayResolve>( 1757 cx, &obj->as<NativeObject>(), id, propp); 1758 } 1759 1760 static inline bool NativeGetPureInline(NativeObject* pobj, jsid id, 1761 PropertyResult prop, Value* vp, 1762 JSContext* cx) { 1763 if (prop.isDenseElement()) { 1764 *vp = pobj->getDenseElement(prop.denseElementIndex()); 1765 return true; 1766 } 1767 if (prop.isTypedArrayElement()) { 1768 size_t idx = prop.typedArrayElementIndex(); 1769 return pobj->as<TypedArrayObject>().getElement<NoGC>(cx, idx, vp); 1770 } 1771 1772 // Fail if we have a custom getter. 1773 PropertyInfo propInfo = prop.propertyInfo(); 1774 if (!propInfo.isDataProperty()) { 1775 return false; 1776 } 1777 1778 *vp = pobj->getSlot(propInfo.slot()); 1779 MOZ_ASSERT(!vp->isMagic()); 1780 return true; 1781 } 1782 1783 bool js::GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp) { 1784 NativeObject* pobj; 1785 PropertyResult prop; 1786 if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) { 1787 return false; 1788 } 1789 1790 if (prop.isNotFound()) { 1791 vp->setUndefined(); 1792 return true; 1793 } 1794 1795 return NativeGetPureInline(pobj, id, prop, vp, cx); 1796 } 1797 1798 bool js::GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp, 1799 bool* found) { 1800 PropertyResult prop; 1801 if (!LookupOwnPropertyPure(cx, obj, id, &prop)) { 1802 return false; 1803 } 1804 1805 if (prop.isNotFound()) { 1806 *found = false; 1807 vp->setUndefined(); 1808 return true; 1809 } 1810 1811 *found = true; 1812 return obj->is<NativeObject>() && 1813 NativeGetPureInline(&obj->as<NativeObject>(), id, prop, vp, cx); 1814 } 1815 1816 static inline bool NativeGetGetterPureInline(NativeObject* holder, 1817 PropertyResult prop, 1818 JSFunction** fp) { 1819 MOZ_ASSERT(prop.isNativeProperty()); 1820 1821 PropertyInfo propInfo = prop.propertyInfo(); 1822 if (holder->hasGetter(propInfo)) { 1823 JSObject* getter = holder->getGetter(propInfo); 1824 if (getter->is<JSFunction>()) { 1825 *fp = &getter->as<JSFunction>(); 1826 return true; 1827 } 1828 } 1829 1830 *fp = nullptr; 1831 return true; 1832 } 1833 1834 bool js::GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp) { 1835 /* Just like GetPropertyPure, but get getter function, without invoking 1836 * it. */ 1837 NativeObject* pobj; 1838 PropertyResult prop; 1839 if (!LookupPropertyPure(cx, obj, id, &pobj, &prop)) { 1840 return false; 1841 } 1842 1843 if (prop.isNotFound()) { 1844 *fp = nullptr; 1845 return true; 1846 } 1847 1848 return prop.isNativeProperty() && NativeGetGetterPureInline(pobj, prop, fp); 1849 } 1850 1851 bool js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, 1852 bool* isOrdinary, MutableHandleObject protop) { 1853 if (obj->is<js::ProxyObject>()) { 1854 return js::Proxy::getPrototypeIfOrdinary(cx, obj, isOrdinary, protop); 1855 } 1856 1857 *isOrdinary = true; 1858 protop.set(obj->staticPrototype()); 1859 return true; 1860 } 1861 1862 /*** ES6 standard internal methods ******************************************/ 1863 1864 bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, 1865 JS::ObjectOpResult& result) { 1866 // The proxy trap subsystem fully handles prototype-setting for proxies 1867 // with dynamic [[Prototype]]s. 1868 if (obj->hasDynamicPrototype()) { 1869 MOZ_ASSERT(obj->is<ProxyObject>()); 1870 return Proxy::setPrototype(cx, obj, proto, result); 1871 } 1872 1873 /* 1874 * ES6 9.1.2 step 3-4 if |obj.[[Prototype]]| has SameValue as |proto| return 1875 * true. Since the values in question are objects, we can just compare 1876 * pointers. 1877 */ 1878 if (proto == obj->staticPrototype()) { 1879 return result.succeed(); 1880 } 1881 1882 /* Disallow mutation of immutable [[Prototype]]s. */ 1883 if (obj->staticPrototypeIsImmutable()) { 1884 return result.fail(JSMSG_CANT_SET_PROTO); 1885 } 1886 1887 /* 1888 * Disallow mutating the [[Prototype]] on WebAssembly GC objects. 1889 */ 1890 if (obj->is<WasmGcObject>()) { 1891 return result.fail(JSMSG_CANT_SET_PROTO); 1892 } 1893 1894 /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ 1895 bool extensible; 1896 if (!IsExtensible(cx, obj, &extensible)) { 1897 return false; 1898 } 1899 if (!extensible) { 1900 return result.fail(JSMSG_CANT_SET_PROTO); 1901 } 1902 1903 /* 1904 * ES6 9.1.2 step 6 forbids generating cyclical prototype chains. But we 1905 * have to do this comparison on the observable WindowProxy, not on the 1906 * possibly-Window object we're setting the proto on. 1907 */ 1908 RootedObject objMaybeWindowProxy(cx, ToWindowProxyIfWindow(obj)); 1909 RootedObject obj2(cx, proto); 1910 while (obj2) { 1911 MOZ_ASSERT(!IsWindow(obj2)); 1912 if (obj2 == objMaybeWindowProxy) { 1913 return result.fail(JSMSG_CANT_SET_PROTO_CYCLE); 1914 } 1915 1916 bool isOrdinary; 1917 if (!GetPrototypeIfOrdinary(cx, obj2, &isOrdinary, &obj2)) { 1918 return false; 1919 } 1920 if (!isOrdinary) { 1921 break; 1922 } 1923 } 1924 1925 Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto)); 1926 if (!JSObject::setProtoUnchecked(cx, obj, taggedProto)) { 1927 return false; 1928 } 1929 1930 return result.succeed(); 1931 } 1932 1933 bool js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto) { 1934 ObjectOpResult result; 1935 return SetPrototype(cx, obj, proto, result) && result.checkStrict(cx, obj); 1936 } 1937 1938 /** 1939 * IsTypedArrayFixedLength ( O ) 1940 * 1941 * ES2025 draft rev 3e6f71c9402f91344ef9560425cc1e8fc45abf86 1942 */ 1943 static bool IsTypedArrayFixedLength(ResizableTypedArrayObject* obj) { 1944 MOZ_ASSERT(obj->hasResizableBuffer()); 1945 1946 // Step 1. 1947 if (obj->isAutoLength()) { 1948 return false; 1949 } 1950 1951 // Steps 2-4. 1952 return obj->isSharedMemory(); 1953 } 1954 1955 bool js::PreventExtensions(JSContext* cx, HandleObject obj, 1956 ObjectOpResult& result) { 1957 if (obj->is<ProxyObject>()) { 1958 return js::Proxy::preventExtensions(cx, obj, result); 1959 } 1960 1961 if (obj->is<WasmGcObject>()) { 1962 return result.failCantPreventExtensions(); 1963 } 1964 1965 if (obj->is<ResizableTypedArrayObject>() && 1966 !IsTypedArrayFixedLength(&obj->as<ResizableTypedArrayObject>())) { 1967 return result.failCantPreventExtensions(); 1968 } 1969 1970 if (!obj->nonProxyIsExtensible()) { 1971 // If the following assertion fails, there's somewhere else a missing 1972 // call to shrinkCapacityToInitializedLength() which needs to be found 1973 // and fixed. 1974 MOZ_ASSERT_IF(obj->is<NativeObject>(), 1975 obj->as<NativeObject>().getDenseInitializedLength() == 1976 obj->as<NativeObject>().getDenseCapacity()); 1977 1978 return result.succeed(); 1979 } 1980 1981 if (obj->is<NativeObject>()) { 1982 // Force lazy properties to be resolved. 1983 Handle<NativeObject*> nobj = obj.as<NativeObject>(); 1984 if (!ResolveLazyProperties(cx, nobj)) { 1985 return false; 1986 } 1987 1988 // Prepare the elements. We have to do this before we mark the object 1989 // non-extensible; that's fine because these changes are not observable. 1990 ObjectElements::PrepareForPreventExtensions(cx, nobj); 1991 } 1992 1993 // Finally, set the NotExtensible flag on the Shape and ObjectElements. 1994 if (!JSObject::setFlag(cx, obj, ObjectFlag::NotExtensible)) { 1995 return false; 1996 } 1997 if (obj->is<NativeObject>()) { 1998 ObjectElements::PreventExtensions(&obj->as<NativeObject>()); 1999 } 2000 2001 return result.succeed(); 2002 } 2003 2004 bool js::PreventExtensions(JSContext* cx, HandleObject obj) { 2005 ObjectOpResult result; 2006 return PreventExtensions(cx, obj, result) && result.checkStrict(cx, obj); 2007 } 2008 2009 bool js::GetOwnPropertyDescriptor( 2010 JSContext* cx, HandleObject obj, HandleId id, 2011 MutableHandle<Maybe<PropertyDescriptor>> desc) { 2012 if (GetOwnPropertyOp op = obj->getOpsGetOwnPropertyDescriptor()) { 2013 bool ok = op(cx, obj, id, desc); 2014 if (ok && desc.isSome()) { 2015 desc->assertComplete(); 2016 } 2017 return ok; 2018 } 2019 2020 return NativeGetOwnPropertyDescriptor(cx, obj.as<NativeObject>(), id, desc); 2021 } 2022 2023 bool js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, 2024 Handle<PropertyDescriptor> desc) { 2025 ObjectOpResult result; 2026 return DefineProperty(cx, obj, id, desc, result) && 2027 result.checkStrict(cx, obj, id); 2028 } 2029 2030 bool js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, 2031 Handle<PropertyDescriptor> desc, 2032 ObjectOpResult& result) { 2033 desc.assertValid(); 2034 if (DefinePropertyOp op = obj->getOpsDefineProperty()) { 2035 return op(cx, obj, id, desc, result); 2036 } 2037 return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result); 2038 } 2039 2040 bool js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id, 2041 HandleObject getter, HandleObject setter, 2042 unsigned attrs, ObjectOpResult& result) { 2043 Rooted<PropertyDescriptor> desc( 2044 cx, PropertyDescriptor::Accessor( 2045 getter ? mozilla::Some(getter) : mozilla::Nothing(), 2046 setter ? mozilla::Some(setter) : mozilla::Nothing(), attrs)); 2047 2048 if (DefinePropertyOp op = obj->getOpsDefineProperty()) { 2049 return op(cx, obj, id, desc, result); 2050 } 2051 return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result); 2052 } 2053 2054 bool js::DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id, 2055 HandleValue value, unsigned attrs, 2056 ObjectOpResult& result) { 2057 Rooted<PropertyDescriptor> desc(cx, PropertyDescriptor::Data(value, attrs)); 2058 if (DefinePropertyOp op = obj->getOpsDefineProperty()) { 2059 return op(cx, obj, id, desc, result); 2060 } 2061 return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result); 2062 } 2063 2064 bool js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id, 2065 HandleObject getter, HandleObject setter, 2066 unsigned attrs) { 2067 ObjectOpResult result; 2068 if (!DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result)) { 2069 return false; 2070 } 2071 if (!result) { 2072 result.reportError(cx, obj, id); 2073 return false; 2074 } 2075 return true; 2076 } 2077 2078 bool js::DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id, 2079 HandleValue value, unsigned attrs) { 2080 ObjectOpResult result; 2081 if (!DefineDataProperty(cx, obj, id, value, attrs, result)) { 2082 return false; 2083 } 2084 if (!result) { 2085 result.reportError(cx, obj, id); 2086 return false; 2087 } 2088 return true; 2089 } 2090 2091 bool js::DefineDataProperty(JSContext* cx, HandleObject obj, PropertyName* name, 2092 HandleValue value, unsigned attrs) { 2093 RootedId id(cx, NameToId(name)); 2094 return DefineDataProperty(cx, obj, id, value, attrs); 2095 } 2096 2097 bool js::DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, 2098 HandleValue value, unsigned attrs) { 2099 RootedId id(cx); 2100 if (!IndexToId(cx, index, &id)) { 2101 return false; 2102 } 2103 return DefineDataProperty(cx, obj, id, value, attrs); 2104 } 2105 2106 /*** SpiderMonkey nonstandard internal methods ******************************/ 2107 2108 // Mark an object as having an immutable prototype 2109 // 2110 // NOTE: This does not correspond to the SetImmutablePrototype ECMAScript 2111 // method. 2112 bool js::SetImmutablePrototype(JSContext* cx, HandleObject obj, 2113 bool* succeeded) { 2114 if (obj->hasDynamicPrototype()) { 2115 return Proxy::setImmutablePrototype(cx, obj, succeeded); 2116 } 2117 2118 if (!JSObject::setFlag(cx, obj, ObjectFlag::ImmutablePrototype)) { 2119 return false; 2120 } 2121 *succeeded = true; 2122 return true; 2123 } 2124 2125 bool js::GetPropertyDescriptor( 2126 JSContext* cx, HandleObject obj, HandleId id, 2127 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc, 2128 MutableHandleObject holder) { 2129 RootedObject pobj(cx); 2130 for (pobj = obj; pobj;) { 2131 if (!GetOwnPropertyDescriptor(cx, pobj, id, desc)) { 2132 return false; 2133 } 2134 2135 if (desc.isSome()) { 2136 holder.set(pobj); 2137 return true; 2138 } 2139 2140 if (!GetPrototype(cx, pobj, &pobj)) { 2141 return false; 2142 } 2143 } 2144 2145 MOZ_ASSERT(desc.isNothing()); 2146 holder.set(nullptr); 2147 return true; 2148 } 2149 2150 /* * */ 2151 2152 extern bool PropertySpecNameToId(JSContext* cx, JSPropertySpec::Name name, 2153 MutableHandleId id); 2154 2155 // If a property or method is part of an experimental feature that can be 2156 // disabled at run-time by a preference, we keep it in the JSFunctionSpec / 2157 // JSPropertySpec list, but omit the definition if the preference is off. 2158 JS_PUBLIC_API bool js::ShouldIgnorePropertyDefinition(JSContext* cx, 2159 JSProtoKey key, jsid id) { 2160 if (!cx->realm()->creationOptions().getToSourceEnabled() && 2161 (id == NameToId(cx->names().toSource) || 2162 id == NameToId(cx->names().uneval))) { 2163 return true; 2164 } 2165 2166 if (key == JSProto_FinalizationRegistry && 2167 !JS::Prefs::experimental_weakrefs_expose_cleanupSome() && 2168 id == NameToId(cx->names().cleanupSome)) { 2169 return true; 2170 } 2171 2172 // It's gently surprising that this is JSProto_Function, but the trick 2173 // to realize is that this is a -constructor function-, not a function 2174 // on the prototype; and the proto of the constructor is JSProto_Function. 2175 if (key == JSProto_Function) { 2176 if (!JS::Prefs::experimental_uint8array_base64() && 2177 (id == NameToId(cx->names().fromBase64) || 2178 id == NameToId(cx->names().fromHex))) { 2179 return true; 2180 } 2181 } 2182 2183 if (key == JSProto_Uint8Array && 2184 !JS::Prefs::experimental_uint8array_base64() && 2185 (id == NameToId(cx->names().setFromBase64) || 2186 id == NameToId(cx->names().setFromHex) || 2187 id == NameToId(cx->names().toBase64) || 2188 id == NameToId(cx->names().toHex))) { 2189 return true; 2190 } 2191 2192 // It's gently surprising that this is JSProto_Function, but the trick 2193 // to realize is that this is a -constructor function-, not a function 2194 // on the prototype; and the proto of the constructor is JSProto_Function. 2195 if (key == JSProto_Function) { 2196 if (!JS::Prefs::experimental_uint8array_base64() && 2197 (id == NameToId(cx->names().fromBase64) || 2198 id == NameToId(cx->names().fromHex))) { 2199 return true; 2200 } 2201 if (!JS::Prefs::experimental_error_iserror() && 2202 id == NameToId(cx->names().isError)) { 2203 return true; 2204 } 2205 if (!JS::Prefs::experimental_iterator_sequencing() && 2206 id == NameToId(cx->names().concat)) { 2207 return true; 2208 } 2209 if (!JS::Prefs::experimental_joint_iteration() && 2210 (id == NameToId(cx->names().zip) || 2211 id == NameToId(cx->names().zipKeyed))) { 2212 return true; 2213 } 2214 } 2215 2216 #ifdef JS_HAS_INTL_API 2217 if (key == JSProto_Date && !JS::Prefs::experimental_temporal() && 2218 id == NameToId(cx->names().toTemporalInstant)) { 2219 return true; 2220 } 2221 #endif 2222 2223 #ifdef NIGHTLY_BUILD 2224 // It's gently surprising that this is JSProto_Function, but the trick 2225 // to realize is that this is a -constructor function-, not a function 2226 // on the prototype; and the proto of the constructor is JSProto_Function. 2227 if (key == JSProto_Function) { 2228 if (!JS::Prefs::experimental_iterator_range() && 2229 (id == NameToId(cx->names().range))) { 2230 return true; 2231 } 2232 if (!JS::Prefs::experimental_promise_allkeyed() && 2233 (id == NameToId(cx->names().allKeyed) || 2234 id == NameToId(cx->names().allSettledKeyed))) { 2235 return true; 2236 } 2237 } 2238 if (key == JSProto_Map || key == JSProto_WeakMap) { 2239 if (!JS::Prefs::experimental_upsert() && 2240 (id == NameToId(cx->names().getOrInsert) || 2241 id == NameToId(cx->names().getOrInsertComputed))) { 2242 return true; 2243 } 2244 } 2245 if (key == JSProto_ArrayBuffer && 2246 !JS::Prefs::experimental_arraybuffer_immutable()) { 2247 if (id == NameToId(cx->names().immutable) || 2248 id == NameToId(cx->names().sliceToImmutable) || 2249 id == NameToId(cx->names().transferToImmutable)) { 2250 return true; 2251 } 2252 } 2253 if (key == JSProto_Iterator && !JS::Prefs::experimental_iterator_chunking()) { 2254 if (id == NameToId(cx->names().chunks) || 2255 id == NameToId(cx->names().windows)) { 2256 return true; 2257 } 2258 } 2259 if (key == JSProto_Iterator && !JS::Prefs::experimental_iterator_join()) { 2260 if (id == NameToId(cx->names().join)) { 2261 return true; 2262 } 2263 } 2264 #endif 2265 2266 if (key == JSProto_Function && 2267 !JS::Prefs::experimental_error_capture_stack_trace() && 2268 id == NameToId(cx->names().captureStackTrace)) { 2269 return true; 2270 } 2271 2272 if (key == JSProto_Math && !JS::Prefs::experimental_math_sumprecise() && 2273 id == NameToId(cx->names().sumPrecise)) { 2274 return true; 2275 } 2276 2277 if (key == JSProto_Atomics && !JS::Prefs::experimental_atomics_pause() && 2278 id == NameToId(cx->names().pause)) { 2279 return true; 2280 } 2281 if (key == JSProto_Atomics && !JS::Prefs::atomics_wait_async() && 2282 id == NameToId(cx->names().waitAsync)) { 2283 return true; 2284 } 2285 2286 return false; 2287 } 2288 2289 static bool DefineFunctionFromSpec(JSContext* cx, HandleObject obj, 2290 const JSFunctionSpec* fs) { 2291 RootedId id(cx); 2292 if (!PropertySpecNameToId(cx, fs->name, &id)) { 2293 return false; 2294 } 2295 2296 if (ShouldIgnorePropertyDefinition(cx, StandardProtoKeyOrNull(obj), id)) { 2297 return true; 2298 } 2299 2300 JSFunction* fun = NewFunctionFromSpec(cx, fs, id); 2301 if (!fun) { 2302 return false; 2303 } 2304 2305 RootedValue funVal(cx, ObjectValue(*fun)); 2306 return DefineDataProperty(cx, obj, id, funVal, fs->flags & ~JSFUN_FLAGS_MASK); 2307 } 2308 2309 bool js::DefineFunctions(JSContext* cx, HandleObject obj, 2310 const JSFunctionSpec* fs) { 2311 for (; fs->name; fs++) { 2312 if (!DefineFunctionFromSpec(cx, obj, fs)) { 2313 return false; 2314 } 2315 } 2316 return true; 2317 } 2318 2319 /*** ToPrimitive ************************************************************/ 2320 2321 /* 2322 * Gets |obj[id]|. If that value's not callable, returns true and stores an 2323 * object value in *vp. If it's callable, calls it with no arguments and |obj| 2324 * as |this|, returning the result in *vp. 2325 * 2326 * This is a mini-abstraction for ES6 draft rev 36 (2015 Mar 17), 2327 * 7.1.1, second algorithm (OrdinaryToPrimitive), steps 5.a-c. 2328 */ 2329 static bool MaybeCallMethod(JSContext* cx, HandleObject obj, HandleId id, 2330 MutableHandleValue vp) { 2331 if (!GetProperty(cx, obj, obj, id, vp)) { 2332 return false; 2333 } 2334 if (!IsCallable(vp)) { 2335 vp.setObject(*obj); 2336 return true; 2337 } 2338 2339 return js::Call(cx, vp, obj, vp); 2340 } 2341 2342 static bool ReportCantConvert(JSContext* cx, unsigned errorNumber, 2343 HandleObject obj, JSType hint) { 2344 const JSClass* clasp = obj->getClass(); 2345 2346 // Avoid recursive death when decompiling in ReportValueError. 2347 RootedString str(cx); 2348 if (hint == JSTYPE_STRING) { 2349 str = JS_AtomizeString(cx, clasp->name); 2350 if (!str) { 2351 return false; 2352 } 2353 } else { 2354 str = nullptr; 2355 } 2356 2357 RootedValue val(cx, ObjectValue(*obj)); 2358 ReportValueError(cx, errorNumber, JSDVG_SEARCH_STACK, val, str, 2359 hint == JSTYPE_UNDEFINED ? "primitive type" 2360 : hint == JSTYPE_STRING ? "string" 2361 : "number"); 2362 return false; 2363 } 2364 2365 bool JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, 2366 MutableHandleValue vp) { 2367 MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || 2368 hint == JSTYPE_UNDEFINED); 2369 2370 Rooted<jsid> id(cx); 2371 2372 const JSClass* clasp = obj->getClass(); 2373 if (hint == JSTYPE_STRING) { 2374 id = NameToId(cx->names().toString); 2375 2376 bool calledToString = false; 2377 if (clasp == &StringObject::class_) { 2378 // Optimize (new String(...)).toString(). 2379 StringObject* nobj = &obj->as<StringObject>(); 2380 if (HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) { 2381 vp.setString(nobj->unbox()); 2382 return true; 2383 } 2384 } else if (clasp == &PlainObject::class_) { 2385 JSFunction* fun; 2386 if (GetPropertyPure(cx, obj, id, vp.address()) && 2387 IsFunctionObject(vp, &fun)) { 2388 // Common case: we have a toString function. Try to short-circuit if 2389 // it's Object.prototype.toString and there's no @@toStringTag. 2390 if (fun->maybeNative() == obj_toString && 2391 !MaybeHasInterestingSymbolProperty( 2392 cx, obj, cx->wellKnownSymbols().toStringTag)) { 2393 vp.setString(cx->names().object_Object_); 2394 return true; 2395 } 2396 if (!js::Call(cx, vp, obj, vp)) { 2397 return false; 2398 } 2399 calledToString = true; 2400 } 2401 } 2402 2403 if (!calledToString) { 2404 if (!MaybeCallMethod(cx, obj, id, vp)) { 2405 return false; 2406 } 2407 } 2408 if (vp.isPrimitive()) { 2409 return true; 2410 } 2411 2412 id = NameToId(cx->names().valueOf); 2413 if (!MaybeCallMethod(cx, obj, id, vp)) { 2414 return false; 2415 } 2416 if (vp.isPrimitive()) { 2417 return true; 2418 } 2419 } else { 2420 id = NameToId(cx->names().valueOf); 2421 2422 if (clasp == &StringObject::class_) { 2423 // Optimize new String(...).valueOf(). 2424 StringObject* nobj = &obj->as<StringObject>(); 2425 if (HasNativeMethodPure(nobj, cx->names().valueOf, str_toString, cx)) { 2426 vp.setString(nobj->unbox()); 2427 return true; 2428 } 2429 } else if (clasp == &NumberObject::class_) { 2430 // Optimize new Number(...).valueOf(). 2431 NumberObject* nobj = &obj->as<NumberObject>(); 2432 if (HasNativeMethodPure(nobj, cx->names().valueOf, num_valueOf, cx)) { 2433 vp.setNumber(nobj->unbox()); 2434 return true; 2435 } 2436 } else if (clasp == &DateObject::class_) { 2437 DateObject* dateObj = &obj->as<DateObject>(); 2438 if (HasNativeMethodPure(dateObj, cx->names().valueOf, date_valueOf, cx)) { 2439 vp.set(dateObj->UTCTime()); 2440 return true; 2441 } 2442 } 2443 2444 if (!MaybeCallMethod(cx, obj, id, vp)) { 2445 return false; 2446 } 2447 if (vp.isPrimitive()) { 2448 return true; 2449 } 2450 2451 id = NameToId(cx->names().toString); 2452 if (!MaybeCallMethod(cx, obj, id, vp)) { 2453 return false; 2454 } 2455 if (vp.isPrimitive()) { 2456 return true; 2457 } 2458 } 2459 2460 return ReportCantConvert(cx, JSMSG_CANT_CONVERT_TO, obj, hint); 2461 } 2462 2463 bool js::ToPrimitiveSlow(JSContext* cx, JSType preferredType, 2464 MutableHandleValue vp) { 2465 // Step numbers refer to the first algorithm listed in ES6 draft rev 36 2466 // (2015 Mar 17) 7.1.1 ToPrimitive. 2467 MOZ_ASSERT(preferredType == JSTYPE_UNDEFINED || 2468 preferredType == JSTYPE_STRING || preferredType == JSTYPE_NUMBER); 2469 RootedTuple<JSObject*, Value, Value> roots(cx); 2470 RootedField<JSObject*, 0> obj(roots, &vp.toObject()); 2471 2472 // Steps 4-5. 2473 RootedField<Value, 1> method(roots); 2474 if (!GetInterestingSymbolProperty(cx, obj, cx->wellKnownSymbols().toPrimitive, 2475 &method)) { 2476 return false; 2477 } 2478 2479 // Step 6. 2480 if (!method.isNullOrUndefined()) { 2481 // Step 6 of GetMethod. js::Call() below would do this check and throw a 2482 // TypeError anyway, but this produces a better error message. 2483 if (!IsCallable(method)) { 2484 return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_NOT_CALLABLE, obj, 2485 preferredType); 2486 } 2487 2488 // Steps 1-3, 6.a-b. 2489 RootedField<Value, 2> arg0( 2490 roots, 2491 StringValue(preferredType == JSTYPE_STRING ? cx->names().string 2492 : preferredType == JSTYPE_NUMBER ? cx->names().number 2493 : cx->names().default_)); 2494 2495 if (!js::Call(cx, method, vp, arg0, vp)) { 2496 return false; 2497 } 2498 2499 // Steps 6.c-d. 2500 if (vp.isObject()) { 2501 return ReportCantConvert(cx, JSMSG_TOPRIMITIVE_RETURNED_OBJECT, obj, 2502 preferredType); 2503 } 2504 return true; 2505 } 2506 2507 return OrdinaryToPrimitive(cx, obj, preferredType, vp); 2508 } 2509 2510 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */ 2511 bool js::ToPropertyKeySlow(JSContext* cx, HandleValue argument, 2512 MutableHandleId result) { 2513 MOZ_ASSERT(argument.isObject()); 2514 2515 // Steps 1-2. 2516 RootedValue key(cx, argument); 2517 if (!ToPrimitiveSlow(cx, JSTYPE_STRING, &key)) { 2518 return false; 2519 } 2520 2521 // Steps 3-4. 2522 return PrimitiveValueToId<CanGC>(cx, key, result); 2523 } 2524 2525 /* * */ 2526 2527 bool js::IsPrototypeOf(JSContext* cx, HandleObject protoObj, JSObject* obj, 2528 bool* result) { 2529 RootedObject obj2(cx, obj); 2530 for (;;) { 2531 // The [[Prototype]] chain might be cyclic. 2532 if (!CheckForInterrupt(cx)) { 2533 return false; 2534 } 2535 if (!GetPrototype(cx, obj2, &obj2)) { 2536 return false; 2537 } 2538 if (!obj2) { 2539 *result = false; 2540 return true; 2541 } 2542 if (obj2 == protoObj) { 2543 *result = true; 2544 return true; 2545 } 2546 } 2547 } 2548 2549 JSObject* js::PrimitiveToObject(JSContext* cx, const Value& v) { 2550 MOZ_ASSERT(v.isPrimitive()); 2551 2552 switch (v.type()) { 2553 case ValueType::String: { 2554 Rooted<JSString*> str(cx, v.toString()); 2555 return StringObject::create(cx, str); 2556 } 2557 case ValueType::Double: 2558 case ValueType::Int32: 2559 return NumberObject::create(cx, v.toNumber()); 2560 case ValueType::Boolean: 2561 return BooleanObject::create(cx, v.toBoolean()); 2562 case ValueType::Symbol: { 2563 RootedSymbol symbol(cx, v.toSymbol()); 2564 return SymbolObject::create(cx, symbol); 2565 } 2566 case ValueType::BigInt: { 2567 RootedBigInt bigInt(cx, v.toBigInt()); 2568 return BigIntObject::create(cx, bigInt); 2569 } 2570 case ValueType::Undefined: 2571 case ValueType::Null: 2572 case ValueType::Magic: 2573 case ValueType::PrivateGCThing: 2574 case ValueType::Object: 2575 break; 2576 } 2577 2578 MOZ_CRASH("unexpected type"); 2579 } 2580 2581 // Like PrimitiveToObject, but returns the JSProtoKey of the prototype that 2582 // would be used without actually creating the object. 2583 JSProtoKey js::PrimitiveToProtoKey(JSContext* cx, const Value& v) { 2584 MOZ_ASSERT(v.isPrimitive()); 2585 2586 switch (v.type()) { 2587 case ValueType::String: 2588 return JSProto_String; 2589 case ValueType::Double: 2590 case ValueType::Int32: 2591 return JSProto_Number; 2592 case ValueType::Boolean: 2593 return JSProto_Boolean; 2594 case ValueType::Symbol: 2595 return JSProto_Symbol; 2596 case ValueType::BigInt: 2597 return JSProto_BigInt; 2598 case ValueType::Undefined: 2599 case ValueType::Null: 2600 case ValueType::Magic: 2601 case ValueType::PrivateGCThing: 2602 case ValueType::Object: 2603 break; 2604 } 2605 2606 MOZ_CRASH("unexpected type"); 2607 } 2608 2609 /* 2610 * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might 2611 * already be an object, use ToObject. reportScanStack controls how null and 2612 * undefined errors are reported. 2613 * 2614 * Callers must handle the already-object case. 2615 */ 2616 JSObject* js::ToObjectSlow(JSContext* cx, JS::HandleValue val, 2617 bool reportScanStack) { 2618 MOZ_ASSERT(!val.isMagic()); 2619 MOZ_ASSERT(!val.isObject()); 2620 2621 if (val.isNullOrUndefined()) { 2622 ReportIsNullOrUndefinedForPropertyAccess( 2623 cx, val, reportScanStack ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK); 2624 return nullptr; 2625 } 2626 2627 return PrimitiveToObject(cx, val); 2628 } 2629 2630 JSObject* js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, 2631 int valIndex, HandleId key) { 2632 MOZ_ASSERT(!val.isMagic()); 2633 MOZ_ASSERT(!val.isObject()); 2634 2635 if (val.isNullOrUndefined()) { 2636 ReportIsNullOrUndefinedForPropertyAccess(cx, val, valIndex, key); 2637 return nullptr; 2638 } 2639 2640 return PrimitiveToObject(cx, val); 2641 } 2642 2643 JSObject* js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, 2644 int valIndex, 2645 Handle<PropertyName*> key) { 2646 MOZ_ASSERT(!val.isMagic()); 2647 MOZ_ASSERT(!val.isObject()); 2648 2649 if (val.isNullOrUndefined()) { 2650 RootedId keyId(cx, NameToId(key)); 2651 ReportIsNullOrUndefinedForPropertyAccess(cx, val, valIndex, keyId); 2652 return nullptr; 2653 } 2654 2655 return PrimitiveToObject(cx, val); 2656 } 2657 2658 JSObject* js::ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, 2659 int valIndex, 2660 HandleValue keyValue) { 2661 MOZ_ASSERT(!val.isMagic()); 2662 MOZ_ASSERT(!val.isObject()); 2663 2664 if (val.isNullOrUndefined()) { 2665 RootedId key(cx); 2666 if (keyValue.isPrimitive()) { 2667 if (!PrimitiveValueToId<CanGC>(cx, keyValue, &key)) { 2668 return nullptr; 2669 } 2670 ReportIsNullOrUndefinedForPropertyAccess(cx, val, valIndex, key); 2671 } else { 2672 ReportIsNullOrUndefinedForPropertyAccess(cx, val, valIndex); 2673 } 2674 return nullptr; 2675 } 2676 2677 return PrimitiveToObject(cx, val); 2678 } 2679 2680 enum class SlotsKind { Fixed, Dynamic }; 2681 2682 class GetObjectSlotNameFunctor : public JS::TracingContext::Functor { 2683 NativeObject* obj; 2684 SlotsKind kind; 2685 2686 public: 2687 explicit GetObjectSlotNameFunctor(NativeObject* obj, SlotsKind kind) 2688 : obj(obj), kind(kind) {} 2689 virtual void operator()(JS::TracingContext* trc, const char* name, char* buf, 2690 size_t bufsize) override; 2691 }; 2692 2693 void GetObjectSlotNameFunctor::operator()(JS::TracingContext* tcx, 2694 const char* name, char* buf, 2695 size_t bufsize) { 2696 MOZ_ASSERT(tcx->index() != JS::TracingContext::InvalidIndex); 2697 2698 uint32_t slot = uint32_t(tcx->index()); 2699 if (kind == SlotsKind::Dynamic) { 2700 slot += obj->numFixedSlots(); 2701 } 2702 2703 Maybe<PropertyKey> key; 2704 NativeShape* shape = obj->as<NativeObject>().shape(); 2705 for (ShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) { 2706 if (iter->hasSlot() && iter->slot() == slot) { 2707 key.emplace(iter->key()); 2708 break; 2709 } 2710 } 2711 2712 if (key.isNothing()) { 2713 do { 2714 const char* slotname = nullptr; 2715 const char* pattern = nullptr; 2716 if (obj->is<GlobalObject>()) { 2717 pattern = "CLASS_OBJECT(%s)"; 2718 if (false) { 2719 ; 2720 } 2721 #define TEST_SLOT_MATCHES_PROTOTYPE(name, clasp) \ 2722 else if ((JSProto_##name) == slot) { \ 2723 slotname = #name; \ 2724 } 2725 JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE) 2726 #undef TEST_SLOT_MATCHES_PROTOTYPE 2727 } else { 2728 pattern = "%s"; 2729 if (obj->is<EnvironmentObject>()) { 2730 if (slot == EnvironmentObject::enclosingEnvironmentSlot()) { 2731 slotname = "enclosing_environment"; 2732 } else if (obj->is<CallObject>()) { 2733 if (slot == CallObject::calleeSlot()) { 2734 slotname = "callee_slot"; 2735 } 2736 } else if (obj->is<WithEnvironmentObject>()) { 2737 if (slot == WithEnvironmentObject::objectSlot()) { 2738 slotname = "with_object"; 2739 } else if (slot == WithEnvironmentObject::thisSlot()) { 2740 slotname = "with_this"; 2741 } 2742 } 2743 } 2744 } 2745 2746 if (slotname) { 2747 snprintf(buf, bufsize, pattern, slotname); 2748 } else { 2749 snprintf(buf, bufsize, "**UNKNOWN SLOT %" PRIu32 "**", slot); 2750 } 2751 } while (false); 2752 } else { 2753 if (key->isInt()) { 2754 snprintf(buf, bufsize, "%" PRId32, key->toInt()); 2755 } else if (key->isAtom()) { 2756 PutEscapedString(buf, bufsize, key->toAtom(), 0); 2757 } else if (key->isSymbol()) { 2758 snprintf(buf, bufsize, "**SYMBOL KEY**"); 2759 } else { 2760 MOZ_CRASH("Unexpected key kind"); 2761 } 2762 } 2763 } 2764 2765 /*** Debugging routines *****************************************************/ 2766 2767 #if defined(DEBUG) || defined(JS_JITSPEW) 2768 2769 /* 2770 * Routines to print out values during debugging. These are JS_PUBLIC_API to 2771 * help the debugger find them and to support temporarily hacking js::Dump* 2772 * calls into other code. 2773 */ 2774 2775 namespace js { 2776 2777 // We don't want jsfriendapi.h to depend on GenericPrinter, 2778 // so these functions are declared directly in the cpp. 2779 2780 JS_PUBLIC_API void DumpValue(const JS::Value& val, js::GenericPrinter& out); 2781 2782 JS_PUBLIC_API void DumpId(jsid id, js::GenericPrinter& out); 2783 2784 JS_PUBLIC_API void DumpInterpreterFrame(JSContext* cx, js::GenericPrinter& out, 2785 InterpreterFrame* start = nullptr); 2786 2787 } // namespace js 2788 2789 JS_PUBLIC_API void js::DumpValue(const Value& val, js::GenericPrinter& out) { 2790 val.dump(out); 2791 } 2792 2793 JS_PUBLIC_API void js::DumpId(jsid id, js::GenericPrinter& out) { 2794 out.printf("jsid %p = ", (void*)id.asRawBits()); 2795 id.dump(out); 2796 } 2797 2798 bool JSObject::hasSameRealmAs(JSContext* cx) const { 2799 return nonCCWRealm() == cx->realm(); 2800 } 2801 2802 bool JSObject::uninlinedIsProxyObject() const { return is<ProxyObject>(); } 2803 2804 bool JSObject::uninlinedNonProxyIsExtensible() const { 2805 return nonProxyIsExtensible(); 2806 } 2807 2808 void JSObject::dump() const { 2809 js::Fprinter out(stderr); 2810 dump(out); 2811 } 2812 2813 void JSObject::dump(js::GenericPrinter& out) const { 2814 js::JSONPrinter json(out); 2815 dump(json); 2816 out.put("\n"); 2817 } 2818 2819 void JSObject::dump(js::JSONPrinter& json) const { 2820 json.beginObject(); 2821 dumpFields(json); 2822 json.endObject(); 2823 } 2824 2825 # define FOR_EACH_CLASS(M) \ 2826 M(ArrayBufferViewObject) \ 2827 M(ArrayBufferObject) \ 2828 M(JSFunction) \ 2829 M(PromiseObject) \ 2830 M(RegExpObject) 2831 2832 static void DumpOwnFields(const JSObject* obj, js::JSONPrinter& json) { 2833 # define CALL(CLASS) \ 2834 if (obj->is<CLASS>()) { \ 2835 obj->as<CLASS>().dumpOwnFields(json); \ 2836 return; \ 2837 } 2838 FOR_EACH_CLASS(CALL) 2839 # undef CALL 2840 } 2841 2842 static void DumpOwnStringContent(const JSObject* obj, js::GenericPrinter& out) { 2843 # define CALL(CLASS) \ 2844 if (obj->is<CLASS>()) { \ 2845 out.put(" "); \ 2846 obj->as<CLASS>().dumpOwnStringContent(out); \ 2847 return; \ 2848 } 2849 FOR_EACH_CLASS(CALL) 2850 # undef CALL 2851 } 2852 2853 # undef FOR_EACH_CLASS 2854 2855 void JSObject::dumpFields(js::JSONPrinter& json) const { 2856 json.formatProperty("address", "(JSObject*)0x%p", this); 2857 2858 if (IsCrossCompartmentWrapper(this)) { 2859 json.formatProperty("compartment", "(JS::Compartment*)0x%p", compartment()); 2860 } else { 2861 JSObject* globalObj = &nonCCWGlobal(); 2862 js::GenericPrinter& out = json.beginStringProperty("nonCCWGlobal"); 2863 globalObj->dumpStringContent(out); 2864 json.endStringProperty(); 2865 } 2866 2867 const JSClass* clasp = getClass(); 2868 json.formatProperty("clasp", "<%s @ (JSClass*)0x%p>", clasp->name, clasp); 2869 2870 js::GenericPrinter& out = json.beginStringProperty("shape"); 2871 shape()->dumpStringContent(out); 2872 json.endStringProperty(); 2873 2874 json.beginObjectProperty("shape.base"); 2875 shape()->base()->dumpFields(json); 2876 json.endObject(); 2877 2878 if (IsProxy(this)) { 2879 const js::BaseProxyHandler* handler = GetProxyHandler(this); 2880 if (IsDeadProxyObject(this)) { 2881 json.formatProperty("handler", "(js::DeadObjectProxy*)0x%p", handler); 2882 } else if (IsCrossCompartmentWrapper(this)) { 2883 json.formatProperty("handler", "(js::CrossCompartmentWrapper*)0x%p", 2884 handler); 2885 } else { 2886 json.formatProperty("handler", "(js::BaseProxyHandler*)0x%p", handler); 2887 } 2888 2889 Value priv = GetProxyPrivate(this); 2890 if (!priv.isUndefined()) { 2891 js::GenericPrinter& out = json.beginStringProperty("private"); 2892 priv.dumpStringContent(out); 2893 json.endStringProperty(); 2894 } 2895 2896 Value expando = GetProxyExpando(this); 2897 if (!expando.isNull()) { 2898 js::GenericPrinter& out = json.beginStringProperty("expando"); 2899 expando.dumpStringContent(out); 2900 json.endStringProperty(); 2901 } 2902 2903 if (is<DebugEnvironmentProxy>()) { 2904 json.boolProperty("isQualifiedVarObj", isQualifiedVarObj()); 2905 json.boolProperty("isUnqualifiedVarObj", isUnqualifiedVarObj()); 2906 } 2907 } 2908 2909 DumpOwnFields(this, json); 2910 2911 if (is<NativeObject>()) { 2912 const auto* nobj = &as<NativeObject>(); 2913 2914 js::GenericPrinter& out = json.beginStringProperty("elementsHeader"); 2915 nobj->getElementsHeader()->dumpStringContent(out); 2916 json.endStringProperty(); 2917 2918 uint32_t reserved = JSCLASS_RESERVED_SLOTS(clasp); 2919 if (reserved) { 2920 char name[256]; 2921 json.beginObjectProperty("reservedSlots"); 2922 for (uint32_t i = 0; i < reserved; i++) { 2923 SprintfLiteral(name, "%u", i); 2924 js::GenericPrinter& out = json.beginStringProperty(name); 2925 nobj->getSlot(i).dumpStringContent(out); 2926 json.endStringProperty(); 2927 } 2928 json.endObject(); 2929 } 2930 2931 json.beginObjectProperty("properties"); 2932 if (PropMap* map = nobj->shape()->propMap()) { 2933 Vector<PropMap*, 8, SystemAllocPolicy> maps; 2934 while (true) { 2935 if (!maps.append(map)) { 2936 json.property("error", "*oom in JSObject::dumpFields*"); 2937 break; 2938 } 2939 if (!map->hasPrevious()) { 2940 break; 2941 } 2942 map = map->asLinked()->previous(); 2943 } 2944 2945 for (size_t i = maps.length(); i > 0; i--) { 2946 size_t index = i - 1; 2947 PropMap* map = maps[index]; 2948 uint32_t len = (index == 0) ? shape()->asNative().propMapLength() 2949 : PropMap::Capacity; 2950 for (uint32_t j = 0; j < len; j++) { 2951 if (!map->hasKey(j)) { 2952 MOZ_ASSERT(map->isDictionary()); 2953 continue; 2954 } 2955 2956 JS::UniqueChars propChars = map->getPropertyNameAt(j); 2957 if (!propChars) { 2958 json.property("error", "*oom in PropMap::getPropertyNameAt*"); 2959 continue; 2960 } 2961 2962 js::GenericPrinter& out = json.beginStringProperty(propChars.get()); 2963 2964 PropertyInfoWithKey prop = map->getPropertyInfoWithKey(j); 2965 if (prop.isDataProperty()) { 2966 nobj->getSlot(prop.slot()).dumpStringContent(out); 2967 out.put(" "); 2968 } else if (prop.isAccessorProperty()) { 2969 out.printf("getter=0x%p, setter=0x%p", nobj->getGetter(prop), 2970 nobj->getSetter(prop)); 2971 out.put(" "); 2972 } 2973 2974 out.put("("); 2975 map->dumpDescriptorStringContentAt(out, j); 2976 out.put(")"); 2977 2978 json.endStringProperty(); 2979 } 2980 } 2981 } 2982 json.endObject(); 2983 2984 uint32_t slots = nobj->getDenseInitializedLength(); 2985 if (slots) { 2986 char name[64]; 2987 json.beginObjectProperty("elements"); 2988 for (uint32_t i = 0; i < slots; i++) { 2989 SprintfLiteral(name, "%u", i); 2990 js::GenericPrinter& out = json.beginStringProperty(name); 2991 nobj->getDenseElement(i).dumpStringContent(out); 2992 json.endStringProperty(); 2993 } 2994 json.endObject(); 2995 } 2996 } 2997 } 2998 2999 void JSObject::dumpStringContent(js::GenericPrinter& out) const { 3000 out.printf("<%s", getClass()->name); 3001 3002 DumpOwnStringContent(this, out); 3003 3004 out.printf(" @ (JSObject*)0x%p>", this); 3005 } 3006 3007 static void MaybeDumpScope(Scope* scope, js::GenericPrinter& out) { 3008 if (scope) { 3009 out.printf(" scope: %s\n", ScopeKindString(scope->kind())); 3010 for (BindingIter bi(scope); bi; bi++) { 3011 out.put(" "); 3012 StringValue(bi.name()).dump(out); 3013 } 3014 } 3015 } 3016 3017 static void MaybeDumpValue(const char* name, const Value& v, 3018 js::GenericPrinter& out) { 3019 if (!v.isNull()) { 3020 out.printf(" %s: ", name); 3021 v.dump(out); 3022 } 3023 } 3024 3025 JS_PUBLIC_API void js::DumpInterpreterFrame(JSContext* cx, 3026 js::GenericPrinter& out, 3027 InterpreterFrame* start) { 3028 /* This should only called during live debugging. */ 3029 ScriptFrameIter i(cx); 3030 if (!start) { 3031 if (i.done()) { 3032 out.printf("no stack for cx = %p\n", (void*)cx); 3033 return; 3034 } 3035 } else { 3036 while (!i.done() && !i.isJSJit() && i.interpFrame() != start) { 3037 ++i; 3038 } 3039 3040 if (i.done()) { 3041 out.printf("fp = %p not found in cx = %p\n", (void*)start, (void*)cx); 3042 return; 3043 } 3044 } 3045 3046 for (; !i.done(); ++i) { 3047 if (i.isJSJit()) { 3048 out.put("JIT frame\n"); 3049 } else { 3050 out.printf("InterpreterFrame at %p\n", (void*)i.interpFrame()); 3051 } 3052 3053 if (i.isFunctionFrame()) { 3054 out.put("callee fun: "); 3055 RootedValue v(cx); 3056 JSObject* fun = i.callee(cx); 3057 v.setObject(*fun); 3058 v.get().dump(out); 3059 } else { 3060 out.put("global or eval frame, no callee\n"); 3061 } 3062 3063 out.printf("file %s line %u\n", i.script()->filename(), 3064 i.script()->lineno()); 3065 3066 if (jsbytecode* pc = i.pc()) { 3067 out.printf(" pc = %p\n", pc); 3068 out.printf(" current op: %s\n", CodeName(JSOp(*pc))); 3069 MaybeDumpScope(i.script()->lookupScope(pc), out); 3070 } 3071 if (i.isFunctionFrame()) { 3072 MaybeDumpValue("this", i.thisArgument(cx), out); 3073 } 3074 if (!i.isJSJit()) { 3075 out.put(" rval: "); 3076 i.interpFrame()->returnValue().get().dump(out); 3077 } 3078 3079 out.put(" flags:"); 3080 if (i.isConstructing()) { 3081 out.put(" constructing"); 3082 } 3083 if (!i.isJSJit() && i.interpFrame()->isDebuggerEvalFrame()) { 3084 out.put(" debugger eval"); 3085 } 3086 if (i.isEvalFrame()) { 3087 out.put(" eval"); 3088 } 3089 out.putChar('\n'); 3090 3091 out.printf(" envChain: (JSObject*) %p\n", (void*)i.environmentChain(cx)); 3092 3093 out.putChar('\n'); 3094 } 3095 } 3096 3097 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */ 3098 3099 JS_PUBLIC_API void js::DumpBacktrace(JSContext* cx, FILE* fp) { 3100 Fprinter out(fp); 3101 js::DumpBacktrace(cx, out); 3102 } 3103 3104 JS_PUBLIC_API void js::DumpBacktrace(JSContext* cx, js::GenericPrinter& out) { 3105 size_t depth = 0; 3106 for (AllFramesIter i(cx); !i.done(); ++i, ++depth) { 3107 const char* filename; 3108 unsigned line; 3109 if (i.hasScript()) { 3110 filename = JS_GetScriptFilename(i.script()); 3111 line = PCToLineNumber(i.script(), i.pc()); 3112 } else { 3113 filename = i.filename(); 3114 line = i.computeLine(); 3115 } 3116 char frameType = i.isInterp() ? 'i' 3117 : i.isBaseline() ? 'b' 3118 : i.isIon() ? 'I' 3119 : i.isWasm() ? 'W' 3120 : '?'; 3121 3122 out.printf("#%zu %14p %c %s:%u", depth, i.rawFramePtr(), frameType, 3123 filename, line); 3124 3125 if (i.hasScript()) { 3126 out.printf(" (%p @ %zu)\n", i.script(), i.script()->pcToOffset(i.pc())); 3127 } else { 3128 out.printf(" (%p)\n", i.pc()); 3129 } 3130 } 3131 } 3132 3133 JS_PUBLIC_API void js::DumpBacktrace(JSContext* cx) { 3134 DumpBacktrace(cx, stdout); 3135 } 3136 3137 /* * */ 3138 3139 bool JSObject::isBackgroundFinalized() const { 3140 if (isTenured()) { 3141 return js::gc::IsBackgroundFinalized(asTenured().getAllocKind()); 3142 } 3143 3144 js::Nursery& nursery = runtimeFromMainThread()->gc.nursery(); 3145 return js::gc::IsBackgroundFinalized(allocKindForTenure(nursery)); 3146 } 3147 3148 js::gc::AllocKind JSObject::allocKind() const { 3149 if (isTenured()) { 3150 return asTenured().getAllocKind(); 3151 } 3152 3153 Nursery& nursery = runtimeFromMainThread()->gc.nursery(); 3154 return allocKindForTenure(nursery); 3155 } 3156 3157 js::gc::AllocKind JSObject::allocKindForTenure( 3158 const js::Nursery& nursery) const { 3159 using namespace js::gc; 3160 3161 MOZ_ASSERT(IsInsideNursery(this)); 3162 3163 if (is<NativeObject>()) { 3164 if (is<ArrayObject>()) { 3165 const NativeObject& nobj = as<NativeObject>(); 3166 MOZ_ASSERT(nobj.numFixedSlots() == 0); 3167 3168 /* Use minimal size object if we are just going to copy the pointer. */ 3169 if (!nursery.isInside(nobj.getUnshiftedElementsHeader())) { 3170 return gc::AllocKind::OBJECT0; 3171 } 3172 3173 size_t nelements = nobj.getDenseCapacity(); 3174 AllocKind kind = GetGCArrayKind(nelements); 3175 MOZ_ASSERT(GetObjectFinalizeKind(getClass()) == gc::FinalizeKind::None); 3176 MOZ_ASSERT(!IsFinalizedKind(kind)); 3177 return kind; 3178 } 3179 3180 if (is<JSFunction>()) { 3181 return as<JSFunction>().getAllocKind(); 3182 } 3183 3184 if (is<FixedLengthTypedArrayObject>()) { 3185 return as<FixedLengthTypedArrayObject>().allocKindForTenure(); 3186 } 3187 3188 return as<NativeObject>().allocKindForTenure(); 3189 } 3190 3191 // Handle all non-native objects. 3192 3193 // Proxies that are CrossCompartmentWrappers may be nursery allocated. 3194 if (is<ProxyObject>()) { 3195 return as<ProxyObject>().allocKindForTenure(); 3196 } 3197 3198 // WasmStructObjects have a variable-length tail which contains the first 3199 // few data fields, so make sure we copy it all over to the new object. 3200 if (is<WasmStructObject>()) { 3201 // Figure out the size of this object, from the object's TypeDef. 3202 const wasm::TypeDef* typeDef = &as<WasmStructObject>().typeDef(); 3203 AllocKind kind = typeDef->structType().allocKind_; 3204 return GetFinalizedAllocKindForClass(kind, getClass()); 3205 } 3206 3207 // WasmArrayObjects sometimes have a variable-length tail which contains the 3208 // data for small arrays. Make sure we copy it all over to the new object. 3209 MOZ_ASSERT(is<WasmArrayObject>()); 3210 gc::AllocKind allocKind = as<WasmArrayObject>().allocKind(); 3211 return allocKind; 3212 } 3213 3214 void JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, 3215 JS::ClassInfo* info, 3216 JS::RuntimeSizes* runtimeSizes) { 3217 // TODO: These will eventually count as GC heap memory. 3218 if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots()) { 3219 info->objectsMallocHeapSlots += 3220 gc::GetAllocSize(zone(), as<NativeObject>().getSlotsHeader()); 3221 } 3222 3223 if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) { 3224 void* allocatedElements = as<NativeObject>().getUnshiftedElementsHeader(); 3225 info->objectsMallocHeapElementsNormal += 3226 gc::GetAllocSize(zone(), allocatedElements); 3227 } 3228 3229 // Other things may be measured in the future if DMD indicates it is 3230 // worthwhile. 3231 if (is<JSFunction>() || is<PlainObject>() || is<ArrayObject>() || 3232 is<CallObject>() || is<RegExpObject>() || is<ProxyObject>()) { 3233 // Do nothing. But this function is hot, and we win by getting the 3234 // common cases out of the way early. Some stats on the most common 3235 // classes, as measured during a vanilla browser session: 3236 // - (53.7%, 53.7%): Function 3237 // - (18.0%, 71.7%): Object 3238 // - (16.9%, 88.6%): Array 3239 // - ( 3.9%, 92.5%): Call 3240 // - ( 2.8%, 95.3%): RegExp 3241 // - ( 1.0%, 96.4%): Proxy 3242 3243 // Note that any JSClass that is special cased below likely needs to 3244 // specify the JSCLASS_DELAY_METADATA_BUILDER flag, or else we will 3245 // probably crash if the object metadata callback attempts to get the 3246 // size of the new object (which Debugger code does) before private 3247 // slots are initialized. 3248 } else if (is<ArgumentsObject>()) { 3249 info->objectsMallocHeapMisc += 3250 as<ArgumentsObject>().sizeOfMisc(mallocSizeOf); 3251 } else if (is<MapObject>()) { 3252 info->objectsMallocHeapMisc += as<MapObject>().sizeOfData(mallocSizeOf); 3253 } else if (is<SetObject>()) { 3254 info->objectsMallocHeapMisc += as<SetObject>().sizeOfData(mallocSizeOf); 3255 } else if (is<PropertyIteratorObject>()) { 3256 info->objectsMallocHeapMisc += 3257 as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf); 3258 } else if (is<ArrayBufferObject>()) { 3259 ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info, 3260 runtimeSizes); 3261 } else if (is<SharedArrayBufferObject>()) { 3262 SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info, 3263 runtimeSizes); 3264 } else if (is<GlobalObject>()) { 3265 as<GlobalObject>().addSizeOfData(mallocSizeOf, info); 3266 } else if (is<WeakCollectionObject>()) { 3267 info->objectsMallocHeapMisc += 3268 as<WeakCollectionObject>().sizeOfExcludingThis(mallocSizeOf); 3269 } else if (is<WasmStructObject>()) { 3270 const WasmStructObject& s = as<WasmStructObject>(); 3271 info->objectsMallocHeapSlots += s.sizeOfExcludingThis(); 3272 } else if (is<WasmArrayObject>()) { 3273 const WasmArrayObject& a = as<WasmArrayObject>(); 3274 info->objectsMallocHeapElementsNormal += a.sizeOfExcludingThis(); 3275 } 3276 #ifdef JS_HAS_CTYPES 3277 else { 3278 // This must be the last case. 3279 info->objectsMallocHeapMisc += ctypes::SizeOfDataIfCDataObject( 3280 mallocSizeOf, const_cast<JSObject*>(this)); 3281 } 3282 #endif 3283 } 3284 3285 size_t JSObject::sizeOfIncludingThisInNursery( 3286 mozilla::MallocSizeOf mallocSizeOf) const { 3287 MOZ_ASSERT(!isTenured()); 3288 3289 const Nursery& nursery = runtimeFromMainThread()->gc.nursery(); 3290 size_t size = gc::Arena::thingSize(allocKindForTenure(nursery)); 3291 3292 if (is<NativeObject>()) { 3293 const NativeObject& native = as<NativeObject>(); 3294 3295 size += native.numDynamicSlots() * sizeof(Value); 3296 3297 if (native.hasDynamicElements()) { 3298 js::ObjectElements& elements = *native.getElementsHeader(); 3299 size += (elements.capacity + elements.numShiftedElements()) * 3300 sizeof(HeapSlot); 3301 } 3302 3303 if (is<ArgumentsObject>()) { 3304 size += as<ArgumentsObject>().sizeOfData(); 3305 } 3306 } else if (is<WasmStructObject>()) { 3307 const WasmStructObject& s = as<WasmStructObject>(); 3308 size += s.sizeOfExcludingThis(); 3309 } else if (is<WasmArrayObject>()) { 3310 const WasmArrayObject& a = as<WasmArrayObject>(); 3311 size += a.sizeOfExcludingThis(); 3312 } 3313 3314 return size; 3315 } 3316 3317 JS::ubi::Node::Size JS::ubi::Concrete<JSObject>::size( 3318 mozilla::MallocSizeOf mallocSizeOf) const { 3319 JSObject& obj = get(); 3320 3321 if (!obj.isTenured()) { 3322 return obj.sizeOfIncludingThisInNursery(mallocSizeOf); 3323 } 3324 3325 JS::ClassInfo info; 3326 obj.addSizeOfExcludingThis(mallocSizeOf, &info, nullptr); 3327 return obj.tenuredSizeOfThis() + info.sizeOfAllThings(); 3328 } 3329 3330 const char16_t JS::ubi::Concrete<JSObject>::concreteTypeName[] = u"JSObject"; 3331 3332 void JSObject::traceChildren(JSTracer* trc) { 3333 TraceCellHeaderEdge(trc, this, "shape"); 3334 3335 Shape* objShape = shape(); 3336 if (objShape->isNative()) { 3337 NativeObject* nobj = &as<NativeObject>(); 3338 3339 if (nobj->hasDynamicSlots()) { 3340 ObjectSlots* slots = nobj->getSlotsHeader(); 3341 MOZ_ASSERT(nobj->slots_ == slots->slots()); 3342 TraceBufferEdge(trc, nobj, &slots, "objectDynamicSlots buffer"); 3343 if (slots != nobj->getSlotsHeader()) { 3344 nobj->slots_ = slots->slots(); 3345 } 3346 } 3347 3348 if (nobj->hasDynamicElements()) { 3349 void* buffer = nobj->getUnshiftedElementsHeader(); 3350 uint32_t numShifted = nobj->getElementsHeader()->numShiftedElements(); 3351 TraceBufferEdge(trc, nobj, &buffer, "objectDynamicElements buffer"); 3352 if (buffer != nobj->getUnshiftedElementsHeader()) { 3353 nobj->elements_ = 3354 reinterpret_cast<ObjectElements*>(buffer)->elements() + numShifted; 3355 } 3356 } 3357 3358 const uint32_t nslots = nobj->slotSpan(); 3359 const uint32_t nfixed = nobj->numFixedSlots(); 3360 3361 { 3362 GetObjectSlotNameFunctor func(nobj, SlotsKind::Fixed); 3363 JS::AutoTracingDetails ctx(trc, func); 3364 TraceRange(trc, std::min(nslots, nfixed), nobj->fixedSlots(), 3365 "objectFixedSlots"); 3366 } 3367 3368 if (nslots > nfixed) { 3369 MOZ_ASSERT(nobj->hasDynamicSlots()); 3370 GetObjectSlotNameFunctor func(nobj, SlotsKind::Dynamic); 3371 JS::AutoTracingDetails ctx(trc, func); 3372 TraceRange(trc, nslots - nfixed, nobj->slots_, "objectDynamicSlots"); 3373 } 3374 3375 TraceRange(trc, nobj->getDenseInitializedLength(), 3376 nobj->getDenseElements().begin(), "objectElements"); 3377 } 3378 3379 // Call the trace hook at the end so that during a moving GC the trace hook 3380 // will see updated fields and slots. 3381 const JSClass* clasp = objShape->getObjectClass(); 3382 if (clasp->hasTrace()) { 3383 clasp->doTrace(trc, this); 3384 } 3385 } 3386 3387 // ES 2016 7.3.20. 3388 [[nodiscard]] JSObject* js::SpeciesConstructor( 3389 JSContext* cx, HandleObject obj, HandleObject defaultCtor, 3390 bool (*isDefaultSpecies)(JSContext*, JSFunction*)) { 3391 // Step 1 (implicit). 3392 3393 // Fast-path for steps 2 - 8. Applies if all of the following conditions 3394 // are met: 3395 // - obj.constructor can be retrieved without side-effects. 3396 // - obj.constructor[[@@species]] can be retrieved without side-effects. 3397 // - obj.constructor[[@@species]] is the builtin's original @@species 3398 // getter. 3399 RootedValue ctor(cx); 3400 bool ctorGetSucceeded = GetPropertyPure( 3401 cx, obj, NameToId(cx->names().constructor), ctor.address()); 3402 if (ctorGetSucceeded && ctor.isObject() && &ctor.toObject() == defaultCtor) { 3403 jsid speciesId = PropertyKey::Symbol(cx->wellKnownSymbols().species); 3404 JSFunction* getter; 3405 if (GetGetterPure(cx, defaultCtor, speciesId, &getter) && getter && 3406 isDefaultSpecies(cx, getter)) { 3407 return defaultCtor; 3408 } 3409 } 3410 3411 // Step 2. 3412 if (!ctorGetSucceeded && 3413 !GetProperty(cx, obj, obj, cx->names().constructor, &ctor)) { 3414 return nullptr; 3415 } 3416 3417 // Step 3. 3418 if (ctor.isUndefined()) { 3419 return defaultCtor; 3420 } 3421 3422 // Step 4. 3423 if (!ctor.isObject()) { 3424 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3425 JSMSG_OBJECT_REQUIRED, 3426 "object's 'constructor' property"); 3427 return nullptr; 3428 } 3429 3430 // Step 5. 3431 RootedObject ctorObj(cx, &ctor.toObject()); 3432 RootedValue s(cx); 3433 RootedId speciesId(cx, PropertyKey::Symbol(cx->wellKnownSymbols().species)); 3434 if (!GetProperty(cx, ctorObj, ctor, speciesId, &s)) { 3435 return nullptr; 3436 } 3437 3438 // Step 6. 3439 if (s.isNullOrUndefined()) { 3440 return defaultCtor; 3441 } 3442 3443 // Step 7. 3444 if (IsConstructor(s)) { 3445 return &s.toObject(); 3446 } 3447 3448 // Step 8. 3449 JS_ReportErrorNumberASCII( 3450 cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR, 3451 "[Symbol.species] property of object's constructor"); 3452 return nullptr; 3453 } 3454 3455 [[nodiscard]] JSObject* js::SpeciesConstructor( 3456 JSContext* cx, HandleObject obj, JSProtoKey ctorKey, 3457 bool (*isDefaultSpecies)(JSContext*, JSFunction*)) { 3458 RootedObject defaultCtor(cx, 3459 GlobalObject::getOrCreateConstructor(cx, ctorKey)); 3460 if (!defaultCtor) { 3461 return nullptr; 3462 } 3463 return SpeciesConstructor(cx, obj, defaultCtor, isDefaultSpecies); 3464 } 3465 3466 bool js::Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp) { 3467 if (MOZ_UNLIKELY(obj->is<ProxyObject>())) { 3468 return Proxy::boxedValue_unbox(cx, obj, vp); 3469 } 3470 3471 if (obj->is<BooleanObject>()) { 3472 vp.setBoolean(obj->as<BooleanObject>().unbox()); 3473 } else if (obj->is<NumberObject>()) { 3474 vp.setNumber(obj->as<NumberObject>().unbox()); 3475 } else if (obj->is<StringObject>()) { 3476 vp.setString(obj->as<StringObject>().unbox()); 3477 } else if (obj->is<DateObject>()) { 3478 vp.set(obj->as<DateObject>().UTCTime()); 3479 } else if (obj->is<SymbolObject>()) { 3480 vp.setSymbol(obj->as<SymbolObject>().unbox()); 3481 } else if (obj->is<BigIntObject>()) { 3482 vp.setBigInt(obj->as<BigIntObject>().unbox()); 3483 } else { 3484 vp.setUndefined(); 3485 } 3486 3487 return true; 3488 } 3489 3490 #ifdef DEBUG 3491 void js::AssertJSClassInvariants(const JSClass* clasp) { 3492 MOZ_ASSERT(JS::StringIsASCII(clasp->name)); 3493 3494 // Native objects shouldn't use the property operation hooks in ObjectOps. 3495 // Doing so could violate JIT invariants. 3496 // 3497 // Environment objects unfortunately use these hooks, but environment objects 3498 // are not exposed directly to script so they're generally less of an issue. 3499 if (clasp->isNativeObject() && clasp != &WithEnvironmentObject::class_ && 3500 clasp != &ModuleEnvironmentObject::class_ && 3501 clasp != &RuntimeLexicalErrorObject::class_) { 3502 MOZ_ASSERT(!clasp->getOpsLookupProperty()); 3503 MOZ_ASSERT_IF(clasp != &MappedArgumentsObject::class_, 3504 !clasp->getOpsDefineProperty()); 3505 MOZ_ASSERT(!clasp->getOpsHasProperty()); 3506 MOZ_ASSERT(!clasp->getOpsGetProperty()); 3507 MOZ_ASSERT(!clasp->getOpsSetProperty()); 3508 MOZ_ASSERT(!clasp->getOpsGetOwnPropertyDescriptor()); 3509 MOZ_ASSERT(!clasp->getOpsDeleteProperty()); 3510 } 3511 } 3512 3513 /* static */ 3514 void JSObject::debugCheckNewObject(Shape* shape, js::gc::AllocKind allocKind, 3515 js::gc::Heap heap) { 3516 const JSClass* clasp = shape->getObjectClass(); 3517 3518 if (!ClassCanHaveFixedData(clasp)) { 3519 NativeShape* nshape = &shape->asNative(); 3520 if (clasp == &ArrayObject::class_) { 3521 // Arrays can store the ObjectElements header inline. 3522 MOZ_ASSERT(nshape->numFixedSlots() == 0); 3523 } else { 3524 MOZ_ASSERT(gc::GetGCKindSlots(allocKind) == nshape->numFixedSlots()); 3525 } 3526 } 3527 3528 using namespace gc; 3529 if (!clasp->isProxyObject()) { 3530 // Check |allocKind| has the correct finalization kind for the class. 3531 gc::FinalizeKind finalizeKind = GetObjectFinalizeKind(clasp); 3532 MOZ_ASSERT_IF(finalizeKind == gc::FinalizeKind::None, 3533 !IsFinalizedKind(allocKind)); 3534 MOZ_ASSERT_IF(finalizeKind == gc::FinalizeKind::Background, 3535 IsBackgroundFinalized(allocKind)); 3536 MOZ_ASSERT_IF(finalizeKind == gc::FinalizeKind::Foreground, 3537 IsForegroundFinalized(allocKind)); 3538 } 3539 3540 // Classes with a finalizer must specify whether instances will be finalized 3541 // on the main thread or in the background, except proxies whose behaviour 3542 // depends on the target object. 3543 static const uint32_t FinalizeMask = 3544 JSCLASS_FOREGROUND_FINALIZE | JSCLASS_BACKGROUND_FINALIZE; 3545 uint32_t flags = clasp->flags; 3546 uint32_t finalizeFlags = flags & FinalizeMask; 3547 if (clasp->hasFinalize() && !clasp->isProxyObject()) { 3548 MOZ_ASSERT(finalizeFlags == JSCLASS_FOREGROUND_FINALIZE || 3549 finalizeFlags == JSCLASS_BACKGROUND_FINALIZE); 3550 } else { 3551 MOZ_ASSERT(finalizeFlags == 0); 3552 } 3553 3554 MOZ_ASSERT_IF(clasp->hasFinalize(), 3555 heap == gc::Heap::Tenured || 3556 CanNurseryAllocateFinalizedClass(clasp) || 3557 clasp->isProxyObject()); 3558 3559 MOZ_ASSERT(!shape->isDictionary()); 3560 3561 // If the class has the JSCLASS_DELAY_METADATA_BUILDER flag, the caller must 3562 // use AutoSetNewObjectMetadata. 3563 MOZ_ASSERT_IF(clasp->shouldDelayMetadataBuilder(), 3564 shape->realm()->hasActiveAutoSetNewObjectMetadata()); 3565 MOZ_ASSERT(!shape->realm()->hasObjectPendingMetadata()); 3566 3567 // Non-native classes manage their own data and slots, so numFixedSlots is 3568 // always 0. Note that proxy classes can have reserved slots but they're not 3569 // included in numFixedSlots. 3570 if (!clasp->isNativeObject()) { 3571 MOZ_ASSERT_IF(!clasp->isProxyObject(), JSCLASS_RESERVED_SLOTS(clasp) == 0); 3572 } 3573 } 3574 #endif