tor-browser

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

JSObject-inl.h (21654B)


      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_inl_h
      8 #define vm_JSObject_inl_h
      9 
     10 #include "vm/JSObject.h"
     11 
     12 #include "gc/Allocator.h"
     13 #include "gc/Zone.h"
     14 #include "js/Object.h"  // JS::GetBuiltinClass
     15 #include "vm/ArrayObject.h"
     16 #include "vm/BoundFunctionObject.h"
     17 #include "vm/EnvironmentObject.h"
     18 #include "vm/JSFunction.h"
     19 #include "vm/PropertyResult.h"
     20 #include "vm/TypedArrayObject.h"
     21 #include "gc/BufferAllocator-inl.h"
     22 #include "gc/GCContext-inl.h"
     23 #include "gc/ObjectKind-inl.h"
     24 #include "vm/ObjectOperations-inl.h"  // js::MaybeHasInterestingSymbolProperty
     25 
     26 namespace js {
     27 
     28 // Get the GC kind to use for scripted 'new', empty object literals ({}), and
     29 // the |Object| constructor.
     30 static inline gc::AllocKind NewObjectGCKind() { return gc::AllocKind::OBJECT4; }
     31 
     32 }  // namespace js
     33 
     34 MOZ_ALWAYS_INLINE uint32_t js::NativeObject::numDynamicSlots() const {
     35  uint32_t slots = getSlotsHeader()->capacity();
     36  MOZ_ASSERT(slots == calculateDynamicSlots());
     37  MOZ_ASSERT_IF(hasDynamicSlots() && !hasUniqueId(), slots != 0);
     38 
     39  return slots;
     40 }
     41 
     42 MOZ_ALWAYS_INLINE uint32_t js::NativeObject::calculateDynamicSlots() const {
     43  return calculateDynamicSlots(numFixedSlots(), slotSpan(), getClass());
     44 }
     45 
     46 /* static */ MOZ_ALWAYS_INLINE uint32_t js::NativeObject::calculateDynamicSlots(
     47    uint32_t nfixed, uint32_t span, const JSClass* clasp) {
     48  if (span <= nfixed) {
     49    return 0;
     50  }
     51 
     52  uint32_t ndynamic = span - nfixed;
     53 
     54  // Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
     55  // the dynamic slots need to get increased again. ArrayObjects ignore
     56  // this because slots are uncommon in that case.
     57  if (clasp != &ArrayObject::class_ && ndynamic <= SLOT_CAPACITY_MIN) {
     58 #ifdef DEBUG
     59    size_t count = SLOT_CAPACITY_MIN + ObjectSlots::VALUES_PER_HEADER;
     60    MOZ_ASSERT(count == gc::GetGoodPower2ElementCount(count, sizeof(Value)));
     61 #endif
     62    return SLOT_CAPACITY_MIN;
     63  }
     64 
     65  uint32_t count = gc::GetGoodPower2ElementCount(
     66      ndynamic + ObjectSlots::VALUES_PER_HEADER, sizeof(Value));
     67 
     68  uint32_t slots = count - ObjectSlots::VALUES_PER_HEADER;
     69  MOZ_ASSERT(slots >= ndynamic);
     70  return slots;
     71 }
     72 
     73 /* static */ MOZ_ALWAYS_INLINE uint32_t
     74 js::NativeObject::calculateDynamicSlots(SharedShape* shape) {
     75  return calculateDynamicSlots(shape->numFixedSlots(), shape->slotSpan(),
     76                               shape->getObjectClass());
     77 }
     78 
     79 inline void JSObject::finalize(JS::GCContext* gcx) {
     80 #ifdef DEBUG
     81  MOZ_ASSERT(isTenured());
     82  js::gc::AllocKind kind = asTenured().getAllocKind();
     83  MOZ_ASSERT(IsFinalizedKind(kind));
     84  MOZ_ASSERT_IF(IsForegroundFinalized(kind),
     85                js::CurrentThreadCanAccessZone(zoneFromAnyThread()));
     86 #endif
     87 
     88  const JSClass* clasp = shape()->getObjectClass();
     89  MOZ_ASSERT(clasp->hasFinalize());
     90  clasp->doFinalize(gcx, this);
     91 }
     92 
     93 inline bool JSObject::isQualifiedVarObj() const {
     94  if (is<js::DebugEnvironmentProxy>()) {
     95    return as<js::DebugEnvironmentProxy>().environment().isQualifiedVarObj();
     96  }
     97  bool rv = hasFlag(js::ObjectFlag::QualifiedVarObj);
     98  MOZ_ASSERT_IF(rv, is<js::GlobalObject>() || is<js::CallObject>() ||
     99                        is<js::VarEnvironmentObject>() ||
    100                        is<js::ModuleEnvironmentObject>() ||
    101                        is<js::NonSyntacticVariablesObject>() ||
    102                        (is<js::WithEnvironmentObject>() &&
    103                         !as<js::WithEnvironmentObject>().isSyntactic()));
    104  return rv;
    105 }
    106 
    107 inline bool JSObject::isUnqualifiedVarObj() const {
    108  if (is<js::DebugEnvironmentProxy>()) {
    109    return as<js::DebugEnvironmentProxy>().environment().isUnqualifiedVarObj();
    110  }
    111  return is<js::GlobalObject>() || is<js::NonSyntacticVariablesObject>();
    112 }
    113 
    114 inline bool JSObject::setQualifiedVarObj(
    115    JSContext* cx, JS::Handle<js::WithEnvironmentObject*> obj) {
    116  MOZ_ASSERT(!obj->isSyntactic());
    117  return setFlag(cx, obj, js::ObjectFlag::QualifiedVarObj);
    118 }
    119 
    120 namespace js {
    121 
    122 #ifdef DEBUG
    123 inline bool ClassCanHaveFixedData(const JSClass* clasp) {
    124  // Normally, the number of fixed slots given an object is the maximum
    125  // permitted for its size class. For array buffers and typed arrays we only
    126  // use enough to cover the class reserved slots, so that the remaining space
    127  // in the object's allocation is available for the buffer's data.
    128  return !clasp->isNativeObject() ||
    129         clasp == &js::FixedLengthArrayBufferObject::class_ ||
    130         clasp == &js::ResizableArrayBufferObject::class_ ||
    131         clasp == &js::ImmutableArrayBufferObject::class_ ||
    132         js::IsTypedArrayClass(clasp);
    133 }
    134 #endif
    135 
    136 class MOZ_RAII AutoSuppressAllocationMetadataBuilder {
    137  JS::Zone* zone;
    138  bool saved;
    139 
    140 public:
    141  explicit AutoSuppressAllocationMetadataBuilder(JSContext* cx)
    142      : zone(cx->zone()), saved(zone->suppressAllocationMetadataBuilder) {
    143    zone->suppressAllocationMetadataBuilder = true;
    144  }
    145 
    146  ~AutoSuppressAllocationMetadataBuilder() {
    147    zone->suppressAllocationMetadataBuilder = saved;
    148  }
    149 };
    150 
    151 // This function is meant to be called from allocation fast paths.
    152 //
    153 // If we do have an allocation metadata builder, it can cause a GC, so the
    154 // object must be rooted. The usual way to do this would be to make our callers
    155 // pass a HandleObject, but that would require them to pay the cost of rooting
    156 // the object unconditionally, even though collecting metadata is rare. Instead,
    157 // SetNewObjectMetadata's contract is that the caller must use the pointer
    158 // returned in place of the pointer passed. If a GC occurs, the returned pointer
    159 // may be the passed pointer, relocated by GC. If no GC could occur, it's just
    160 // passed through. We root nothing unless necessary.
    161 template <typename T>
    162 [[nodiscard]] static inline T* SetNewObjectMetadata(JSContext* cx, T* obj) {
    163  MOZ_ASSERT(cx->realm()->hasAllocationMetadataBuilder());
    164  MOZ_ASSERT(!cx->realm()->hasObjectPendingMetadata());
    165 
    166  // The metadata builder is invoked for each object created on the main thread,
    167  // except when it's suppressed or we're throwing over-recursion error.
    168  if (!cx->zone()->suppressAllocationMetadataBuilder &&
    169      !cx->isThrowingOverRecursed()) {
    170    // Don't collect metadata on objects that represent metadata, to avoid
    171    // recursion.
    172    AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
    173 
    174    Rooted<T*> rooted(cx, obj);
    175    cx->realm()->setNewObjectMetadata(cx, rooted);
    176    return rooted;
    177  }
    178 
    179  return obj;
    180 }
    181 
    182 }  // namespace js
    183 
    184 inline js::GlobalObject& JSObject::nonCCWGlobal() const {
    185  /*
    186   * The global is read-barriered so that it is kept live by access through
    187   * the Realm. When accessed through a JSObject, however, the global will be
    188   * already kept live by the black JSObject's group pointer, so does not
    189   * need to be read-barriered.
    190   */
    191  return *nonCCWRealm()->unsafeUnbarrieredMaybeGlobal();
    192 }
    193 
    194 inline bool JSObject::nonProxyIsExtensible() const {
    195  MOZ_ASSERT(!uninlinedIsProxyObject());
    196 
    197  // [[Extensible]] for ordinary non-proxy objects is an object flag.
    198  return !hasFlag(js::ObjectFlag::NotExtensible);
    199 }
    200 
    201 inline bool JSObject::hasInvalidatedTeleporting() const {
    202  return hasFlag(js::ObjectFlag::InvalidatedTeleporting);
    203 }
    204 
    205 inline bool JSObject::needsProxyGetSetResultValidation() const {
    206  return hasFlag(js::ObjectFlag::NeedsProxyGetSetResultValidation);
    207 }
    208 
    209 MOZ_ALWAYS_INLINE bool JSObject::maybeHasInterestingSymbolProperty() const {
    210  if (is<js::NativeObject>()) {
    211    return as<js::NativeObject>().hasInterestingSymbol();
    212  }
    213  return true;
    214 }
    215 
    216 inline bool JSObject::staticPrototypeIsImmutable() const {
    217  MOZ_ASSERT(hasStaticPrototype());
    218  return hasFlag(js::ObjectFlag::ImmutablePrototype);
    219 }
    220 
    221 namespace js {
    222 
    223 static MOZ_ALWAYS_INLINE bool IsFunctionObject(const js::Value& v) {
    224  return v.isObject() && v.toObject().is<JSFunction>();
    225 }
    226 
    227 static MOZ_ALWAYS_INLINE bool IsFunctionObject(const js::Value& v,
    228                                               JSFunction** fun) {
    229  if (v.isObject() && v.toObject().is<JSFunction>()) {
    230    *fun = &v.toObject().as<JSFunction>();
    231    return true;
    232  }
    233  return false;
    234 }
    235 
    236 static MOZ_ALWAYS_INLINE bool IsNativeFunction(const js::Value& v,
    237                                               JSNative native) {
    238  JSFunction* fun;
    239  return IsFunctionObject(v, &fun) && fun->maybeNative() == native;
    240 }
    241 
    242 static MOZ_ALWAYS_INLINE bool IsNativeFunction(const JSObject* obj,
    243                                               JSNative native) {
    244  return obj->is<JSFunction>() && obj->as<JSFunction>().maybeNative() == native;
    245 }
    246 
    247 // Return whether looking up a method on 'obj' definitely resolves to the
    248 // original specified native function. The method may conservatively return
    249 // 'false' in the case of proxies or other non-native objects.
    250 static MOZ_ALWAYS_INLINE bool HasNativeMethodPure(JSObject* obj,
    251                                                  PropertyName* name,
    252                                                  JSNative native,
    253                                                  JSContext* cx) {
    254  Value v;
    255  if (!GetPropertyPure(cx, obj, NameToId(name), &v)) {
    256    return false;
    257  }
    258 
    259  return IsNativeFunction(v, native);
    260 }
    261 
    262 // Return whether 'obj' definitely has no @@toPrimitive method.
    263 static MOZ_ALWAYS_INLINE bool HasNoToPrimitiveMethodPure(JSObject* obj,
    264                                                         JSContext* cx) {
    265  JS::Symbol* toPrimitive = cx->wellKnownSymbols().toPrimitive;
    266  JSObject* holder;
    267  if (!MaybeHasInterestingSymbolProperty(cx, obj, toPrimitive, &holder)) {
    268 #ifdef DEBUG
    269    NativeObject* pobj;
    270    PropertyResult prop;
    271    MOZ_ASSERT(LookupPropertyPure(cx, obj, PropertyKey::Symbol(toPrimitive),
    272                                  &pobj, &prop));
    273    MOZ_ASSERT(prop.isNotFound());
    274 #endif
    275    return true;
    276  }
    277 
    278  NativeObject* pobj;
    279  PropertyResult prop;
    280  if (!LookupPropertyPure(cx, holder, PropertyKey::Symbol(toPrimitive), &pobj,
    281                          &prop)) {
    282    return false;
    283  }
    284 
    285  return prop.isNotFound();
    286 }
    287 
    288 extern bool ToPropertyKeySlow(JSContext* cx, HandleValue argument,
    289                              MutableHandleId result);
    290 
    291 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
    292 MOZ_ALWAYS_INLINE bool ToPropertyKey(JSContext* cx, HandleValue argument,
    293                                     MutableHandleId result) {
    294  if (MOZ_LIKELY(argument.isPrimitive())) {
    295    return PrimitiveValueToId<CanGC>(cx, argument, result);
    296  }
    297 
    298  return ToPropertyKeySlow(cx, argument, result);
    299 }
    300 
    301 /*
    302 * Return true if this is a compiler-created internal function accessed by
    303 * its own object. Such a function object must not be accessible to script
    304 * or embedding code.
    305 */
    306 inline bool IsInternalFunctionObject(JSObject& funobj) {
    307  JSFunction& fun = funobj.as<JSFunction>();
    308  return fun.isInterpreted() && !fun.environment();
    309 }
    310 
    311 inline gc::Heap GetInitialHeap(NewObjectKind newKind, const JSClass* clasp,
    312                               gc::AllocSite* site = nullptr) {
    313  if (newKind != GenericObject) {
    314    return gc::Heap::Tenured;
    315  }
    316  if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp)) {
    317    return gc::Heap::Tenured;
    318  }
    319  if (site) {
    320    return site->initialHeap();
    321  }
    322  return gc::Heap::Default;
    323 }
    324 
    325 /*
    326 * Make an object with the specified prototype. If parent is null, it will
    327 * default to the prototype's global if the prototype is non-null.
    328 */
    329 NativeObject* NewObjectWithGivenTaggedProto(JSContext* cx, const JSClass* clasp,
    330                                            Handle<TaggedProto> proto,
    331                                            gc::AllocKind allocKind,
    332                                            NewObjectKind newKind,
    333                                            ObjectFlags objFlags);
    334 
    335 NativeObject* NewObjectWithGivenTaggedProtoAndAllocSite(
    336    JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto,
    337    gc::AllocKind allocKind, NewObjectKind newKind, ObjectFlags objFlags,
    338    gc::AllocSite* site);
    339 
    340 template <NewObjectKind NewKind>
    341 inline NativeObject* NewObjectWithGivenTaggedProto(JSContext* cx,
    342                                                   const JSClass* clasp,
    343                                                   Handle<TaggedProto> proto,
    344                                                   ObjectFlags objFlags) {
    345  gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
    346  return NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, NewKind,
    347                                       objFlags);
    348 }
    349 
    350 template <NewObjectKind NewKind>
    351 inline NativeObject* NewObjectWithGivenTaggedProtoAndAllocSite(
    352    JSContext* cx, const JSClass* clasp, Handle<TaggedProto> proto,
    353    ObjectFlags objFlags, gc::AllocSite* site) {
    354  gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
    355  return NewObjectWithGivenTaggedProtoAndAllocSite(cx, clasp, proto, allocKind,
    356                                                   NewKind, objFlags, site);
    357 }
    358 
    359 namespace detail {
    360 
    361 template <typename T, NewObjectKind NewKind>
    362 inline T* NewObjectWithGivenTaggedProtoForKind(JSContext* cx,
    363                                               Handle<TaggedProto> proto) {
    364  JSObject* obj = NewObjectWithGivenTaggedProto<NewKind>(cx, &T::class_, proto,
    365                                                         ObjectFlags());
    366  return obj ? &obj->as<T>() : nullptr;
    367 }
    368 
    369 }  // namespace detail
    370 
    371 template <typename T>
    372 inline T* NewObjectWithGivenTaggedProto(JSContext* cx,
    373                                        Handle<TaggedProto> proto) {
    374  return detail::NewObjectWithGivenTaggedProtoForKind<T, GenericObject>(cx,
    375                                                                        proto);
    376 }
    377 
    378 inline NativeObject* NewObjectWithGivenProto(JSContext* cx,
    379                                             const JSClass* clasp,
    380                                             HandleObject proto) {
    381  return NewObjectWithGivenTaggedProto<GenericObject>(
    382      cx, clasp, AsTaggedProto(proto), ObjectFlags());
    383 }
    384 
    385 inline NativeObject* NewObjectWithGivenProtoAndAllocSite(
    386    JSContext* cx, const JSClass* clasp, HandleObject proto,
    387    js::gc::AllocSite* site) {
    388  return NewObjectWithGivenTaggedProtoAndAllocSite<GenericObject>(
    389      cx, clasp, AsTaggedProto(proto), ObjectFlags(), site);
    390 }
    391 
    392 inline NativeObject* NewTenuredObjectWithGivenProto(
    393    JSContext* cx, const JSClass* clasp, HandleObject proto,
    394    ObjectFlags objFlags = ObjectFlags()) {
    395  return NewObjectWithGivenTaggedProto<TenuredObject>(
    396      cx, clasp, AsTaggedProto(proto), objFlags);
    397 }
    398 
    399 template <typename T>
    400 inline T* NewObjectWithGivenProto(JSContext* cx, HandleObject proto) {
    401  return detail::NewObjectWithGivenTaggedProtoForKind<T, GenericObject>(
    402      cx, AsTaggedProto(proto));
    403 }
    404 
    405 template <typename T>
    406 inline T* NewTenuredObjectWithGivenProto(JSContext* cx, HandleObject proto) {
    407  return detail::NewObjectWithGivenTaggedProtoForKind<T, TenuredObject>(
    408      cx, AsTaggedProto(proto));
    409 }
    410 
    411 template <typename T>
    412 inline T* NewObjectWithGivenProtoAndKinds(JSContext* cx, HandleObject proto,
    413                                          gc::AllocKind allocKind,
    414                                          NewObjectKind newKind) {
    415  JSObject* obj = NewObjectWithGivenTaggedProto(
    416      cx, &T::class_, AsTaggedProto(proto), allocKind, newKind, ObjectFlags());
    417  return obj ? &obj->as<T>() : nullptr;
    418 }
    419 
    420 // Make an object with the prototype set according to the cached prototype or
    421 // Object.prototype.
    422 NativeObject* NewObjectWithClassProto(JSContext* cx, const JSClass* clasp,
    423                                      HandleObject proto,
    424                                      gc::AllocKind allocKind,
    425                                      NewObjectKind newKind = GenericObject,
    426                                      ObjectFlags objFlags = ObjectFlags());
    427 
    428 inline NativeObject* NewObjectWithClassProto(
    429    JSContext* cx, const JSClass* clasp, HandleObject proto,
    430    NewObjectKind newKind = GenericObject,
    431    ObjectFlags objFlags = ObjectFlags()) {
    432  gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
    433  return NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind,
    434                                 objFlags);
    435 }
    436 
    437 template <class T>
    438 inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto) {
    439  JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, GenericObject);
    440  return obj ? &obj->as<T>() : nullptr;
    441 }
    442 
    443 template <class T>
    444 inline T* NewObjectWithClassProtoAndKind(JSContext* cx, HandleObject proto,
    445                                         NewObjectKind newKind,
    446                                         ObjectFlags objFlags = ObjectFlags()) {
    447  JSObject* obj =
    448      NewObjectWithClassProto(cx, &T::class_, proto, newKind, objFlags);
    449  return obj ? &obj->as<T>() : nullptr;
    450 }
    451 
    452 template <class T>
    453 inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto,
    454                                  gc::AllocKind allocKind,
    455                                  NewObjectKind newKind = GenericObject) {
    456  NativeObject* obj =
    457      NewObjectWithClassProto(cx, &T::class_, proto, allocKind, newKind);
    458  return obj ? &obj->as<T>() : nullptr;
    459 }
    460 
    461 /*
    462 * Create a native instance of the given class with parent and proto set
    463 * according to the context's active global.
    464 */
    465 inline NativeObject* NewBuiltinClassInstance(
    466    JSContext* cx, const JSClass* clasp, gc::AllocKind allocKind,
    467    NewObjectKind newKind = GenericObject) {
    468  return NewObjectWithClassProto(cx, clasp, nullptr, allocKind, newKind);
    469 }
    470 
    471 inline NativeObject* NewBuiltinClassInstance(
    472    JSContext* cx, const JSClass* clasp,
    473    NewObjectKind newKind = GenericObject) {
    474  gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
    475  return NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
    476 }
    477 
    478 template <typename T>
    479 inline T* NewBuiltinClassInstance(JSContext* cx) {
    480  JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, GenericObject);
    481  return obj ? &obj->as<T>() : nullptr;
    482 }
    483 
    484 template <typename T>
    485 inline T* NewTenuredBuiltinClassInstance(JSContext* cx) {
    486  JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, TenuredObject);
    487  return obj ? &obj->as<T>() : nullptr;
    488 }
    489 
    490 template <typename T>
    491 inline T* NewBuiltinClassInstanceWithKind(JSContext* cx,
    492                                          NewObjectKind newKind) {
    493  JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
    494  return obj ? &obj->as<T>() : nullptr;
    495 }
    496 
    497 template <typename T>
    498 inline T* NewBuiltinClassInstance(JSContext* cx, gc::AllocKind allocKind,
    499                                  NewObjectKind newKind = GenericObject) {
    500  JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind);
    501  return obj ? &obj->as<T>() : nullptr;
    502 }
    503 
    504 static constexpr gc::AllocKind GuessArrayGCKind(size_t numElements) {
    505  if (numElements) {
    506    return gc::GetGCArrayKind(numElements);
    507  }
    508  return gc::AllocKind::OBJECT8;
    509 }
    510 
    511 // Returns ESClass::Other if the value isn't an object, or if the object
    512 // isn't of one of the enumerated classes.  Otherwise returns the appropriate
    513 // class.
    514 inline bool GetClassOfValue(JSContext* cx, HandleValue v, ESClass* cls) {
    515  if (!v.isObject()) {
    516    *cls = ESClass::Other;
    517    return true;
    518  }
    519 
    520  RootedObject obj(cx, &v.toObject());
    521  return JS::GetBuiltinClass(cx, obj, cls);
    522 }
    523 
    524 extern NativeObject* InitClass(
    525    JSContext* cx, HandleObject obj, const JSClass* protoClass,
    526    HandleObject protoProto, const char* name, JSNative constructor,
    527    unsigned nargs, const JSPropertySpec* ps, const JSFunctionSpec* fs,
    528    const JSPropertySpec* static_ps, const JSFunctionSpec* static_fs,
    529    NativeObject** ctorp = nullptr);
    530 
    531 MOZ_ALWAYS_INLINE const char* GetObjectClassName(JSContext* cx,
    532                                                 HandleObject obj) {
    533  if (obj->is<ProxyObject>()) {
    534    return Proxy::className(cx, obj);
    535  }
    536 
    537  return obj->getClass()->name;
    538 }
    539 
    540 inline bool IsCallable(const Value& v) {
    541  return v.isObject() && v.toObject().isCallable();
    542 }
    543 
    544 // ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
    545 inline bool IsConstructor(const Value& v) {
    546  return v.isObject() && v.toObject().isConstructor();
    547 }
    548 
    549 static inline bool MaybePreserveDOMWrapper(JSContext* cx, HandleObject obj) {
    550  const JSClass* clasp = obj->getClass();
    551  // If this ever changes, we'll just need to reevaluate the check below
    552  MOZ_ASSERT_IF(clasp->preservesWrapper(), clasp->isDOMClass());
    553  if (!clasp->isDOMClass()) {
    554    return true;
    555  }
    556 
    557  if (!obj->zone()->preserveWrapper(obj.get())) {
    558    return cx->runtime()->preserveWrapperCallback(cx, obj);
    559  }
    560 
    561  return true;
    562 }
    563 
    564 } /* namespace js */
    565 
    566 MOZ_ALWAYS_INLINE bool JSObject::isCallable() const {
    567  if (is<JSFunction>()) {
    568    return true;
    569  }
    570  if (is<js::ProxyObject>()) {
    571    const js::ProxyObject& p = as<js::ProxyObject>();
    572    return p.handler()->isCallable(const_cast<JSObject*>(this));
    573  }
    574  return callHook() != nullptr;
    575 }
    576 
    577 MOZ_ALWAYS_INLINE bool JSObject::isConstructor() const {
    578  if (is<JSFunction>()) {
    579    const JSFunction& fun = as<JSFunction>();
    580    return fun.isConstructor();
    581  }
    582  if (is<js::BoundFunctionObject>()) {
    583    const js::BoundFunctionObject& bound = as<js::BoundFunctionObject>();
    584    return bound.isConstructor();
    585  }
    586  if (is<js::ProxyObject>()) {
    587    const js::ProxyObject& p = as<js::ProxyObject>();
    588    return p.handler()->isConstructor(const_cast<JSObject*>(this));
    589  }
    590  return constructHook() != nullptr;
    591 }
    592 
    593 MOZ_ALWAYS_INLINE JSNative JSObject::callHook() const {
    594  return getClass()->getCall();
    595 }
    596 
    597 MOZ_ALWAYS_INLINE JSNative JSObject::constructHook() const {
    598  return getClass()->getConstruct();
    599 }
    600 
    601 #endif /* vm_JSObject_inl_h */