tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */