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/. */
7 #ifndef vm_JSObject_inl_h
8 #define vm_JSObject_inl_h
10 #include "vm/JSObject.h"
12 #include "js/Object.h" // JS::GetBuiltinClass
13 #include "vm/ArrayObject.h"
14 #include "vm/BoundFunctionObject.h"
15 #include "vm/EnvironmentObject.h"
16 #include "vm/JSFunction.h"
17 #include "vm/Probes.h"
18 #include "vm/PropertyResult.h"
19 #include "vm/TypedArrayObject.h"
21 #ifdef ENABLE_RECORD_TUPLE
22 # include "vm/TupleType.h"
25 #include "gc/GCContext-inl.h"
26 #include "gc/ObjectKind-inl.h"
27 #include "vm/ObjectOperations-inl.h" // js::MaybeHasInterestingSymbolProperty
31 #ifdef ENABLE_RECORD_TUPLE
32 // Defined in vm/RecordTupleShared.{h,cpp}. We cannot include that file
33 // because it causes circular dependencies.
34 extern bool IsExtendedPrimitiveWrapper(const JSObject
& obj
);
37 // Get the GC kind to use for scripted 'new', empty object literals ({}), and
38 // the |Object| constructor.
39 static inline gc::AllocKind
NewObjectGCKind() { return gc::AllocKind::OBJECT4
; }
43 MOZ_ALWAYS_INLINE
uint32_t js::NativeObject::numDynamicSlots() const {
44 uint32_t slots
= getSlotsHeader()->capacity();
45 MOZ_ASSERT(slots
== calculateDynamicSlots());
46 MOZ_ASSERT_IF(hasDynamicSlots() && !hasUniqueId(), slots
!= 0);
51 MOZ_ALWAYS_INLINE
uint32_t js::NativeObject::calculateDynamicSlots() const {
52 return calculateDynamicSlots(numFixedSlots(), slotSpan(), getClass());
55 /* static */ MOZ_ALWAYS_INLINE
uint32_t js::NativeObject::calculateDynamicSlots(
56 uint32_t nfixed
, uint32_t span
, const JSClass
* clasp
) {
61 uint32_t ndynamic
= span
- nfixed
;
63 // Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
64 // the dynamic slots need to get increased again. ArrayObjects ignore
65 // this because slots are uncommon in that case.
66 if (clasp
!= &ArrayObject::class_
&& ndynamic
<= SLOT_CAPACITY_MIN
) {
67 return SLOT_CAPACITY_MIN
;
71 mozilla::RoundUpPow2(ndynamic
+ ObjectSlots::VALUES_PER_HEADER
);
73 uint32_t slots
= count
- ObjectSlots::VALUES_PER_HEADER
;
74 MOZ_ASSERT(slots
>= ndynamic
);
78 /* static */ MOZ_ALWAYS_INLINE
uint32_t
79 js::NativeObject::calculateDynamicSlots(SharedShape
* shape
) {
80 return calculateDynamicSlots(shape
->numFixedSlots(), shape
->slotSpan(),
81 shape
->getObjectClass());
84 inline void JSObject::finalize(JS::GCContext
* gcx
) {
85 js::probes::FinalizeObject(this);
88 MOZ_ASSERT(isTenured());
89 if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
90 /* Assert we're on the main thread. */
91 MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
95 js::Shape
* objShape
= shape();
97 const JSClass
* clasp
= objShape
->getObjectClass();
98 if (clasp
->hasFinalize()) {
99 clasp
->doFinalize(gcx
, this);
102 if (!objShape
->isNative()) {
106 js::NativeObject
* nobj
= &as
<js::NativeObject
>();
107 if (nobj
->hasDynamicSlots()) {
108 js::ObjectSlots
* slotsHeader
= nobj
->getSlotsHeader();
109 size_t size
= js::ObjectSlots::allocSize(slotsHeader
->capacity());
110 gcx
->free_(this, slotsHeader
, size
, js::MemoryUse::ObjectSlots
);
113 if (nobj
->hasDynamicElements()) {
114 js::ObjectElements
* elements
= nobj
->getElementsHeader();
115 size_t size
= elements
->numAllocatedElements() * sizeof(js::HeapSlot
);
116 gcx
->free_(this, nobj
->getUnshiftedElementsHeader(), size
,
117 js::MemoryUse::ObjectElements
);
121 inline bool JSObject::isQualifiedVarObj() const {
122 if (is
<js::DebugEnvironmentProxy
>()) {
123 return as
<js::DebugEnvironmentProxy
>().environment().isQualifiedVarObj();
125 bool rv
= hasFlag(js::ObjectFlag::QualifiedVarObj
);
126 MOZ_ASSERT_IF(rv
, is
<js::GlobalObject
>() || is
<js::CallObject
>() ||
127 is
<js::VarEnvironmentObject
>() ||
128 is
<js::ModuleEnvironmentObject
>() ||
129 is
<js::NonSyntacticVariablesObject
>() ||
130 (is
<js::WithEnvironmentObject
>() &&
131 !as
<js::WithEnvironmentObject
>().isSyntactic()));
135 inline bool JSObject::isUnqualifiedVarObj() const {
136 if (is
<js::DebugEnvironmentProxy
>()) {
137 return as
<js::DebugEnvironmentProxy
>().environment().isUnqualifiedVarObj();
139 return is
<js::GlobalObject
>() || is
<js::NonSyntacticVariablesObject
>();
142 inline bool JSObject::canHaveFixedElements() const {
143 return (is
<js::ArrayObject
>() || IF_RECORD_TUPLE(is
<js::TupleType
>(), false));
149 inline bool ClassCanHaveFixedData(const JSClass
* clasp
) {
150 // Normally, the number of fixed slots given an object is the maximum
151 // permitted for its size class. For array buffers and typed arrays we only
152 // use enough to cover the class reserved slots, so that the remaining space
153 // in the object's allocation is available for the buffer's data.
154 return !clasp
->isNativeObject() ||
155 clasp
== &js::FixedLengthArrayBufferObject::class_
||
156 clasp
== &js::ResizableArrayBufferObject::class_
||
157 js::IsTypedArrayClass(clasp
);
161 class MOZ_RAII AutoSuppressAllocationMetadataBuilder
{
166 explicit AutoSuppressAllocationMetadataBuilder(JSContext
* cx
)
167 : zone(cx
->zone()), saved(zone
->suppressAllocationMetadataBuilder
) {
168 zone
->suppressAllocationMetadataBuilder
= true;
171 ~AutoSuppressAllocationMetadataBuilder() {
172 zone
->suppressAllocationMetadataBuilder
= saved
;
176 // This function is meant to be called from allocation fast paths.
178 // If we do have an allocation metadata builder, it can cause a GC, so the
179 // object must be rooted. The usual way to do this would be to make our callers
180 // pass a HandleObject, but that would require them to pay the cost of rooting
181 // the object unconditionally, even though collecting metadata is rare. Instead,
182 // SetNewObjectMetadata's contract is that the caller must use the pointer
183 // returned in place of the pointer passed. If a GC occurs, the returned pointer
184 // may be the passed pointer, relocated by GC. If no GC could occur, it's just
185 // passed through. We root nothing unless necessary.
186 template <typename T
>
187 [[nodiscard
]] static inline T
* SetNewObjectMetadata(JSContext
* cx
, T
* obj
) {
188 MOZ_ASSERT(cx
->realm()->hasAllocationMetadataBuilder());
189 MOZ_ASSERT(!cx
->realm()->hasObjectPendingMetadata());
191 // The metadata builder is invoked for each object created on the main thread,
192 // except when it's suppressed.
193 if (!cx
->zone()->suppressAllocationMetadataBuilder
) {
194 // Don't collect metadata on objects that represent metadata, to avoid
196 AutoSuppressAllocationMetadataBuilder
suppressMetadata(cx
);
198 Rooted
<T
*> rooted(cx
, obj
);
199 cx
->realm()->setNewObjectMetadata(cx
, rooted
);
208 inline js::GlobalObject
& JSObject::nonCCWGlobal() const {
210 * The global is read-barriered so that it is kept live by access through
211 * the Realm. When accessed through a JSObject, however, the global will be
212 * already kept live by the black JSObject's group pointer, so does not
213 * need to be read-barriered.
215 return *nonCCWRealm()->unsafeUnbarrieredMaybeGlobal();
218 inline bool JSObject::nonProxyIsExtensible() const {
219 MOZ_ASSERT(!uninlinedIsProxyObject());
221 #ifdef ENABLE_RECORD_TUPLE
222 if (js::IsExtendedPrimitiveWrapper(*this)) {
226 // [[Extensible]] for ordinary non-proxy objects is an object flag.
227 return !hasFlag(js::ObjectFlag::NotExtensible
);
230 inline bool JSObject::hasInvalidatedTeleporting() const {
231 return hasFlag(js::ObjectFlag::InvalidatedTeleporting
);
234 inline bool JSObject::needsProxyGetSetResultValidation() const {
235 return hasFlag(js::ObjectFlag::NeedsProxyGetSetResultValidation
);
238 MOZ_ALWAYS_INLINE
bool JSObject::maybeHasInterestingSymbolProperty() const {
239 if (is
<js::NativeObject
>()) {
240 return as
<js::NativeObject
>().hasInterestingSymbol();
245 inline bool JSObject::staticPrototypeIsImmutable() const {
246 MOZ_ASSERT(hasStaticPrototype());
247 return hasFlag(js::ObjectFlag::ImmutablePrototype
);
252 static MOZ_ALWAYS_INLINE
bool IsFunctionObject(const js::Value
& v
) {
253 return v
.isObject() && v
.toObject().is
<JSFunction
>();
256 static MOZ_ALWAYS_INLINE
bool IsFunctionObject(const js::Value
& v
,
258 if (v
.isObject() && v
.toObject().is
<JSFunction
>()) {
259 *fun
= &v
.toObject().as
<JSFunction
>();
265 static MOZ_ALWAYS_INLINE
bool IsNativeFunction(const js::Value
& v
,
268 return IsFunctionObject(v
, &fun
) && fun
->maybeNative() == native
;
271 static MOZ_ALWAYS_INLINE
bool IsNativeFunction(const JSObject
* obj
,
273 return obj
->is
<JSFunction
>() && obj
->as
<JSFunction
>().maybeNative() == native
;
276 // Return whether looking up a method on 'obj' definitely resolves to the
277 // original specified native function. The method may conservatively return
278 // 'false' in the case of proxies or other non-native objects.
279 static MOZ_ALWAYS_INLINE
bool HasNativeMethodPure(JSObject
* obj
,
284 if (!GetPropertyPure(cx
, obj
, NameToId(name
), &v
)) {
288 return IsNativeFunction(v
, native
);
291 // Return whether 'obj' definitely has no @@toPrimitive method.
292 static MOZ_ALWAYS_INLINE
bool HasNoToPrimitiveMethodPure(JSObject
* obj
,
294 JS::Symbol
* toPrimitive
= cx
->wellKnownSymbols().toPrimitive
;
296 if (!MaybeHasInterestingSymbolProperty(cx
, obj
, toPrimitive
, &holder
)) {
300 MOZ_ASSERT(LookupPropertyPure(cx
, obj
, PropertyKey::Symbol(toPrimitive
),
302 MOZ_ASSERT(prop
.isNotFound());
309 if (!LookupPropertyPure(cx
, holder
, PropertyKey::Symbol(toPrimitive
), &pobj
,
314 return prop
.isNotFound();
317 extern bool ToPropertyKeySlow(JSContext
* cx
, HandleValue argument
,
318 MutableHandleId result
);
320 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
321 MOZ_ALWAYS_INLINE
bool ToPropertyKey(JSContext
* cx
, HandleValue argument
,
322 MutableHandleId result
) {
323 if (MOZ_LIKELY(argument
.isPrimitive())) {
324 return PrimitiveValueToId
<CanGC
>(cx
, argument
, result
);
327 return ToPropertyKeySlow(cx
, argument
, result
);
331 * Return true if this is a compiler-created internal function accessed by
332 * its own object. Such a function object must not be accessible to script
335 inline bool IsInternalFunctionObject(JSObject
& funobj
) {
336 JSFunction
& fun
= funobj
.as
<JSFunction
>();
337 return fun
.isInterpreted() && !fun
.environment();
340 inline gc::Heap
GetInitialHeap(NewObjectKind newKind
, const JSClass
* clasp
,
341 gc::AllocSite
* site
= nullptr) {
342 if (newKind
!= GenericObject
) {
343 return gc::Heap::Tenured
;
345 if (clasp
->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp
)) {
346 return gc::Heap::Tenured
;
349 return site
->initialHeap();
351 return gc::Heap::Default
;
355 * Make an object with the specified prototype. If parent is null, it will
356 * default to the prototype's global if the prototype is non-null.
358 NativeObject
* NewObjectWithGivenTaggedProto(JSContext
* cx
, const JSClass
* clasp
,
359 Handle
<TaggedProto
> proto
,
360 gc::AllocKind allocKind
,
361 NewObjectKind newKind
,
362 ObjectFlags objFlags
);
364 template <NewObjectKind NewKind
>
365 inline NativeObject
* NewObjectWithGivenTaggedProto(JSContext
* cx
,
366 const JSClass
* clasp
,
367 Handle
<TaggedProto
> proto
,
368 ObjectFlags objFlags
) {
369 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
370 return NewObjectWithGivenTaggedProto(cx
, clasp
, proto
, allocKind
, NewKind
,
376 template <typename T
, NewObjectKind NewKind
>
377 inline T
* NewObjectWithGivenTaggedProtoForKind(JSContext
* cx
,
378 Handle
<TaggedProto
> proto
) {
379 JSObject
* obj
= NewObjectWithGivenTaggedProto
<NewKind
>(cx
, &T::class_
, proto
,
381 return obj
? &obj
->as
<T
>() : nullptr;
384 } // namespace detail
386 template <typename T
>
387 inline T
* NewObjectWithGivenTaggedProto(JSContext
* cx
,
388 Handle
<TaggedProto
> proto
) {
389 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, GenericObject
>(cx
,
393 inline NativeObject
* NewObjectWithGivenProto(JSContext
* cx
,
394 const JSClass
* clasp
,
395 HandleObject proto
) {
396 return NewObjectWithGivenTaggedProto
<GenericObject
>(
397 cx
, clasp
, AsTaggedProto(proto
), ObjectFlags());
400 inline NativeObject
* NewTenuredObjectWithGivenProto(
401 JSContext
* cx
, const JSClass
* clasp
, HandleObject proto
,
402 ObjectFlags objFlags
= ObjectFlags()) {
403 return NewObjectWithGivenTaggedProto
<TenuredObject
>(
404 cx
, clasp
, AsTaggedProto(proto
), objFlags
);
407 template <typename T
>
408 inline T
* NewObjectWithGivenProto(JSContext
* cx
, HandleObject proto
) {
409 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, GenericObject
>(
410 cx
, AsTaggedProto(proto
));
413 template <typename T
>
414 inline T
* NewTenuredObjectWithGivenProto(JSContext
* cx
, HandleObject proto
) {
415 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, TenuredObject
>(
416 cx
, AsTaggedProto(proto
));
419 template <typename T
>
420 inline T
* NewObjectWithGivenProtoAndKinds(JSContext
* cx
, HandleObject proto
,
421 gc::AllocKind allocKind
,
422 NewObjectKind newKind
) {
423 JSObject
* obj
= NewObjectWithGivenTaggedProto(
424 cx
, &T::class_
, AsTaggedProto(proto
), allocKind
, newKind
);
425 return obj
? &obj
->as
<T
>() : nullptr;
428 // Make an object with the prototype set according to the cached prototype or
430 NativeObject
* NewObjectWithClassProto(JSContext
* cx
, const JSClass
* clasp
,
432 gc::AllocKind allocKind
,
433 NewObjectKind newKind
= GenericObject
,
434 ObjectFlags objFlags
= ObjectFlags());
436 inline NativeObject
* NewObjectWithClassProto(
437 JSContext
* cx
, const JSClass
* clasp
, HandleObject proto
,
438 NewObjectKind newKind
= GenericObject
,
439 ObjectFlags objFlags
= ObjectFlags()) {
440 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
441 return NewObjectWithClassProto(cx
, clasp
, proto
, allocKind
, newKind
,
446 inline T
* NewObjectWithClassProto(JSContext
* cx
, HandleObject proto
) {
447 JSObject
* obj
= NewObjectWithClassProto(cx
, &T::class_
, proto
, GenericObject
);
448 return obj
? &obj
->as
<T
>() : nullptr;
452 inline T
* NewObjectWithClassProtoAndKind(JSContext
* cx
, HandleObject proto
,
453 NewObjectKind newKind
,
454 ObjectFlags objFlags
= ObjectFlags()) {
456 NewObjectWithClassProto(cx
, &T::class_
, proto
, newKind
, objFlags
);
457 return obj
? &obj
->as
<T
>() : nullptr;
461 inline T
* NewObjectWithClassProto(JSContext
* cx
, HandleObject proto
,
462 gc::AllocKind allocKind
,
463 NewObjectKind newKind
= GenericObject
) {
465 NewObjectWithClassProto(cx
, &T::class_
, proto
, allocKind
, newKind
);
466 return obj
? &obj
->as
<T
>() : nullptr;
470 * Create a native instance of the given class with parent and proto set
471 * according to the context's active global.
473 inline NativeObject
* NewBuiltinClassInstance(
474 JSContext
* cx
, const JSClass
* clasp
, gc::AllocKind allocKind
,
475 NewObjectKind newKind
= GenericObject
) {
476 return NewObjectWithClassProto(cx
, clasp
, nullptr, allocKind
, newKind
);
479 inline NativeObject
* NewBuiltinClassInstance(
480 JSContext
* cx
, const JSClass
* clasp
,
481 NewObjectKind newKind
= GenericObject
) {
482 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
483 return NewBuiltinClassInstance(cx
, clasp
, allocKind
, newKind
);
486 template <typename T
>
487 inline T
* NewBuiltinClassInstance(JSContext
* cx
) {
488 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, GenericObject
);
489 return obj
? &obj
->as
<T
>() : nullptr;
492 template <typename T
>
493 inline T
* NewTenuredBuiltinClassInstance(JSContext
* cx
) {
494 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, TenuredObject
);
495 return obj
? &obj
->as
<T
>() : nullptr;
498 template <typename T
>
499 inline T
* NewBuiltinClassInstanceWithKind(JSContext
* cx
,
500 NewObjectKind newKind
) {
501 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, newKind
);
502 return obj
? &obj
->as
<T
>() : nullptr;
505 template <typename T
>
506 inline T
* NewBuiltinClassInstance(JSContext
* cx
, gc::AllocKind allocKind
,
507 NewObjectKind newKind
= GenericObject
) {
508 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, allocKind
, newKind
);
509 return obj
? &obj
->as
<T
>() : nullptr;
512 static inline gc::AllocKind
GuessArrayGCKind(size_t numElements
) {
514 return gc::GetGCArrayKind(numElements
);
516 return gc::AllocKind::OBJECT8
;
519 // Returns ESClass::Other if the value isn't an object, or if the object
520 // isn't of one of the enumerated classes. Otherwise returns the appropriate
522 inline bool GetClassOfValue(JSContext
* cx
, HandleValue v
, ESClass
* cls
) {
524 *cls
= ESClass::Other
;
528 RootedObject
obj(cx
, &v
.toObject());
529 return JS::GetBuiltinClass(cx
, obj
, cls
);
532 extern NativeObject
* InitClass(
533 JSContext
* cx
, HandleObject obj
, const JSClass
* protoClass
,
534 HandleObject protoProto
, const char* name
, JSNative constructor
,
535 unsigned nargs
, const JSPropertySpec
* ps
, const JSFunctionSpec
* fs
,
536 const JSPropertySpec
* static_ps
, const JSFunctionSpec
* static_fs
,
537 NativeObject
** ctorp
= nullptr);
539 MOZ_ALWAYS_INLINE
const char* GetObjectClassName(JSContext
* cx
,
541 if (obj
->is
<ProxyObject
>()) {
542 return Proxy::className(cx
, obj
);
545 return obj
->getClass()->name
;
548 inline bool IsCallable(const Value
& v
) {
549 return v
.isObject() && v
.toObject().isCallable();
552 // ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
553 inline bool IsConstructor(const Value
& v
) {
554 return v
.isObject() && v
.toObject().isConstructor();
557 static inline bool MaybePreserveDOMWrapper(JSContext
* cx
, HandleObject obj
) {
558 if (!obj
->getClass()->isDOMClass()) {
562 MOZ_ASSERT(cx
->runtime()->preserveWrapperCallback
);
563 return cx
->runtime()->preserveWrapperCallback(cx
, obj
);
568 MOZ_ALWAYS_INLINE
bool JSObject::isCallable() const {
569 if (is
<JSFunction
>()) {
572 if (is
<js::ProxyObject
>()) {
573 const js::ProxyObject
& p
= as
<js::ProxyObject
>();
574 return p
.handler()->isCallable(const_cast<JSObject
*>(this));
576 return callHook() != nullptr;
579 MOZ_ALWAYS_INLINE
bool JSObject::isConstructor() const {
580 if (is
<JSFunction
>()) {
581 const JSFunction
& fun
= as
<JSFunction
>();
582 return fun
.isConstructor();
584 if (is
<js::BoundFunctionObject
>()) {
585 const js::BoundFunctionObject
& bound
= as
<js::BoundFunctionObject
>();
586 return bound
.isConstructor();
588 if (is
<js::ProxyObject
>()) {
589 const js::ProxyObject
& p
= as
<js::ProxyObject
>();
590 return p
.handler()->isConstructor(const_cast<JSObject
*>(this));
592 return constructHook() != nullptr;
595 MOZ_ALWAYS_INLINE JSNative
JSObject::callHook() const {
596 return getClass()->getCall();
599 MOZ_ALWAYS_INLINE JSNative
JSObject::constructHook() const {
600 return getClass()->getConstruct();
603 #endif /* vm_JSObject_inl_h */