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 or we're throwing over-recursion error.
193 if (!cx
->zone()->suppressAllocationMetadataBuilder
&&
194 !cx
->isThrowingOverRecursed()) {
195 // Don't collect metadata on objects that represent metadata, to avoid
197 AutoSuppressAllocationMetadataBuilder
suppressMetadata(cx
);
199 Rooted
<T
*> rooted(cx
, obj
);
200 cx
->realm()->setNewObjectMetadata(cx
, rooted
);
209 inline js::GlobalObject
& JSObject::nonCCWGlobal() const {
211 * The global is read-barriered so that it is kept live by access through
212 * the Realm. When accessed through a JSObject, however, the global will be
213 * already kept live by the black JSObject's group pointer, so does not
214 * need to be read-barriered.
216 return *nonCCWRealm()->unsafeUnbarrieredMaybeGlobal();
219 inline bool JSObject::nonProxyIsExtensible() const {
220 MOZ_ASSERT(!uninlinedIsProxyObject());
222 #ifdef ENABLE_RECORD_TUPLE
223 if (js::IsExtendedPrimitiveWrapper(*this)) {
227 // [[Extensible]] for ordinary non-proxy objects is an object flag.
228 return !hasFlag(js::ObjectFlag::NotExtensible
);
231 inline bool JSObject::hasInvalidatedTeleporting() const {
232 return hasFlag(js::ObjectFlag::InvalidatedTeleporting
);
235 inline bool JSObject::needsProxyGetSetResultValidation() const {
236 return hasFlag(js::ObjectFlag::NeedsProxyGetSetResultValidation
);
239 MOZ_ALWAYS_INLINE
bool JSObject::maybeHasInterestingSymbolProperty() const {
240 if (is
<js::NativeObject
>()) {
241 return as
<js::NativeObject
>().hasInterestingSymbol();
246 inline bool JSObject::staticPrototypeIsImmutable() const {
247 MOZ_ASSERT(hasStaticPrototype());
248 return hasFlag(js::ObjectFlag::ImmutablePrototype
);
253 static MOZ_ALWAYS_INLINE
bool IsFunctionObject(const js::Value
& v
) {
254 return v
.isObject() && v
.toObject().is
<JSFunction
>();
257 static MOZ_ALWAYS_INLINE
bool IsFunctionObject(const js::Value
& v
,
259 if (v
.isObject() && v
.toObject().is
<JSFunction
>()) {
260 *fun
= &v
.toObject().as
<JSFunction
>();
266 static MOZ_ALWAYS_INLINE
bool IsNativeFunction(const js::Value
& v
,
269 return IsFunctionObject(v
, &fun
) && fun
->maybeNative() == native
;
272 static MOZ_ALWAYS_INLINE
bool IsNativeFunction(const JSObject
* obj
,
274 return obj
->is
<JSFunction
>() && obj
->as
<JSFunction
>().maybeNative() == native
;
277 // Return whether looking up a method on 'obj' definitely resolves to the
278 // original specified native function. The method may conservatively return
279 // 'false' in the case of proxies or other non-native objects.
280 static MOZ_ALWAYS_INLINE
bool HasNativeMethodPure(JSObject
* obj
,
285 if (!GetPropertyPure(cx
, obj
, NameToId(name
), &v
)) {
289 return IsNativeFunction(v
, native
);
292 // Return whether 'obj' definitely has no @@toPrimitive method.
293 static MOZ_ALWAYS_INLINE
bool HasNoToPrimitiveMethodPure(JSObject
* obj
,
295 JS::Symbol
* toPrimitive
= cx
->wellKnownSymbols().toPrimitive
;
297 if (!MaybeHasInterestingSymbolProperty(cx
, obj
, toPrimitive
, &holder
)) {
301 MOZ_ASSERT(LookupPropertyPure(cx
, obj
, PropertyKey::Symbol(toPrimitive
),
303 MOZ_ASSERT(prop
.isNotFound());
310 if (!LookupPropertyPure(cx
, holder
, PropertyKey::Symbol(toPrimitive
), &pobj
,
315 return prop
.isNotFound();
318 extern bool ToPropertyKeySlow(JSContext
* cx
, HandleValue argument
,
319 MutableHandleId result
);
321 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
322 MOZ_ALWAYS_INLINE
bool ToPropertyKey(JSContext
* cx
, HandleValue argument
,
323 MutableHandleId result
) {
324 if (MOZ_LIKELY(argument
.isPrimitive())) {
325 return PrimitiveValueToId
<CanGC
>(cx
, argument
, result
);
328 return ToPropertyKeySlow(cx
, argument
, result
);
332 * Return true if this is a compiler-created internal function accessed by
333 * its own object. Such a function object must not be accessible to script
336 inline bool IsInternalFunctionObject(JSObject
& funobj
) {
337 JSFunction
& fun
= funobj
.as
<JSFunction
>();
338 return fun
.isInterpreted() && !fun
.environment();
341 inline gc::Heap
GetInitialHeap(NewObjectKind newKind
, const JSClass
* clasp
,
342 gc::AllocSite
* site
= nullptr) {
343 if (newKind
!= GenericObject
) {
344 return gc::Heap::Tenured
;
346 if (clasp
->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp
)) {
347 return gc::Heap::Tenured
;
350 return site
->initialHeap();
352 return gc::Heap::Default
;
356 * Make an object with the specified prototype. If parent is null, it will
357 * default to the prototype's global if the prototype is non-null.
359 NativeObject
* NewObjectWithGivenTaggedProto(JSContext
* cx
, const JSClass
* clasp
,
360 Handle
<TaggedProto
> proto
,
361 gc::AllocKind allocKind
,
362 NewObjectKind newKind
,
363 ObjectFlags objFlags
);
365 template <NewObjectKind NewKind
>
366 inline NativeObject
* NewObjectWithGivenTaggedProto(JSContext
* cx
,
367 const JSClass
* clasp
,
368 Handle
<TaggedProto
> proto
,
369 ObjectFlags objFlags
) {
370 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
371 return NewObjectWithGivenTaggedProto(cx
, clasp
, proto
, allocKind
, NewKind
,
377 template <typename T
, NewObjectKind NewKind
>
378 inline T
* NewObjectWithGivenTaggedProtoForKind(JSContext
* cx
,
379 Handle
<TaggedProto
> proto
) {
380 JSObject
* obj
= NewObjectWithGivenTaggedProto
<NewKind
>(cx
, &T::class_
, proto
,
382 return obj
? &obj
->as
<T
>() : nullptr;
385 } // namespace detail
387 template <typename T
>
388 inline T
* NewObjectWithGivenTaggedProto(JSContext
* cx
,
389 Handle
<TaggedProto
> proto
) {
390 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, GenericObject
>(cx
,
394 inline NativeObject
* NewObjectWithGivenProto(JSContext
* cx
,
395 const JSClass
* clasp
,
396 HandleObject proto
) {
397 return NewObjectWithGivenTaggedProto
<GenericObject
>(
398 cx
, clasp
, AsTaggedProto(proto
), ObjectFlags());
401 inline NativeObject
* NewTenuredObjectWithGivenProto(
402 JSContext
* cx
, const JSClass
* clasp
, HandleObject proto
,
403 ObjectFlags objFlags
= ObjectFlags()) {
404 return NewObjectWithGivenTaggedProto
<TenuredObject
>(
405 cx
, clasp
, AsTaggedProto(proto
), objFlags
);
408 template <typename T
>
409 inline T
* NewObjectWithGivenProto(JSContext
* cx
, HandleObject proto
) {
410 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, GenericObject
>(
411 cx
, AsTaggedProto(proto
));
414 template <typename T
>
415 inline T
* NewTenuredObjectWithGivenProto(JSContext
* cx
, HandleObject proto
) {
416 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, TenuredObject
>(
417 cx
, AsTaggedProto(proto
));
420 template <typename T
>
421 inline T
* NewObjectWithGivenProtoAndKinds(JSContext
* cx
, HandleObject proto
,
422 gc::AllocKind allocKind
,
423 NewObjectKind newKind
) {
424 JSObject
* obj
= NewObjectWithGivenTaggedProto(
425 cx
, &T::class_
, AsTaggedProto(proto
), allocKind
, newKind
);
426 return obj
? &obj
->as
<T
>() : nullptr;
429 // Make an object with the prototype set according to the cached prototype or
431 NativeObject
* NewObjectWithClassProto(JSContext
* cx
, const JSClass
* clasp
,
433 gc::AllocKind allocKind
,
434 NewObjectKind newKind
= GenericObject
,
435 ObjectFlags objFlags
= ObjectFlags());
437 inline NativeObject
* NewObjectWithClassProto(
438 JSContext
* cx
, const JSClass
* clasp
, HandleObject proto
,
439 NewObjectKind newKind
= GenericObject
,
440 ObjectFlags objFlags
= ObjectFlags()) {
441 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
442 return NewObjectWithClassProto(cx
, clasp
, proto
, allocKind
, newKind
,
447 inline T
* NewObjectWithClassProto(JSContext
* cx
, HandleObject proto
) {
448 JSObject
* obj
= NewObjectWithClassProto(cx
, &T::class_
, proto
, GenericObject
);
449 return obj
? &obj
->as
<T
>() : nullptr;
453 inline T
* NewObjectWithClassProtoAndKind(JSContext
* cx
, HandleObject proto
,
454 NewObjectKind newKind
,
455 ObjectFlags objFlags
= ObjectFlags()) {
457 NewObjectWithClassProto(cx
, &T::class_
, proto
, newKind
, objFlags
);
458 return obj
? &obj
->as
<T
>() : nullptr;
462 inline T
* NewObjectWithClassProto(JSContext
* cx
, HandleObject proto
,
463 gc::AllocKind allocKind
,
464 NewObjectKind newKind
= GenericObject
) {
466 NewObjectWithClassProto(cx
, &T::class_
, proto
, allocKind
, newKind
);
467 return obj
? &obj
->as
<T
>() : nullptr;
471 * Create a native instance of the given class with parent and proto set
472 * according to the context's active global.
474 inline NativeObject
* NewBuiltinClassInstance(
475 JSContext
* cx
, const JSClass
* clasp
, gc::AllocKind allocKind
,
476 NewObjectKind newKind
= GenericObject
) {
477 return NewObjectWithClassProto(cx
, clasp
, nullptr, allocKind
, newKind
);
480 inline NativeObject
* NewBuiltinClassInstance(
481 JSContext
* cx
, const JSClass
* clasp
,
482 NewObjectKind newKind
= GenericObject
) {
483 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
484 return NewBuiltinClassInstance(cx
, clasp
, allocKind
, newKind
);
487 template <typename T
>
488 inline T
* NewBuiltinClassInstance(JSContext
* cx
) {
489 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, GenericObject
);
490 return obj
? &obj
->as
<T
>() : nullptr;
493 template <typename T
>
494 inline T
* NewTenuredBuiltinClassInstance(JSContext
* cx
) {
495 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, TenuredObject
);
496 return obj
? &obj
->as
<T
>() : nullptr;
499 template <typename T
>
500 inline T
* NewBuiltinClassInstanceWithKind(JSContext
* cx
,
501 NewObjectKind newKind
) {
502 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, newKind
);
503 return obj
? &obj
->as
<T
>() : nullptr;
506 template <typename T
>
507 inline T
* NewBuiltinClassInstance(JSContext
* cx
, gc::AllocKind allocKind
,
508 NewObjectKind newKind
= GenericObject
) {
509 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, allocKind
, newKind
);
510 return obj
? &obj
->as
<T
>() : nullptr;
513 static inline gc::AllocKind
GuessArrayGCKind(size_t numElements
) {
515 return gc::GetGCArrayKind(numElements
);
517 return gc::AllocKind::OBJECT8
;
520 // Returns ESClass::Other if the value isn't an object, or if the object
521 // isn't of one of the enumerated classes. Otherwise returns the appropriate
523 inline bool GetClassOfValue(JSContext
* cx
, HandleValue v
, ESClass
* cls
) {
525 *cls
= ESClass::Other
;
529 RootedObject
obj(cx
, &v
.toObject());
530 return JS::GetBuiltinClass(cx
, obj
, cls
);
533 extern NativeObject
* InitClass(
534 JSContext
* cx
, HandleObject obj
, const JSClass
* protoClass
,
535 HandleObject protoProto
, const char* name
, JSNative constructor
,
536 unsigned nargs
, const JSPropertySpec
* ps
, const JSFunctionSpec
* fs
,
537 const JSPropertySpec
* static_ps
, const JSFunctionSpec
* static_fs
,
538 NativeObject
** ctorp
= nullptr);
540 MOZ_ALWAYS_INLINE
const char* GetObjectClassName(JSContext
* cx
,
542 if (obj
->is
<ProxyObject
>()) {
543 return Proxy::className(cx
, obj
);
546 return obj
->getClass()->name
;
549 inline bool IsCallable(const Value
& v
) {
550 return v
.isObject() && v
.toObject().isCallable();
553 // ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
554 inline bool IsConstructor(const Value
& v
) {
555 return v
.isObject() && v
.toObject().isConstructor();
558 static inline bool MaybePreserveDOMWrapper(JSContext
* cx
, HandleObject obj
) {
559 if (!obj
->getClass()->isDOMClass()) {
563 MOZ_ASSERT(cx
->runtime()->preserveWrapperCallback
);
564 return cx
->runtime()->preserveWrapperCallback(cx
, obj
);
569 MOZ_ALWAYS_INLINE
bool JSObject::isCallable() const {
570 if (is
<JSFunction
>()) {
573 if (is
<js::ProxyObject
>()) {
574 const js::ProxyObject
& p
= as
<js::ProxyObject
>();
575 return p
.handler()->isCallable(const_cast<JSObject
*>(this));
577 return callHook() != nullptr;
580 MOZ_ALWAYS_INLINE
bool JSObject::isConstructor() const {
581 if (is
<JSFunction
>()) {
582 const JSFunction
& fun
= as
<JSFunction
>();
583 return fun
.isConstructor();
585 if (is
<js::BoundFunctionObject
>()) {
586 const js::BoundFunctionObject
& bound
= as
<js::BoundFunctionObject
>();
587 return bound
.isConstructor();
589 if (is
<js::ProxyObject
>()) {
590 const js::ProxyObject
& p
= as
<js::ProxyObject
>();
591 return p
.handler()->isConstructor(const_cast<JSObject
*>(this));
593 return constructHook() != nullptr;
596 MOZ_ALWAYS_INLINE JSNative
JSObject::callHook() const {
597 return getClass()->getCall();
600 MOZ_ALWAYS_INLINE JSNative
JSObject::constructHook() const {
601 return getClass()->getConstruct();
604 #endif /* vm_JSObject_inl_h */