Class.h (30119B)
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 /* JSClass definition and its component types, plus related interfaces. */ 8 9 #ifndef js_Class_h 10 #define js_Class_h 11 12 #include "mozilla/Attributes.h" 13 #include "mozilla/Maybe.h" 14 15 #include "jstypes.h" 16 17 #include "js/CallArgs.h" 18 #include "js/HeapAPI.h" 19 #include "js/Id.h" 20 #include "js/TypeDecls.h" 21 22 /* 23 * A JSClass acts as a vtable for JS objects that allows JSAPI clients to 24 * control various aspects of the behavior of an object like property lookup. 25 * It contains some engine-private extensions that allows more control over 26 * object behavior and, e.g., allows custom slow layout. 27 */ 28 29 struct JSAtomState; 30 struct JSFunctionSpec; 31 32 namespace js { 33 34 class PropertyResult; 35 36 // These are equal to js::FunctionClass / js::ExtendedFunctionClass. 37 extern JS_PUBLIC_DATA const JSClass* const FunctionClassPtr; 38 extern JS_PUBLIC_DATA const JSClass* const FunctionExtendedClassPtr; 39 40 } // namespace js 41 42 namespace JS { 43 44 /** 45 * Per ES6, the [[DefineOwnProperty]] internal method has three different 46 * possible outcomes: 47 * 48 * - It can throw an exception (which we indicate by returning false). 49 * 50 * - It can return true, indicating unvarnished success. 51 * 52 * - It can return false, indicating "strict failure". The property could 53 * not be defined. It's an error, but no exception was thrown. 54 * 55 * It's not just [[DefineOwnProperty]]: all the mutating internal methods have 56 * the same three outcomes. (The other affected internal methods are [[Set]], 57 * [[Delete]], [[SetPrototypeOf]], and [[PreventExtensions]].) 58 * 59 * If you think this design is awful, you're not alone. But as it's the 60 * standard, we must represent these boolean "success" values somehow. 61 * ObjectOpSuccess is the class for this. It's like a bool, but when it's false 62 * it also stores an error code. 63 * 64 * Typical usage: 65 * 66 * ObjectOpResult result; 67 * if (!DefineProperty(cx, obj, id, ..., result)) { 68 * return false; 69 * } 70 * if (!result) { 71 * return result.reportError(cx, obj, id); 72 * } 73 * 74 * Users don't have to call `result.report()`; another possible ending is: 75 * 76 * argv.rval().setBoolean(result.ok()); 77 * return true; 78 */ 79 class ObjectOpResult { 80 private: 81 /** 82 * code_ is either one of the special codes OkCode or Uninitialized, or an 83 * error code. For now the error codes are JS friend API and are defined in 84 * js/public/friend/ErrorNumbers.msg. 85 * 86 * code_ is uintptr_t (rather than uint32_t) for the convenience of the 87 * JITs, which would otherwise have to deal with either padding or stack 88 * alignment on 64-bit platforms. 89 */ 90 uintptr_t code_; 91 92 public: 93 enum SpecialCodes : uintptr_t { OkCode = 0, Uninitialized = uintptr_t(-1) }; 94 95 ObjectOpResult() : code_(Uninitialized) {} 96 97 /* Return true if succeed() was called. */ 98 bool ok() const { 99 MOZ_ASSERT(code_ != Uninitialized); 100 return code_ == OkCode; 101 } 102 103 explicit operator bool() const { return ok(); } 104 105 /* Set this ObjectOpResult to true and return true. */ 106 bool succeed() { 107 code_ = OkCode; 108 return true; 109 } 110 111 /* 112 * Set this ObjectOpResult to false with an error code. 113 * 114 * Always returns true, as a convenience. Typical usage will be: 115 * 116 * if (funny condition) { 117 * return result.fail(JSMSG_CANT_DO_THE_THINGS); 118 * } 119 * 120 * The true return value indicates that no exception is pending, and it 121 * would be OK to ignore the failure and continue. 122 */ 123 bool fail(uint32_t msg) { 124 MOZ_ASSERT(msg != OkCode); 125 code_ = msg; 126 return true; 127 } 128 129 JS_PUBLIC_API bool failCantRedefineProp(); 130 JS_PUBLIC_API bool failReadOnly(); 131 JS_PUBLIC_API bool failGetterOnly(); 132 JS_PUBLIC_API bool failCantDelete(); 133 134 JS_PUBLIC_API bool failCantSetInterposed(); 135 JS_PUBLIC_API bool failCantDefineWindowElement(); 136 JS_PUBLIC_API bool failCantDeleteWindowElement(); 137 JS_PUBLIC_API bool failCantDefineWindowNamedProperty(); 138 JS_PUBLIC_API bool failCantDeleteWindowNamedProperty(); 139 JS_PUBLIC_API bool failCantPreventExtensions(); 140 JS_PUBLIC_API bool failCantSetProto(); 141 JS_PUBLIC_API bool failNoNamedSetter(); 142 JS_PUBLIC_API bool failNoIndexedSetter(); 143 JS_PUBLIC_API bool failNotDataDescriptor(); 144 JS_PUBLIC_API bool failInvalidDescriptor(); 145 146 // Careful: This case has special handling in Object.defineProperty. 147 JS_PUBLIC_API bool failCantDefineWindowNonConfigurable(); 148 149 JS_PUBLIC_API bool failBadArrayLength(); 150 JS_PUBLIC_API bool failBadIndex(); 151 152 uint32_t failureCode() const { 153 MOZ_ASSERT(!ok()); 154 return uint32_t(code_); 155 } 156 157 /* 158 * Report an error if necessary; return true to proceed and 159 * false if an error was reported. 160 * 161 * The precise rules are like this: 162 * 163 * - If ok(), then we succeeded. Do nothing and return true. 164 * - Otherwise, if |strict| is true, throw a TypeError and return false. 165 * - Otherwise, do nothing and return true. 166 */ 167 bool checkStrictModeError(JSContext* cx, HandleObject obj, HandleId id, 168 bool strict) { 169 if (ok() || !strict) { 170 return true; 171 } 172 return reportError(cx, obj, id); 173 } 174 175 /* 176 * The same as checkStrictModeError(cx, id, strict), except the 177 * operation is not associated with a particular property id. This is 178 * used for [[PreventExtensions]] and [[SetPrototypeOf]]. failureCode() 179 * must not be an error that has "{0}" in the error message. 180 */ 181 bool checkStrictModeError(JSContext* cx, HandleObject obj, bool strict) { 182 if (ok() || !strict) { 183 return true; 184 } 185 return reportError(cx, obj); 186 } 187 188 /* Throw a TypeError. Call this only if !ok(). */ 189 bool reportError(JSContext* cx, HandleObject obj, HandleId id); 190 191 /* 192 * The same as reportError(cx, obj, id), except the operation is not 193 * associated with a particular property id. 194 */ 195 bool reportError(JSContext* cx, HandleObject obj); 196 197 // Convenience method. Return true if ok(); otherwise throw a TypeError 198 // and return false. 199 bool checkStrict(JSContext* cx, HandleObject obj, HandleId id) { 200 return checkStrictModeError(cx, obj, id, true); 201 } 202 203 // Convenience method. The same as checkStrict(cx, obj, id), except the 204 // operation is not associated with a particular property id. 205 bool checkStrict(JSContext* cx, HandleObject obj) { 206 return checkStrictModeError(cx, obj, true); 207 } 208 }; 209 210 } // namespace JS 211 212 // JSClass operation signatures. 213 214 /** Add a property named by id to obj. */ 215 typedef bool (*JSAddPropertyOp)(JSContext* cx, JS::HandleObject obj, 216 JS::HandleId id, JS::HandleValue v); 217 218 /** 219 * Delete a property named by id in obj. 220 * 221 * If an error occurred, return false as per normal JSAPI error practice. 222 * 223 * If no error occurred, but the deletion attempt wasn't allowed (perhaps 224 * because the property was non-configurable), call result.fail() and 225 * return true. This will cause |delete obj[id]| to evaluate to false in 226 * non-strict mode code, and to throw a TypeError in strict mode code. 227 * 228 * If no error occurred and the deletion wasn't disallowed (this is *not* the 229 * same as saying that a deletion actually occurred -- deleting a non-existent 230 * property, or an inherited property, is allowed -- it's just pointless), 231 * call result.succeed() and return true. 232 */ 233 typedef bool (*JSDeletePropertyOp)(JSContext* cx, JS::HandleObject obj, 234 JS::HandleId id, JS::ObjectOpResult& result); 235 236 /** 237 * The type of ObjectOps::enumerate. This callback overrides a portion of 238 * SpiderMonkey's default [[Enumerate]] internal method. When an ordinary object 239 * is enumerated, that object and each object on its prototype chain is tested 240 * for an enumerate op, and those ops are called in order. The properties each 241 * op adds to the 'properties' vector are added to the set of values the for-in 242 * loop will iterate over. All of this is nonstandard. 243 * 244 * An object is "enumerated" when it's the target of a for-in loop or 245 * JS_Enumerate(). The callback's job is to populate 'properties' with the 246 * object's property keys. If `enumerableOnly` is true, the callback should only 247 * add enumerable properties. 248 */ 249 typedef bool (*JSNewEnumerateOp)(JSContext* cx, JS::HandleObject obj, 250 JS::MutableHandleIdVector properties, 251 bool enumerableOnly); 252 253 /** 254 * The old-style JSClass.enumerate op should define all lazy properties not 255 * yet reflected in obj. 256 */ 257 typedef bool (*JSEnumerateOp)(JSContext* cx, JS::HandleObject obj); 258 259 /** 260 * The type of ObjectOps::funToString. This callback allows an object to 261 * provide a custom string to use when Function.prototype.toString is invoked on 262 * that object. A null return value means OOM. 263 */ 264 typedef JSString* (*JSFunToStringOp)(JSContext* cx, JS::HandleObject obj, 265 bool isToSource); 266 267 /** 268 * Resolve a lazy property named by id in obj by defining it directly in obj. 269 * Lazy properties are those reflected from some peer native property space 270 * (e.g., the DOM attributes for a given node reflected as obj) on demand. 271 * 272 * JS looks for a property in an object, and if not found, tries to resolve 273 * the given id. *resolvedp should be set to true iff the property was defined 274 * on |obj|. 275 * 276 * See JS::dbg::ShouldAvoidSideEffects in Debug.h if this function has any 277 * other side-effect than just resolving the property. 278 */ 279 typedef bool (*JSResolveOp)(JSContext* cx, JS::HandleObject obj, 280 JS::HandleId id, bool* resolvedp); 281 282 /** 283 * A class with a resolve hook can optionally have a mayResolve hook. This hook 284 * must have no side effects and must return true for a given id if the resolve 285 * hook may resolve this id. This is useful when we're doing a "pure" lookup: if 286 * mayResolve returns false, we know we don't have to call the effectful resolve 287 * hook. 288 * 289 * maybeObj, if non-null, is the object on which we're doing the lookup. This 290 * can be nullptr: during JIT compilation we sometimes know the Class but not 291 * the object. 292 */ 293 typedef bool (*JSMayResolveOp)(const JSAtomState& names, jsid id, 294 JSObject* maybeObj); 295 296 /** 297 * Finalize obj, which the garbage collector has determined to be unreachable 298 * from other live objects or from GC roots. Obviously, finalizers must never 299 * store a reference to obj. 300 */ 301 typedef void (*JSFinalizeOp)(JS::GCContext* gcx, JSObject* obj); 302 303 /** 304 * Function type for trace operation of the class called to enumerate all 305 * traceable things reachable from obj's private data structure. For each such 306 * thing, a trace implementation must call JS::TraceEdge on the thing's 307 * location. 308 * 309 * JSTraceOp implementation can assume that no other threads mutates object 310 * state. It must not change state of the object or corresponding native 311 * structures. The only exception for this rule is the case when the embedding 312 * needs a tight integration with GC. In that case the embedding can check if 313 * the traversal is a part of the marking phase through calling 314 * JS_IsGCMarkingTracer and apply a special code like emptying caches or 315 * marking its native structures. 316 */ 317 typedef void (*JSTraceOp)(JSTracer* trc, JSObject* obj); 318 319 typedef size_t (*JSObjectMovedOp)(JSObject* obj, JSObject* old); 320 321 namespace js { 322 323 /* Internal / friend API operation signatures. */ 324 325 typedef bool (*LookupPropertyOp)(JSContext* cx, JS::HandleObject obj, 326 JS::HandleId id, JS::MutableHandleObject objp, 327 PropertyResult* propp); 328 typedef bool (*DefinePropertyOp)(JSContext* cx, JS::HandleObject obj, 329 JS::HandleId id, 330 JS::Handle<JS::PropertyDescriptor> desc, 331 JS::ObjectOpResult& result); 332 typedef bool (*HasPropertyOp)(JSContext* cx, JS::HandleObject obj, 333 JS::HandleId id, bool* foundp); 334 typedef bool (*GetPropertyOp)(JSContext* cx, JS::HandleObject obj, 335 JS::HandleValue receiver, JS::HandleId id, 336 JS::MutableHandleValue vp); 337 typedef bool (*SetPropertyOp)(JSContext* cx, JS::HandleObject obj, 338 JS::HandleId id, JS::HandleValue v, 339 JS::HandleValue receiver, 340 JS::ObjectOpResult& result); 341 typedef bool (*GetOwnPropertyOp)( 342 JSContext* cx, JS::HandleObject obj, JS::HandleId id, 343 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc); 344 typedef bool (*DeletePropertyOp)(JSContext* cx, JS::HandleObject obj, 345 JS::HandleId id, JS::ObjectOpResult& result); 346 347 class JS_PUBLIC_API ElementAdder { 348 public: 349 enum GetBehavior { 350 // Check if the element exists before performing the Get and preserve 351 // holes. 352 CheckHasElemPreserveHoles, 353 354 // Perform a Get operation, like obj[index] in JS. 355 GetElement 356 }; 357 358 private: 359 // Only one of these is used. 360 JS::RootedObject resObj_; 361 JS::Value* vp_; 362 363 uint32_t index_; 364 #ifdef DEBUG 365 uint32_t length_; 366 #endif 367 GetBehavior getBehavior_; 368 369 public: 370 ElementAdder(JSContext* cx, JSObject* obj, uint32_t length, 371 GetBehavior behavior) 372 : resObj_(cx, obj), 373 vp_(nullptr), 374 index_(0), 375 #ifdef DEBUG 376 length_(length), 377 #endif 378 getBehavior_(behavior) { 379 } 380 ElementAdder(JSContext* cx, JS::Value* vp, uint32_t length, 381 GetBehavior behavior) 382 : resObj_(cx), 383 vp_(vp), 384 index_(0), 385 #ifdef DEBUG 386 length_(length), 387 #endif 388 getBehavior_(behavior) { 389 } 390 391 GetBehavior getBehavior() const { return getBehavior_; } 392 393 bool append(JSContext* cx, JS::HandleValue v); 394 void appendHole(); 395 }; 396 397 typedef bool (*GetElementsOp)(JSContext* cx, JS::HandleObject obj, 398 uint32_t begin, uint32_t end, 399 ElementAdder* adder); 400 401 /** Callback for the creation of constructor and prototype objects. */ 402 typedef JSObject* (*ClassObjectCreationOp)(JSContext* cx, JSProtoKey key); 403 404 /** 405 * Callback for custom post-processing after class initialization via 406 * ClassSpec. 407 */ 408 typedef bool (*FinishClassInitOp)(JSContext* cx, JS::HandleObject ctor, 409 JS::HandleObject proto); 410 411 const size_t JSCLASS_CACHED_PROTO_WIDTH = 7; 412 413 struct MOZ_STATIC_CLASS ClassSpec { 414 ClassObjectCreationOp createConstructor; 415 ClassObjectCreationOp createPrototype; 416 const JSFunctionSpec* constructorFunctions; 417 const JSPropertySpec* constructorProperties; 418 const JSFunctionSpec* prototypeFunctions; 419 const JSPropertySpec* prototypeProperties; 420 FinishClassInitOp finishInit; 421 uintptr_t flags; 422 423 static const size_t ProtoKeyWidth = JSCLASS_CACHED_PROTO_WIDTH; 424 425 static const uintptr_t ProtoKeyMask = (1 << ProtoKeyWidth) - 1; 426 static const uintptr_t DontDefineConstructor = 1 << ProtoKeyWidth; 427 428 bool defined() const { return !!createConstructor; } 429 430 // The ProtoKey this class inherits from. 431 JSProtoKey inheritanceProtoKey() const { 432 MOZ_ASSERT(defined()); 433 static_assert(JSProto_Null == 0, "zeroed key must be null"); 434 435 // Default: Inherit from Object. 436 if (!(flags & ProtoKeyMask)) { 437 return JSProto_Object; 438 } 439 440 return JSProtoKey(flags & ProtoKeyMask); 441 } 442 443 bool shouldDefineConstructor() const { 444 MOZ_ASSERT(defined()); 445 return !(flags & DontDefineConstructor); 446 } 447 }; 448 449 struct MOZ_STATIC_CLASS ClassExtension { 450 /** 451 * Optional hook called when an object is moved by generational or 452 * compacting GC. 453 * 454 * There may exist weak pointers to an object that are not traced through 455 * when the normal trace APIs are used, for example objects in the wrapper 456 * cache. This hook allows these pointers to be updated. 457 * 458 * Note that this hook can be called before JS_NewObject() returns if a GC 459 * is triggered during construction of the object. This can happen for 460 * global objects for example. 461 * 462 * The function should return the difference between nursery bytes used and 463 * tenured bytes used, which may be nonzero e.g. if some nursery-allocated 464 * data beyond the actual GC thing is moved into malloced memory. 465 * 466 * This is used to compute the nursery promotion rate. 467 */ 468 JSObjectMovedOp objectMovedOp; 469 }; 470 471 struct MOZ_STATIC_CLASS ObjectOps { 472 LookupPropertyOp lookupProperty; 473 DefinePropertyOp defineProperty; 474 HasPropertyOp hasProperty; 475 GetPropertyOp getProperty; 476 SetPropertyOp setProperty; 477 GetOwnPropertyOp getOwnPropertyDescriptor; 478 DeletePropertyOp deleteProperty; 479 GetElementsOp getElements; 480 JSFunToStringOp funToString; 481 }; 482 483 } // namespace js 484 485 static constexpr const js::ClassSpec* JS_NULL_CLASS_SPEC = nullptr; 486 static constexpr const js::ClassExtension* JS_NULL_CLASS_EXT = nullptr; 487 488 static constexpr const js::ObjectOps* JS_NULL_OBJECT_OPS = nullptr; 489 490 // Classes, objects, and properties. 491 492 // Must call a callback when the first property is added to an object of this 493 // class. If this is set, the object must store a pointer at 494 // JS_OBJECT_WRAPPER_SLOT to the C++ wrapper as a PrivateValue or 495 // UndefinedValue() if the object does not have a wrapper. 496 static const uint32_t JSCLASS_PRESERVES_WRAPPER = 1 << 0; 497 498 // Class's initialization code will call `SetNewObjectMetadata` itself. 499 static const uint32_t JSCLASS_DELAY_METADATA_BUILDER = 1 << 1; 500 501 // Class is an XPCWrappedNative. WeakMaps use this to override the wrapper 502 // disposal mechanism. 503 static const uint32_t JSCLASS_IS_WRAPPED_NATIVE = 1 << 2; 504 505 // First reserved slot is `PrivateValue(nsISupports*)` or `UndefinedValue`. 506 static constexpr uint32_t JSCLASS_SLOT0_IS_NSISUPPORTS = 1 << 3; 507 508 // Objects are DOM. 509 static const uint32_t JSCLASS_IS_DOMJSCLASS = 1 << 4; 510 511 // If wrapped by an xray wrapper, the builtin class's constructor won't be 512 // unwrapped and invoked. Instead, the constructor is resolved in the caller's 513 // compartment and invoked with a wrapped newTarget. The constructor has to 514 // detect and handle this situation. See PromiseConstructor for details. 515 static const uint32_t JSCLASS_HAS_XRAYED_CONSTRUCTOR = 1 << 5; 516 517 // Objects of this class act like the value undefined, in some contexts. 518 static const uint32_t JSCLASS_EMULATES_UNDEFINED = 1 << 6; 519 520 // Reserved for embeddings. 521 static const uint32_t JSCLASS_USERBIT1 = 1 << 7; 522 523 // To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or 524 // JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where n 525 // is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1. 526 527 // Room for 8 flags below ... 528 static const uintptr_t JSCLASS_RESERVED_SLOTS_SHIFT = 8; 529 // ... and 16 above this field. 530 static const uint32_t JSCLASS_RESERVED_SLOTS_WIDTH = 8; 531 532 static const uint32_t JSCLASS_RESERVED_SLOTS_MASK = 533 js::BitMask(JSCLASS_RESERVED_SLOTS_WIDTH); 534 535 static constexpr uint32_t JSCLASS_HAS_RESERVED_SLOTS(uint32_t n) { 536 return (n & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT; 537 } 538 539 static constexpr uint32_t JSCLASS_HIGH_FLAGS_SHIFT = 540 JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH; 541 542 static const uint32_t JSCLASS_INTERNAL_FLAG1 = 543 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 0); 544 static const uint32_t JSCLASS_IS_GLOBAL = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 1); 545 static const uint32_t JSCLASS_INTERNAL_FLAG2 = 546 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 2); 547 static const uint32_t JSCLASS_IS_PROXY = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 3); 548 static const uint32_t JSCLASS_SKIP_NURSERY_FINALIZE = 549 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 4); 550 551 // Reserved for embeddings. 552 static const uint32_t JSCLASS_USERBIT2 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 5); 553 static const uint32_t JSCLASS_USERBIT3 = 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 6); 554 555 static const uint32_t JSCLASS_BACKGROUND_FINALIZE = 556 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 7); 557 static const uint32_t JSCLASS_FOREGROUND_FINALIZE = 558 1 << (JSCLASS_HIGH_FLAGS_SHIFT + 8); 559 560 // Bits 25 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see 561 // below. 562 563 // ECMA-262 requires that most constructors used internally create objects 564 // with "the original Foo.prototype value" as their [[Prototype]] (__proto__) 565 // member initial value. The "original ... value" verbiage is there because 566 // in ECMA-262, global properties naming class objects are read/write and 567 // deleteable, for the most part. 568 // 569 // Implementing this efficiently requires that global objects have classes 570 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was 571 // previously allowed, but is now an ES5 violation and thus unsupported. 572 // 573 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at 574 // the beginning of every global object's slots for use by the 575 // application. 576 static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5; 577 static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT = 578 JSCLASS_GLOBAL_APPLICATION_SLOTS + 1; 579 580 static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) { 581 return JSCLASS_IS_GLOBAL | 582 JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + n); 583 } 584 585 static constexpr uint32_t JSCLASS_GLOBAL_FLAGS = 586 JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0); 587 588 // Fast access to the original value of each standard class's prototype. 589 static const uint32_t JSCLASS_CACHED_PROTO_SHIFT = JSCLASS_HIGH_FLAGS_SHIFT + 9; 590 static const uint32_t JSCLASS_CACHED_PROTO_MASK = 591 js::BitMask(js::JSCLASS_CACHED_PROTO_WIDTH); 592 593 static_assert(JSProto_LIMIT <= (JSCLASS_CACHED_PROTO_MASK + 1), 594 "JSProtoKey must not exceed the maximum cacheable proto-mask"); 595 596 static constexpr uint32_t JSCLASS_HAS_CACHED_PROTO(JSProtoKey key) { 597 return uint32_t(key) << JSCLASS_CACHED_PROTO_SHIFT; 598 } 599 600 // See JSCLASS_PRESERVES_WRAPPER. 601 static constexpr size_t JS_OBJECT_WRAPPER_SLOT = 0; 602 603 struct MOZ_STATIC_CLASS JSClassOps { 604 /* Function pointer members (may be null). */ 605 JSAddPropertyOp addProperty; 606 JSDeletePropertyOp delProperty; 607 JSEnumerateOp enumerate; 608 JSNewEnumerateOp newEnumerate; 609 JSResolveOp resolve; 610 JSMayResolveOp mayResolve; 611 JSFinalizeOp finalize; 612 JSNative call; 613 JSNative construct; 614 JSTraceOp trace; 615 }; 616 617 static constexpr const JSClassOps* JS_NULL_CLASS_OPS = nullptr; 618 619 // Note: This is a MOZ_STATIC_CLASS, as having a non-static JSClass 620 // can lead to bizarre behaviour, however the annotation 621 // is at the bottom to handle some incompatibility with GCC 622 // annotation processing. 623 struct alignas(js::gc::JSClassAlignBytes) JSClass { 624 const char* name; 625 uint32_t flags; 626 const JSClassOps* cOps; 627 628 const js::ClassSpec* spec; 629 const js::ClassExtension* ext; 630 const js::ObjectOps* oOps; 631 632 // Public accessors: 633 634 JSAddPropertyOp getAddProperty() const { 635 return cOps ? cOps->addProperty : nullptr; 636 } 637 JSDeletePropertyOp getDelProperty() const { 638 return cOps ? cOps->delProperty : nullptr; 639 } 640 JSEnumerateOp getEnumerate() const { 641 return cOps ? cOps->enumerate : nullptr; 642 } 643 JSNewEnumerateOp getNewEnumerate() const { 644 return cOps ? cOps->newEnumerate : nullptr; 645 } 646 JSResolveOp getResolve() const { return cOps ? cOps->resolve : nullptr; } 647 JSMayResolveOp getMayResolve() const { 648 return cOps ? cOps->mayResolve : nullptr; 649 } 650 JSNative getCall() const { return cOps ? cOps->call : nullptr; } 651 JSNative getConstruct() const { return cOps ? cOps->construct : nullptr; } 652 653 bool hasFinalize() const { return cOps && cOps->finalize; } 654 bool hasTrace() const { return cOps && cOps->trace; } 655 656 bool isTrace(JSTraceOp trace) const { return cOps && cOps->trace == trace; } 657 658 // The special treatment of |finalize| and |trace| is necessary because if we 659 // assign either of those hooks to a local variable and then call it -- as is 660 // done with the other hooks -- the GC hazard analysis gets confused. 661 void doFinalize(JS::GCContext* gcx, JSObject* obj) const { 662 MOZ_ASSERT(cOps && cOps->finalize); 663 cOps->finalize(gcx, obj); 664 } 665 void doTrace(JSTracer* trc, JSObject* obj) const { 666 MOZ_ASSERT(cOps && cOps->trace); 667 cOps->trace(trc, obj); 668 } 669 670 /* 671 * Objects of this class aren't native objects. They don't have Shapes that 672 * describe their properties and layout. Classes using this flag must 673 * provide their own property behavior, either by being proxy classes (do 674 * this) or by overriding all the ObjectOps except getElements 675 * (don't do this). 676 */ 677 static const uint32_t NON_NATIVE = JSCLASS_INTERNAL_FLAG2; 678 679 // A JSObject created from a JSClass extends from one of: 680 // - js::NativeObject 681 // - js::ProxyObject 682 // 683 // While it is possible to introduce new families of objects, it is strongly 684 // discouraged. The JITs would be entirely unable to optimize them and testing 685 // coverage is low. The existing NativeObject and ProxyObject are extremely 686 // flexible and are able to represent the entire Gecko embedding requirements. 687 // 688 // NOTE: Internal to SpiderMonkey, there is an experimental js::TypedObject 689 // object family for future WASM features. 690 bool isNativeObject() const { return !(flags & NON_NATIVE); } 691 bool isProxyObject() const { return flags & JSCLASS_IS_PROXY; } 692 693 bool emulatesUndefined() const { return flags & JSCLASS_EMULATES_UNDEFINED; } 694 695 bool isJSFunction() const { 696 return this == js::FunctionClassPtr || this == js::FunctionExtendedClassPtr; 697 } 698 699 bool nonProxyCallable() const { 700 MOZ_ASSERT(!isProxyObject()); 701 return isJSFunction() || getCall(); 702 } 703 704 bool isGlobal() const { return flags & JSCLASS_IS_GLOBAL; } 705 706 bool isDOMClass() const { return flags & JSCLASS_IS_DOMJSCLASS; } 707 708 bool shouldDelayMetadataBuilder() const { 709 return flags & JSCLASS_DELAY_METADATA_BUILDER; 710 } 711 712 bool isWrappedNative() const { return flags & JSCLASS_IS_WRAPPED_NATIVE; } 713 714 bool slot0IsISupports() const { return flags & JSCLASS_SLOT0_IS_NSISUPPORTS; } 715 716 bool preservesWrapper() const { return flags & JSCLASS_PRESERVES_WRAPPER; } 717 718 static size_t offsetOfFlags() { return offsetof(JSClass, flags); } 719 720 // Internal / friend API accessors: 721 722 bool specDefined() const { return spec ? spec->defined() : false; } 723 JSProtoKey specInheritanceProtoKey() const { 724 return spec ? spec->inheritanceProtoKey() : JSProto_Null; 725 } 726 bool specShouldDefineConstructor() const { 727 return spec ? spec->shouldDefineConstructor() : true; 728 } 729 js::ClassObjectCreationOp specCreateConstructorHook() const { 730 return spec ? spec->createConstructor : nullptr; 731 } 732 js::ClassObjectCreationOp specCreatePrototypeHook() const { 733 return spec ? spec->createPrototype : nullptr; 734 } 735 const JSFunctionSpec* specConstructorFunctions() const { 736 return spec ? spec->constructorFunctions : nullptr; 737 } 738 const JSPropertySpec* specConstructorProperties() const { 739 return spec ? spec->constructorProperties : nullptr; 740 } 741 const JSFunctionSpec* specPrototypeFunctions() const { 742 return spec ? spec->prototypeFunctions : nullptr; 743 } 744 const JSPropertySpec* specPrototypeProperties() const { 745 return spec ? spec->prototypeProperties : nullptr; 746 } 747 js::FinishClassInitOp specFinishInitHook() const { 748 return spec ? spec->finishInit : nullptr; 749 } 750 751 JSObjectMovedOp extObjectMovedOp() const { 752 return ext ? ext->objectMovedOp : nullptr; 753 } 754 755 js::LookupPropertyOp getOpsLookupProperty() const { 756 return oOps ? oOps->lookupProperty : nullptr; 757 } 758 js::DefinePropertyOp getOpsDefineProperty() const { 759 return oOps ? oOps->defineProperty : nullptr; 760 } 761 js::HasPropertyOp getOpsHasProperty() const { 762 return oOps ? oOps->hasProperty : nullptr; 763 } 764 js::GetPropertyOp getOpsGetProperty() const { 765 return oOps ? oOps->getProperty : nullptr; 766 } 767 js::SetPropertyOp getOpsSetProperty() const { 768 return oOps ? oOps->setProperty : nullptr; 769 } 770 js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor() const { 771 return oOps ? oOps->getOwnPropertyDescriptor : nullptr; 772 } 773 js::DeletePropertyOp getOpsDeleteProperty() const { 774 return oOps ? oOps->deleteProperty : nullptr; 775 } 776 js::GetElementsOp getOpsGetElements() const { 777 return oOps ? oOps->getElements : nullptr; 778 } 779 JSFunToStringOp getOpsFunToString() const { 780 return oOps ? oOps->funToString : nullptr; 781 } 782 } MOZ_STATIC_CLASS; 783 784 static constexpr uint32_t JSCLASS_RESERVED_SLOTS(const JSClass* clasp) { 785 return (clasp->flags >> JSCLASS_RESERVED_SLOTS_SHIFT) & 786 JSCLASS_RESERVED_SLOTS_MASK; 787 } 788 789 static constexpr bool JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(const JSClass* clasp) { 790 return (clasp->flags & JSCLASS_IS_GLOBAL) && 791 JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT; 792 } 793 794 static constexpr JSProtoKey JSCLASS_CACHED_PROTO_KEY(const JSClass* clasp) { 795 return JSProtoKey((clasp->flags >> JSCLASS_CACHED_PROTO_SHIFT) & 796 JSCLASS_CACHED_PROTO_MASK); 797 } 798 799 namespace js { 800 801 /** 802 * Enumeration describing possible values of the [[Class]] internal property 803 * value of objects. 804 */ 805 enum class ESClass { 806 Object, 807 Array, 808 Number, 809 String, 810 Boolean, 811 RegExp, 812 ArrayBuffer, 813 SharedArrayBuffer, 814 Date, 815 Set, 816 Map, 817 Promise, 818 MapIterator, 819 SetIterator, 820 Arguments, 821 Error, 822 BigInt, 823 Function, // Note: Only JSFunction objects. 824 825 /** None of the above. */ 826 Other 827 }; 828 829 /* Fills |vp| with the unboxed value for boxed types, or undefined otherwise. */ 830 bool Unbox(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp); 831 832 // Classes with JSCLASS_SKIP_NURSERY_FINALIZE or Wrapper classes with 833 // CROSS_COMPARTMENT flags will not have their finalizer called if they are 834 // nursery allocated and not promoted to the tenured heap. The finalizers for 835 // these classes must do nothing except free data which was allocated via 836 // Nursery::allocateBuffer. 837 inline bool CanNurseryAllocateFinalizedClass(const JSClass* const clasp) { 838 MOZ_ASSERT(clasp->hasFinalize()); 839 return clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE; 840 } 841 842 #ifdef DEBUG 843 JS_PUBLIC_API bool HasObjectMovedOp(JSObject* obj); 844 #endif 845 846 } /* namespace js */ 847 848 #endif /* js_Class_h */