JSObject.h (40074B)
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 #ifndef vm_JSObject_h 8 #define vm_JSObject_h 9 10 #include "mozilla/MemoryReporting.h" 11 12 #include "jsfriendapi.h" 13 14 #include "js/friend/ErrorMessages.h" // JSErrNum 15 #include "js/GCVector.h" 16 #include "js/shadow/Zone.h" // JS::shadow::Zone 17 #include "js/Wrapper.h" 18 #include "vm/Shape.h" 19 20 namespace JS { 21 struct ClassInfo; 22 } // namespace JS 23 24 namespace js { 25 26 using PropertyDescriptorVector = JS::GCVector<JS::PropertyDescriptor>; 27 class GCMarker; 28 class JS_PUBLIC_API GenericPrinter; 29 class JSONPrinter; 30 class Nursery; 31 struct AutoEnterOOMUnsafeRegion; 32 33 namespace gc { 34 class RelocationOverlay; 35 } // namespace gc 36 37 /****************************************************************************/ 38 39 class GlobalObject; 40 class NativeObject; 41 class WithEnvironmentObject; 42 43 enum class IntegrityLevel { Sealed, Frozen }; 44 45 /* 46 * The NewObjectKind allows an allocation site to specify the lifetime 47 * requirements that must be fixed at allocation time. 48 */ 49 enum NewObjectKind { 50 /* This is the default. Most objects are generic. */ 51 GenericObject, 52 53 /* 54 * Objects which will not benefit from being allocated in the nursery 55 * (e.g. because they are known to have a long lifetime) may be allocated 56 * with this kind to place them immediately into the tenured generation. 57 */ 58 TenuredObject 59 }; 60 61 // Forward declarations, required for later friend declarations. 62 bool PreventExtensions(JSContext* cx, JS::HandleObject obj, 63 JS::ObjectOpResult& result); 64 bool SetImmutablePrototype(JSContext* cx, JS::HandleObject obj, 65 bool* succeeded); 66 67 } /* namespace js */ 68 69 /* 70 * [SMDOC] JSObject layout 71 * 72 * A JavaScript object. 73 * 74 * This is the base class for all objects exposed to JS script (as well as some 75 * objects that are only accessed indirectly). Subclasses add additional fields 76 * and execution semantics. The runtime class of an arbitrary JSObject is 77 * identified by JSObject::getClass(). 78 * 79 * All objects have a non-null Shape, stored in the cell header, which describes 80 * the current layout and set of property keys of the object. 81 * 82 * Each Shape has a pointer to a BaseShape. The BaseShape contains the object's 83 * prototype object, its class, and its realm. 84 * 85 * NOTE: Some operations can change the contents of an object (including class) 86 * in-place so avoid assuming an object with same pointer has same class 87 * as before. 88 * - JSObject::swap() 89 */ 90 class JSObject 91 : public js::gc::CellWithTenuredGCPointer<js::gc::Cell, js::Shape> { 92 public: 93 // The Shape is stored in the cell header. 94 js::Shape* shape() const { return headerPtr(); } 95 96 // Like shape(), but uses getAtomic to read the header word. 97 js::Shape* shapeMaybeForwarded() const { return headerPtrAtomic(); } 98 99 #ifndef JS_64BIT 100 // Ensure fixed slots have 8-byte alignment on 32-bit platforms. 101 uint32_t padding_; 102 #endif 103 104 private: 105 friend class js::GCMarker; 106 friend class js::GlobalObject; 107 friend class js::Nursery; 108 friend class js::gc::RelocationOverlay; 109 friend bool js::PreventExtensions(JSContext* cx, JS::HandleObject obj, 110 JS::ObjectOpResult& result); 111 friend bool js::SetImmutablePrototype(JSContext* cx, JS::HandleObject obj, 112 bool* succeeded); 113 114 public: 115 const JSClass* getClass() const { return shape()->getObjectClass(); } 116 bool hasClass(const JSClass* c) const { return getClass() == c; } 117 118 js::LookupPropertyOp getOpsLookupProperty() const { 119 return getClass()->getOpsLookupProperty(); 120 } 121 js::DefinePropertyOp getOpsDefineProperty() const { 122 return getClass()->getOpsDefineProperty(); 123 } 124 js::HasPropertyOp getOpsHasProperty() const { 125 return getClass()->getOpsHasProperty(); 126 } 127 js::GetPropertyOp getOpsGetProperty() const { 128 return getClass()->getOpsGetProperty(); 129 } 130 js::SetPropertyOp getOpsSetProperty() const { 131 return getClass()->getOpsSetProperty(); 132 } 133 js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor() const { 134 return getClass()->getOpsGetOwnPropertyDescriptor(); 135 } 136 js::DeletePropertyOp getOpsDeleteProperty() const { 137 return getClass()->getOpsDeleteProperty(); 138 } 139 js::GetElementsOp getOpsGetElements() const { 140 return getClass()->getOpsGetElements(); 141 } 142 JSFunToStringOp getOpsFunToString() const { 143 return getClass()->getOpsFunToString(); 144 } 145 146 JS::Compartment* compartment() const { return shape()->compartment(); } 147 JS::Compartment* maybeCompartment() const { return compartment(); } 148 149 void initShape(js::Shape* shape) { 150 // Note: use Cell::Zone() instead of zone() because zone() relies on the 151 // shape we still have to initialize. 152 MOZ_ASSERT(Cell::zone() == shape->zone()); 153 initHeaderPtr(shape); 154 } 155 void setShape(js::Shape* shape) { 156 MOZ_ASSERT(maybeCCWRealm() == shape->realm()); 157 setHeaderPtr(shape); 158 } 159 160 static bool setFlag(JSContext* cx, JS::HandleObject obj, js::ObjectFlag flag); 161 162 bool hasFlag(js::ObjectFlag flag) const { 163 return shape()->hasObjectFlag(flag); 164 } 165 166 bool hasAnyFlag(js::ObjectFlags flags) const { 167 return shape()->objectFlags().hasAnyFlag(flags); 168 } 169 170 // Change this object's shape for a prototype mutation. 171 // 172 // Note: the caller must ensure the object has a mutable proto, is extensible, 173 // etc. 174 static bool setProtoUnchecked(JSContext* cx, JS::HandleObject obj, 175 js::Handle<js::TaggedProto> proto); 176 177 // An object is marked IsUsedAsPrototype if it is (or was) another object's 178 // prototype. Optimization heuristics will make use of this flag. 179 // 180 // This flag is only relevant for static prototypes. Proxy traps can return 181 // objects without this flag set. 182 // 183 // NOTE: it's important to call setIsUsedAsPrototype *after* initializing the 184 // object's properties, because that avoids unnecessary shadowing checks and 185 // reshaping. 186 // 187 // See: ReshapeForProtoMutation, ReshapeForShadowedProp 188 bool isUsedAsPrototype() const { 189 return hasFlag(js::ObjectFlag::IsUsedAsPrototype); 190 } 191 static bool setIsUsedAsPrototype(JSContext* cx, JS::HandleObject obj) { 192 return setFlag(cx, obj, js::ObjectFlag::IsUsedAsPrototype); 193 } 194 195 bool useWatchtowerTestingLog() const { 196 return hasFlag(js::ObjectFlag::UseWatchtowerTestingLog); 197 } 198 static bool setUseWatchtowerTestingLog(JSContext* cx, JS::HandleObject obj) { 199 return setFlag(cx, obj, js::ObjectFlag::UseWatchtowerTestingLog); 200 } 201 202 bool isGenerationCountedGlobal() const { 203 return hasFlag(js::ObjectFlag::GenerationCountedGlobal); 204 } 205 206 bool hasRealmFuseProperty() const { 207 return hasFlag(js::ObjectFlag::HasRealmFuseProperty); 208 } 209 static bool setHasRealmFuseProperty(JSContext* cx, JS::HandleObject obj) { 210 return setFlag(cx, obj, js::ObjectFlag::HasRealmFuseProperty); 211 } 212 213 bool hasNonFunctionAccessor() const { 214 return hasFlag(js::ObjectFlag::HasNonFunctionAccessor); 215 } 216 static bool setHasNonFunctionAccessor(JSContext* cx, JS::HandleObject obj) { 217 return setFlag(cx, obj, js::ObjectFlag::HasNonFunctionAccessor); 218 } 219 220 bool hasObjectFuse() const { return hasFlag(js::ObjectFlag::HasObjectFuse); } 221 222 // A "qualified" varobj is the object on which "qualified" variable 223 // declarations (i.e., those defined with "var") are kept. 224 // 225 // Conceptually, when a var binding is defined, it is defined on the 226 // innermost qualified varobj on the environment chain. 227 // 228 // Function scopes (CallObjects) are qualified varobjs, and there can be 229 // no other qualified varobj that is more inner for var bindings in that 230 // function. As such, all references to local var bindings in a function 231 // may be statically bound to the function scope. This is subject to 232 // further optimization. Unaliased bindings inside functions reside 233 // entirely on the frame, not in CallObjects. 234 // 235 // Global scopes are also qualified varobjs. It is possible to statically 236 // know, for a given script, that are no more inner qualified varobjs, so 237 // free variable references can be statically bound to the global. 238 // 239 // Finally, there are non-syntactic qualified varobjs used by embedders 240 // (e.g., Gecko and XPConnect), as they often wish to run scripts under a 241 // scope that captures var bindings. 242 inline bool isQualifiedVarObj() const; 243 244 // Non-syntactic with-environment objects can be made qualified varobjs after 245 // construction. All other qualified varobjs are directly marked as such when 246 // allocating the object. 247 static inline bool setQualifiedVarObj( 248 JSContext* cx, JS::Handle<js::WithEnvironmentObject*> obj); 249 250 // An "unqualified" varobj is the object on which "unqualified" 251 // assignments (i.e., bareword assignments for which the LHS does not 252 // exist on the environment chain) are kept. 253 inline bool isUnqualifiedVarObj() const; 254 255 // Once the "invalidated teleporting" flag is set for an object, it is never 256 // cleared and it may cause the JITs to insert additional guards when 257 // accessing properties on this object. While the flag remains clear, the 258 // shape teleporting optimization can be used to avoid those extra checks. 259 // 260 // The flag is set on the object if either: 261 // 262 // * Its own proto was mutated or it was on the proto chain of an object that 263 // had its proto mutated. 264 // 265 // * It was on the proto chain of an object that started shadowing a property 266 // on this object. 267 // 268 // See: 269 // - ReshapeForProtoMutation 270 // - ReshapeForShadowedProp 271 // - ProtoChainSupportsTeleporting 272 inline bool hasInvalidatedTeleporting() const; 273 static bool setInvalidatedTeleporting(JSContext* cx, JS::HandleObject obj) { 274 MOZ_ASSERT(obj->isUsedAsPrototype()); 275 MOZ_ASSERT(obj->hasStaticPrototype(), 276 "teleporting as a concept is only applicable to static " 277 "(not dynamically-computed) prototypes"); 278 return setFlag(cx, obj, js::ObjectFlag::InvalidatedTeleporting); 279 } 280 281 [[nodiscard]] static bool reshapeForTeleporting(JSContext* cx, 282 JS::HandleObject obj); 283 284 /* 285 * Whether there may be "interesting symbol" properties on this object. An 286 * interesting symbol is a symbol for which symbol->isInterestingSymbol() 287 * returns true. 288 */ 289 MOZ_ALWAYS_INLINE bool maybeHasInterestingSymbolProperty() const; 290 291 inline bool needsProxyGetSetResultValidation() const; 292 293 /* GC support. */ 294 295 void traceChildren(JSTracer* trc); 296 297 void fixupAfterMovingGC() {} 298 299 static const JS::TraceKind TraceKind = JS::TraceKind::Object; 300 301 static constexpr size_t thingSize(js::gc::AllocKind kind); 302 303 MOZ_ALWAYS_INLINE JS::Zone* zone() const { 304 MOZ_ASSERT_IF(!isTenured(), nurseryZone() == shape()->zone()); 305 return shape()->zone(); 306 } 307 MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZone() const { 308 return JS::shadow::Zone::from(zone()); 309 } 310 MOZ_ALWAYS_INLINE JS::Zone* zoneFromAnyThread() const { 311 MOZ_ASSERT_IF(!isTenured(), 312 nurseryZoneFromAnyThread() == shape()->zoneFromAnyThread()); 313 return shape()->zoneFromAnyThread(); 314 } 315 MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZoneFromAnyThread() const { 316 return JS::shadow::Zone::from(zoneFromAnyThread()); 317 } 318 static MOZ_ALWAYS_INLINE void postWriteBarrier(void* cellp, JSObject* prev, 319 JSObject* next) { 320 js::gc::PostWriteBarrierImpl<JSObject>(cellp, prev, next); 321 } 322 323 js::gc::AllocKind allocKind() const; 324 325 /* Return the allocKind we would use if we were to tenure this object. */ 326 js::gc::AllocKind allocKindForTenure(const js::Nursery& nursery) const; 327 328 size_t tenuredSizeOfThis() const { 329 MOZ_ASSERT(isTenured()); 330 return js::gc::Arena::thingSize(asTenured().getAllocKind()); 331 } 332 333 void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, 334 JS::ClassInfo* info, 335 JS::RuntimeSizes* runtimeSizes); 336 337 // We can only use addSizeOfExcludingThis on tenured objects: it assumes it 338 // can apply mallocSizeOf to bits and pieces of the object, whereas objects 339 // in the nursery may have those bits and pieces allocated in the nursery 340 // along with them, and are not each their own malloc blocks. 341 size_t sizeOfIncludingThisInNursery(mozilla::MallocSizeOf mallocSizeOf) const; 342 343 #ifdef DEBUG 344 static void debugCheckNewObject(js::Shape* shape, js::gc::AllocKind allocKind, 345 js::gc::Heap heap); 346 #else 347 static void debugCheckNewObject(js::Shape* shape, js::gc::AllocKind allocKind, 348 js::gc::Heap heap) {} 349 #endif 350 351 /* 352 * We permit proxies to dynamically compute their prototype if desired. 353 * (Not all proxies will so desire: in particular, most DOM proxies can 354 * track their prototype with a single, nullable JSObject*.) If a proxy 355 * so desires, we store (JSObject*)0x1 in the proto field of the object's 356 * group. 357 * 358 * We offer three ways to get an object's prototype: 359 * 360 * 1. obj->staticPrototype() returns the prototype, but it asserts if obj 361 * is a proxy, and the proxy has opted to dynamically compute its 362 * prototype using a getPrototype() handler. 363 * 2. obj->taggedProto() returns a TaggedProto, which can be tested to 364 * check if the proto is an object, nullptr, or lazily computed. 365 * 3. js::GetPrototype(cx, obj, &proto) computes the proto of an object. 366 * If obj is a proxy with dynamically-computed prototype, this code may 367 * perform arbitrary behavior (allocation, GC, run JS) while computing 368 * the proto. 369 */ 370 371 js::TaggedProto taggedProto() const { return shape()->proto(); } 372 373 bool uninlinedIsProxyObject() const; 374 375 JSObject* staticPrototype() const { 376 MOZ_ASSERT(hasStaticPrototype()); 377 return taggedProto().toObjectOrNull(); 378 } 379 380 // Normal objects and a subset of proxies have an uninteresting, static 381 // (albeit perhaps mutable) [[Prototype]]. For such objects the 382 // [[Prototype]] is just a value returned when needed for accesses, or 383 // modified in response to requests. These objects store the 384 // [[Prototype]] directly within |obj->group()|. 385 bool hasStaticPrototype() const { return !hasDynamicPrototype(); } 386 387 // The remaining proxies have a [[Prototype]] requiring dynamic computation 388 // for every access, going through the proxy handler {get,set}Prototype and 389 // setImmutablePrototype methods. (Wrappers particularly use this to keep 390 // the wrapper/wrappee [[Prototype]]s consistent.) 391 bool hasDynamicPrototype() const { 392 bool dynamic = taggedProto().isDynamic(); 393 MOZ_ASSERT_IF(dynamic, uninlinedIsProxyObject()); 394 return dynamic; 395 } 396 397 // True iff this object's [[Prototype]] is immutable. Must be called only 398 // on objects with a static [[Prototype]]! 399 inline bool staticPrototypeIsImmutable() const; 400 401 /* 402 * Environment chains. 403 * 404 * The environment chain of an object is the link in the search path when 405 * a script does a name lookup on an environment object. For JS internal 406 * environment objects --- Call, LexicalEnvironment, and WithEnvironment 407 * --- the chain is stored in the first fixed slot of the object. For 408 * other environment objects, the chain goes directly to the global. 409 * 410 * In code which is not marked hasNonSyntacticScope, environment chains 411 * can contain only syntactic environment objects (see 412 * IsSyntacticEnvironment) with a global object at the root as the 413 * environment of the outermost non-function script. In 414 * hasNonSyntacticScope code, the environment of the outermost 415 * non-function script might not be a global object, and can have a mix of 416 * other objects above it before the global object is reached. 417 */ 418 419 /* 420 * Get the enclosing environment of an object. When called on a 421 * non-EnvironmentObject, this will just be the global (the name 422 * "enclosing environment" still applies in this situation because 423 * non-EnvironmentObjects can be on the environment chain). 424 */ 425 inline JSObject* enclosingEnvironment() const; 426 427 // Cross-compartment wrappers are not associated with a single realm/global, 428 // so these methods assert the object is not a CCW. 429 inline js::GlobalObject& nonCCWGlobal() const; 430 431 JS::Realm* nonCCWRealm() const { 432 MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(this)); 433 return shape()->realm(); 434 } 435 bool hasSameRealmAs(JSContext* cx) const; 436 437 // Returns the object's realm even if the object is a CCW (be careful, in 438 // this case the realm is not very meaningful because wrappers are shared by 439 // all realms in the compartment). 440 JS::Realm* maybeCCWRealm() const { return shape()->realm(); } 441 442 /* 443 * ES5 meta-object properties and operations. 444 */ 445 446 public: 447 // Indicates whether a non-proxy is extensible. Don't call on proxies! 448 // This method really shouldn't exist -- but there are a few internal 449 // places that want it (JITs and the like), and it'd be a pain to mark them 450 // all as friends. 451 inline bool nonProxyIsExtensible() const; 452 bool uninlinedNonProxyIsExtensible() const; 453 454 public: 455 /* 456 * Back to generic stuff. 457 */ 458 MOZ_ALWAYS_INLINE bool isCallable() const; 459 MOZ_ALWAYS_INLINE bool isConstructor() const; 460 MOZ_ALWAYS_INLINE JSNative callHook() const; 461 MOZ_ALWAYS_INLINE JSNative constructHook() const; 462 463 bool isBackgroundFinalized() const; 464 465 MOZ_ALWAYS_INLINE void finalize(JS::GCContext* gcx); 466 467 public: 468 static bool nonNativeSetProperty(JSContext* cx, js::HandleObject obj, 469 js::HandleId id, js::HandleValue v, 470 js::HandleValue receiver, 471 JS::ObjectOpResult& result); 472 static bool nonNativeSetElement(JSContext* cx, js::HandleObject obj, 473 uint32_t index, js::HandleValue v, 474 js::HandleValue receiver, 475 JS::ObjectOpResult& result); 476 477 static void swap(JSContext* cx, JS::HandleObject a, JS::HandleObject b, 478 js::AutoEnterOOMUnsafeRegion& oomUnsafe); 479 480 /* 481 * In addition to the generic object interface provided by JSObject, 482 * specific types of objects may provide additional operations. To access, 483 * these addition operations, callers should use the pattern: 484 * 485 * if (obj.is<XObject>()) { 486 * XObject& x = obj.as<XObject>(); 487 * x.foo(); 488 * } 489 * 490 * These XObject classes form a hierarchy. For example, for a cloned block 491 * object, the following predicates are true: is<ClonedBlockObject>, 492 * is<NestedScopeObject> and is<ScopeObject>. Each of these has a 493 * respective class that derives and adds operations. 494 * 495 * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file 496 * triplet (along with any class YObject that derives XObject). 497 * 498 * Note that X represents a low-level representation and does not query the 499 * [[Class]] property of object defined by the spec: use |JS::GetBuiltinClass| 500 * for this. 501 */ 502 503 template <class T> 504 inline bool is() const { 505 return getClass() == &T::class_; 506 } 507 508 template <class T> 509 T& as() { 510 MOZ_ASSERT(this->is<T>()); 511 return *static_cast<T*>(this); 512 } 513 514 template <class T> 515 const T& as() const { 516 MOZ_ASSERT(this->is<T>()); 517 return *static_cast<const T*>(this); 518 } 519 520 /* 521 * True if either this or CheckedUnwrap(this) is an object of class T. 522 * (Only two objects are checked, regardless of how many wrappers there 523 * are.) 524 * 525 * /!\ Note: This can be true at one point, but false later for the same 526 * object, thanks to js::NukeCrossCompartmentWrapper and friends. 527 */ 528 template <class T> 529 bool canUnwrapAs(); 530 531 /* 532 * Unwrap and downcast to class T. 533 * 534 * Precondition: `this->canUnwrapAs<T>()`. Note that it's not enough to 535 * have checked this at some point in the past; if there's any doubt as to 536 * whether js::Nuke* could have been called in the meantime, check again. 537 */ 538 template <class T> 539 T& unwrapAs(); 540 541 /* 542 * Tries to unwrap and downcast to class T. Returns nullptr if (and only if) a 543 * wrapper with a security policy is involved. Crashes in all builds if the 544 * (possibly unwrapped) object is not of class T (for example, because it's a 545 * dead wrapper). 546 */ 547 template <class T> 548 inline T* maybeUnwrapAs(); 549 550 /* 551 * Tries to unwrap and downcast to an object with class |clasp|. Returns 552 * nullptr if (and only if) a wrapper with a security policy is involved. 553 * Crashes in all builds if the (possibly unwrapped) object doesn't have class 554 * |clasp| (for example, because it's a dead wrapper). 555 */ 556 inline JSObject* maybeUnwrapAs(const JSClass* clasp); 557 558 /* 559 * Tries to unwrap and downcast to class T. Returns nullptr if a wrapper with 560 * a security policy is involved or if the object does not have class T. 561 */ 562 template <class T> 563 T* maybeUnwrapIf(); 564 565 #if defined(DEBUG) || defined(JS_JITSPEW) 566 void dump() const; 567 void dump(js::GenericPrinter& out) const; 568 void dump(js::JSONPrinter& json) const; 569 570 void dumpFields(js::JSONPrinter& json) const; 571 void dumpStringContent(js::GenericPrinter& out) const; 572 #endif 573 574 // Maximum size in bytes of a JSObject. 575 #ifdef JS_64BIT 576 static constexpr size_t MAX_BYTE_SIZE = 577 3 * sizeof(void*) + 16 * sizeof(JS::Value); 578 #else 579 static constexpr size_t MAX_BYTE_SIZE = 580 4 * sizeof(void*) + 16 * sizeof(JS::Value); 581 #endif 582 583 protected: 584 // JIT Accessors. 585 // 586 // To help avoid writing Spectre-unsafe code, we only allow MacroAssembler 587 // to call the method below. 588 friend class js::jit::MacroAssembler; 589 590 static constexpr size_t offsetOfShape() { return offsetOfHeaderPtr(); } 591 592 private: 593 JSObject(const JSObject& other) = delete; 594 void operator=(const JSObject& other) = delete; 595 596 protected: 597 // For the allocator only, to be used with placement new. 598 friend class js::gc::GCRuntime; 599 JSObject() = default; 600 }; 601 602 template <> 603 inline bool JSObject::is<JSObject>() const { 604 return true; 605 } 606 607 template <typename Wrapper> 608 template <typename U> 609 MOZ_ALWAYS_INLINE JS::Handle<U*> js::RootedOperations<JSObject*, Wrapper>::as() 610 const { 611 const Wrapper& self = *static_cast<const Wrapper*>(this); 612 MOZ_ASSERT(self->template is<U>()); 613 return Handle<U*>::fromMarkedLocation( 614 reinterpret_cast<U* const*>(self.address())); 615 } 616 617 template <typename Wrapper> 618 template <class U> 619 MOZ_ALWAYS_INLINE JS::Handle<U*> js::HandleOperations<JSObject*, Wrapper>::as() 620 const { 621 const JS::Handle<JSObject*>& self = 622 *static_cast<const JS::Handle<JSObject*>*>(this); 623 MOZ_ASSERT(self->template is<U>()); 624 return Handle<U*>::fromMarkedLocation( 625 reinterpret_cast<U* const*>(self.address())); 626 } 627 628 template <class T> 629 bool JSObject::canUnwrapAs() { 630 static_assert(!std::is_convertible_v<T*, js::Wrapper*>, 631 "T can't be a Wrapper type; this function discards wrappers"); 632 633 if (is<T>()) { 634 return true; 635 } 636 JSObject* obj = js::CheckedUnwrapStatic(this); 637 return obj && obj->is<T>(); 638 } 639 640 template <class T> 641 T& JSObject::unwrapAs() { 642 static_assert(!std::is_convertible_v<T*, js::Wrapper*>, 643 "T can't be a Wrapper type; this function discards wrappers"); 644 645 if (is<T>()) { 646 return as<T>(); 647 } 648 649 // Since the caller just called canUnwrapAs<T>(), which does a 650 // CheckedUnwrap, this does not need to repeat the security check. 651 JSObject* unwrapped = js::UncheckedUnwrap(this); 652 MOZ_ASSERT(js::CheckedUnwrapStatic(this) == unwrapped, 653 "check that the security check we skipped really is redundant"); 654 return unwrapped->as<T>(); 655 } 656 657 template <class T> 658 inline T* JSObject::maybeUnwrapAs() { 659 static_assert(!std::is_convertible_v<T*, js::Wrapper*>, 660 "T can't be a Wrapper type; this function discards wrappers"); 661 662 if (is<T>()) { 663 return &as<T>(); 664 } 665 666 JSObject* unwrapped = js::CheckedUnwrapStatic(this); 667 if (!unwrapped) { 668 return nullptr; 669 } 670 671 if (MOZ_LIKELY(unwrapped->is<T>())) { 672 return &unwrapped->as<T>(); 673 } 674 675 MOZ_CRASH("Invalid object. Dead wrapper?"); 676 } 677 678 inline JSObject* JSObject::maybeUnwrapAs(const JSClass* clasp) { 679 if (hasClass(clasp)) { 680 return this; 681 } 682 683 JSObject* unwrapped = js::CheckedUnwrapStatic(this); 684 if (!unwrapped) { 685 return nullptr; 686 } 687 688 if (MOZ_LIKELY(unwrapped->hasClass(clasp))) { 689 return unwrapped; 690 } 691 692 MOZ_CRASH("Invalid object. Dead wrapper?"); 693 } 694 695 template <class T> 696 T* JSObject::maybeUnwrapIf() { 697 static_assert(!std::is_convertible_v<T*, js::Wrapper*>, 698 "T can't be a Wrapper type; this function discards wrappers"); 699 700 if (is<T>()) { 701 return &as<T>(); 702 } 703 704 JSObject* unwrapped = js::CheckedUnwrapStatic(this); 705 return (unwrapped && unwrapped->is<T>()) ? &unwrapped->as<T>() : nullptr; 706 } 707 708 /* 709 * The only sensible way to compare JSObject with == is by identity. We use 710 * const& instead of * as a syntactic way to assert non-null. This leads to an 711 * abundance of address-of operators to identity. Hence this overload. 712 */ 713 static MOZ_ALWAYS_INLINE bool operator==(const JSObject& lhs, 714 const JSObject& rhs) { 715 return &lhs == &rhs; 716 } 717 718 static MOZ_ALWAYS_INLINE bool operator!=(const JSObject& lhs, 719 const JSObject& rhs) { 720 return &lhs != &rhs; 721 } 722 723 // Size of the various GC thing allocation sizes used for objects. 724 struct JSObject_Slots0 : JSObject { 725 void* data[2]; 726 }; 727 struct JSObject_Slots2 : JSObject { 728 void* data[2]; 729 js::Value fslots[2]; 730 }; 731 struct JSObject_Slots4 : JSObject { 732 void* data[2]; 733 js::Value fslots[4]; 734 }; 735 struct JSObject_Slots6 : JSObject { 736 void* data[2]; 737 js::Value fslots[6]; 738 }; 739 struct JSObject_Slots7 : JSObject { 740 // Only used for extended functions which are required to have exactly seven 741 // fixed slots due to JIT assumptions. 742 void* data[2]; 743 js::Value fslots[7]; 744 }; 745 struct JSObject_Slots8 : JSObject { 746 void* data[2]; 747 js::Value fslots[8]; 748 }; 749 struct JSObject_Slots12 : JSObject { 750 void* data[2]; 751 js::Value fslots[12]; 752 }; 753 struct JSObject_Slots16 : JSObject { 754 void* data[2]; 755 js::Value fslots[16]; 756 }; 757 758 /* static */ 759 constexpr size_t JSObject::thingSize(js::gc::AllocKind kind) { 760 MOZ_ASSERT(IsObjectAllocKind(kind)); 761 constexpr uint8_t objectSizes[] = { 762 #define EXPAND_OJBECT_SIZE(_1, _2, _3, sizedType, _4, _5, _6) sizeof(sizedType), 763 FOR_EACH_OBJECT_ALLOCKIND(EXPAND_OJBECT_SIZE)}; 764 return objectSizes[size_t(kind)]; 765 } 766 767 namespace js { 768 769 // Returns true if object may possibly use JSObject::swap. The JITs may better 770 // optimize objects that can never swap (and thus change their type). 771 // 772 // If ObjectMayBeSwapped is false, it is safe to guard on pointer identity to 773 // test immutable features of the object. For example, the target of a 774 // JSFunction will not change. Note: the object can still be moved by GC. 775 extern bool ObjectMayBeSwapped(const JSObject* obj); 776 777 extern bool DefineFunctions(JSContext* cx, HandleObject obj, 778 const JSFunctionSpec* fs); 779 780 /* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */ 781 extern bool ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp); 782 783 inline bool ToPrimitive(JSContext* cx, MutableHandleValue vp) { 784 if (vp.isPrimitive()) { 785 return true; 786 } 787 return ToPrimitiveSlow(cx, JSTYPE_UNDEFINED, vp); 788 } 789 790 inline bool ToPrimitive(JSContext* cx, JSType preferredType, 791 MutableHandleValue vp) { 792 if (vp.isPrimitive()) { 793 return true; 794 } 795 return ToPrimitiveSlow(cx, preferredType, vp); 796 } 797 798 /* 799 * toString support. (This isn't called GetClassName because there's a macro in 800 * <windows.h> with that name.) 801 */ 802 MOZ_ALWAYS_INLINE const char* GetObjectClassName(JSContext* cx, 803 HandleObject obj); 804 805 } /* namespace js */ 806 807 namespace js { 808 809 // ES6 9.1.15 GetPrototypeFromConstructor. 810 extern bool GetPrototypeFromConstructor(JSContext* cx, 811 js::HandleObject newTarget, 812 JSProtoKey intrinsicDefaultProto, 813 js::MutableHandleObject proto); 814 815 // https://tc39.github.io/ecma262/#sec-getprototypefromconstructor 816 // 817 // Determine which [[Prototype]] to use when creating a new object using a 818 // builtin constructor. 819 // 820 // This sets `proto` to `nullptr` to mean "the builtin prototype object for 821 // this type in the current realm", the common case. 822 // 823 // We could set it to `cx->global()->getOrCreatePrototype(protoKey)`, but 824 // nullptr gets a fast path in e.g. js::NewObjectWithClassProtoCommon. 825 // 826 // intrinsicDefaultProto can be JSProto_Null if there's no appropriate 827 // JSProtoKey enum; but we then select the wrong prototype object in a 828 // multi-realm corner case (see bug 1515167). 829 MOZ_ALWAYS_INLINE bool GetPrototypeFromBuiltinConstructor( 830 JSContext* cx, const CallArgs& args, JSProtoKey intrinsicDefaultProto, 831 js::MutableHandleObject proto) { 832 // We can skip the "prototype" lookup in the two common cases: 833 // 1. Builtin constructor called without `new`, as in `obj = Object();`. 834 // 2. Builtin constructor called with `new`, as in `obj = new Object();`. 835 // 836 // Cases that can't take the fast path include `new MySubclassOfObject()`, 837 // `new otherGlobal.Object()`, and `Reflect.construct(Object, [], Date)`. 838 if (!args.isConstructing() || 839 &args.newTarget().toObject() == &args.callee()) { 840 MOZ_ASSERT(args.callee().hasSameRealmAs(cx)); 841 proto.set(nullptr); 842 return true; 843 } 844 845 // We're calling this constructor from a derived class, retrieve the 846 // actual prototype from newTarget. 847 RootedObject newTarget(cx, &args.newTarget().toObject()); 848 return GetPrototypeFromConstructor(cx, newTarget, intrinsicDefaultProto, 849 proto); 850 } 851 852 /* ES6 draft rev 32 (2015 Feb 2) 6.2.4.5 ToPropertyDescriptor(Obj) */ 853 bool ToPropertyDescriptor(JSContext* cx, HandleValue descval, 854 bool checkAccessors, 855 MutableHandle<JS::PropertyDescriptor> desc); 856 857 /* 858 * Throw a TypeError if desc.getter() or setter() is not 859 * callable. This performs exactly the checks omitted by ToPropertyDescriptor 860 * when checkAccessors is false. 861 */ 862 Result<> CheckPropertyDescriptorAccessors(JSContext* cx, 863 Handle<JS::PropertyDescriptor> desc); 864 865 void CompletePropertyDescriptor(MutableHandle<JS::PropertyDescriptor> desc); 866 867 /* 868 * Read property descriptors from props, as for Object.defineProperties. See 869 * ES5 15.2.3.7 steps 3-5. 870 */ 871 extern bool ReadPropertyDescriptors( 872 JSContext* cx, HandleObject props, bool checkAccessors, 873 MutableHandleIdVector ids, MutableHandle<PropertyDescriptorVector> descs); 874 875 /* Read the name using a dynamic lookup on the envChain. */ 876 extern bool LookupName(JSContext* cx, Handle<PropertyName*> name, 877 HandleObject envChain, MutableHandleObject objp, 878 MutableHandleObject pobjp, PropertyResult* propp); 879 880 extern bool LookupNameNoGC(JSContext* cx, PropertyName* name, 881 JSObject* envChain, NativeObject** pobjp, 882 PropertyResult* propp); 883 884 /* 885 * Like LookupName except returns the global object if 'name' is not found in 886 * any preceding scope. 887 * 888 * Additionally, pobjp and propp are not needed by callers so they are not 889 * returned. 890 */ 891 extern JSObject* LookupNameWithGlobalDefault(JSContext* cx, 892 Handle<PropertyName*> name, 893 HandleObject envChain); 894 895 /* 896 * Like LookupName except returns the unqualified var object if 'name' is not 897 * found in any preceding scope. Normally the unqualified var object is the 898 * global. If the value for the name in the looked-up scope is an uninitialized 899 * lexical, a RuntimeLexicalErrorObject is returned. 900 * 901 * Additionally, pobjp is not needed by callers so it is not returned. 902 */ 903 extern JSObject* LookupNameUnqualified(JSContext* cx, 904 Handle<PropertyName*> name, 905 HandleObject envChain); 906 907 } // namespace js 908 909 namespace js { 910 911 /* 912 * Family of Pure property lookup functions. The bool return does NOT have the 913 * standard SpiderMonkey semantics. The return value means "can this operation 914 * be performed and produce a valid result without any side effects?". If any of 915 * these return true, then the outparam can be inspected to determine the 916 * result. 917 */ 918 919 bool LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id, 920 NativeObject** objp, PropertyResult* propp); 921 922 bool LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, 923 PropertyResult* propp); 924 925 bool GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp); 926 927 bool GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp, 928 bool* found); 929 930 bool GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp); 931 932 /* 933 * Like JS::FromPropertyDescriptor, but ignore desc.object() and always set vp 934 * to an object on success. 935 * 936 * Use JS::FromPropertyDescriptor for getOwnPropertyDescriptor, since 937 * desc.object() is used to indicate whether a result was found or not. Use 938 * this instead for defineProperty: it would be senseless to define a "missing" 939 * property. 940 */ 941 extern bool FromPropertyDescriptorToObject(JSContext* cx, 942 Handle<JS::PropertyDescriptor> desc, 943 MutableHandleValue vp); 944 945 // obj is a JSObject*, but we root it immediately up front. We do it 946 // that way because we need a Rooted temporary in this method anyway. 947 extern bool IsPrototypeOf(JSContext* cx, HandleObject protoObj, JSObject* obj, 948 bool* result); 949 950 /* Wrap boolean, number or string as Boolean, Number or String object. */ 951 extern JSObject* PrimitiveToObject(JSContext* cx, const Value& v); 952 extern JSProtoKey PrimitiveToProtoKey(JSContext* cx, const Value& v); 953 954 } /* namespace js */ 955 956 namespace js { 957 958 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, 959 int valIndex, HandleId key); 960 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, 961 int valIndex, 962 Handle<PropertyName*> key); 963 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val, 964 int valIndex, HandleValue keyValue); 965 966 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess(JSContext* cx, 967 HandleValue vp, 968 int vpIndex, 969 HandleId key) { 970 if (vp.isObject()) { 971 return &vp.toObject(); 972 } 973 return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key); 974 } 975 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess( 976 JSContext* cx, HandleValue vp, int vpIndex, Handle<PropertyName*> key) { 977 if (vp.isObject()) { 978 return &vp.toObject(); 979 } 980 return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key); 981 } 982 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess( 983 JSContext* cx, HandleValue vp, int vpIndex, HandleValue key) { 984 if (vp.isObject()) { 985 return &vp.toObject(); 986 } 987 return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key); 988 } 989 990 /* 991 * Report a TypeError: "so-and-so is not an object". 992 * Using NotNullObject is usually less code. 993 */ 994 extern void ReportNotObject(JSContext* cx, const Value& v); 995 996 inline JSObject* RequireObject(JSContext* cx, HandleValue v) { 997 if (v.isObject()) { 998 return &v.toObject(); 999 } 1000 ReportNotObject(cx, v); 1001 return nullptr; 1002 } 1003 1004 /* 1005 * Report a TypeError: "SOMETHING must be an object, got VALUE". 1006 * Using NotNullObject is usually less code. 1007 * 1008 * By default this function will attempt to report the expression which computed 1009 * the value which given as argument. This can be disabled by using 1010 * JSDVG_IGNORE_STACK. 1011 */ 1012 extern void ReportNotObject(JSContext* cx, JSErrNum err, int spindex, 1013 HandleValue v); 1014 1015 inline JSObject* RequireObject(JSContext* cx, JSErrNum err, int spindex, 1016 HandleValue v) { 1017 if (v.isObject()) { 1018 return &v.toObject(); 1019 } 1020 ReportNotObject(cx, err, spindex, v); 1021 return nullptr; 1022 } 1023 1024 extern void ReportNotObject(JSContext* cx, JSErrNum err, HandleValue v); 1025 1026 inline JSObject* RequireObject(JSContext* cx, JSErrNum err, HandleValue v) { 1027 if (v.isObject()) { 1028 return &v.toObject(); 1029 } 1030 ReportNotObject(cx, err, v); 1031 return nullptr; 1032 } 1033 1034 /* 1035 * Report a TypeError: "N-th argument of FUN must be an object, got VALUE". 1036 * Using NotNullObjectArg is usually less code. 1037 */ 1038 extern void ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun, 1039 HandleValue v); 1040 1041 inline JSObject* RequireObjectArg(JSContext* cx, const char* nth, 1042 const char* fun, HandleValue v) { 1043 if (v.isObject()) { 1044 return &v.toObject(); 1045 } 1046 ReportNotObjectArg(cx, nth, fun, v); 1047 return nullptr; 1048 } 1049 1050 extern bool GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, 1051 const char* method, 1052 MutableHandleObject objp); 1053 1054 /* Helper for throwing, always returns false. */ 1055 extern bool Throw(JSContext* cx, HandleId id, unsigned errorNumber, 1056 const char* details = nullptr); 1057 1058 /* 1059 * ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each 1060 * of obj's own properties' attributes appropriately: each property becomes 1061 * non-configurable, and if level == Frozen, data properties become 1062 * non-writable as well. 1063 */ 1064 extern bool SetIntegrityLevel(JSContext* cx, HandleObject obj, 1065 IntegrityLevel level); 1066 1067 inline bool FreezeObject(JSContext* cx, HandleObject obj) { 1068 return SetIntegrityLevel(cx, obj, IntegrityLevel::Frozen); 1069 } 1070 1071 /* 1072 * ES6 rev 29 (6 Dec 2014) 7.3.14. Code shared by Object.isSealed and 1073 * Object.isFrozen. 1074 */ 1075 extern bool TestIntegrityLevel(JSContext* cx, HandleObject obj, 1076 IntegrityLevel level, bool* resultp); 1077 1078 [[nodiscard]] extern JSObject* SpeciesConstructor( 1079 JSContext* cx, HandleObject obj, HandleObject defaultCtor, 1080 bool (*isDefaultSpecies)(JSContext*, JSFunction*)); 1081 1082 [[nodiscard]] extern JSObject* SpeciesConstructor( 1083 JSContext* cx, HandleObject obj, JSProtoKey ctorKey, 1084 bool (*isDefaultSpecies)(JSContext*, JSFunction*)); 1085 1086 extern bool GetObjectFromHostDefinedData(JSContext* cx, 1087 MutableHandleObject obj); 1088 1089 #ifdef DEBUG 1090 inline bool IsObjectValueInCompartment(const Value& v, JS::Compartment* comp) { 1091 if (!v.isObject()) { 1092 return true; 1093 } 1094 return v.toObject().compartment() == comp; 1095 } 1096 #endif 1097 1098 /* 1099 * A generic trace hook that calls the object's 'trace' method. 1100 * 1101 * If you are introducing a new JSObject subclass, MyObject, that needs a custom 1102 * JSClassOps::trace function, it's often helpful to write `trace` as a 1103 * non-static member function, since `this` will the correct type. In this case, 1104 * you can use `CallTraceMethod<MyObject>` as your JSClassOps::trace value. 1105 */ 1106 template <typename ObjectSubclass> 1107 void CallTraceMethod(JSTracer* trc, JSObject* obj) { 1108 obj->as<ObjectSubclass>().trace(trc); 1109 } 1110 1111 #ifdef JS_HAS_CTYPES 1112 1113 namespace ctypes { 1114 1115 extern size_t SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, 1116 JSObject* obj); 1117 1118 } // namespace ctypes 1119 1120 #endif 1121 1122 #ifdef DEBUG 1123 void AssertJSClassInvariants(const JSClass* clasp); 1124 #endif 1125 1126 } /* namespace js */ 1127 1128 #endif /* vm_JSObject_h */