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/EnvironmentObject.h"
15 #include "vm/JSFunction.h"
16 #include "vm/Probes.h"
17 #include "vm/PropertyResult.h"
18 #include "vm/TypedArrayObject.h"
20 #include "gc/FreeOp-inl.h"
21 #include "gc/Marking-inl.h"
22 #include "gc/ObjectKind-inl.h"
23 #include "vm/ObjectOperations-inl.h" // js::MaybeHasInterestingSymbolProperty
24 #include "vm/Realm-inl.h"
28 #ifdef ENABLE_RECORD_TUPLE
29 // Defined in vm/RecordTupleShared.{h,cpp}. We cannot include that file
30 // because it causes circular dependencies.
31 extern bool IsExtendedPrimitiveWrapper(const JSObject
& obj
);
34 // Get the GC kind to use for scripted 'new', empty object literals ({}), and
35 // the |Object| constructor.
36 static inline gc::AllocKind
NewObjectGCKind() { return gc::AllocKind::OBJECT4
; }
40 MOZ_ALWAYS_INLINE
uint32_t js::NativeObject::numDynamicSlots() const {
41 uint32_t slots
= getSlotsHeader()->capacity();
42 MOZ_ASSERT(slots
== calculateDynamicSlots());
43 MOZ_ASSERT_IF(hasDynamicSlots(), slots
!= 0);
48 MOZ_ALWAYS_INLINE
uint32_t js::NativeObject::calculateDynamicSlots() const {
49 return calculateDynamicSlots(numFixedSlots(), slotSpan(), getClass());
52 /* static */ MOZ_ALWAYS_INLINE
uint32_t js::NativeObject::calculateDynamicSlots(
53 uint32_t nfixed
, uint32_t span
, const JSClass
* clasp
) {
58 uint32_t ndynamic
= span
- nfixed
;
60 // Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
61 // the dynamic slots need to get increased again. ArrayObjects ignore
62 // this because slots are uncommon in that case.
63 if (clasp
!= &ArrayObject::class_
&& ndynamic
<= SLOT_CAPACITY_MIN
) {
64 return SLOT_CAPACITY_MIN
;
68 mozilla::RoundUpPow2(ndynamic
+ ObjectSlots::VALUES_PER_HEADER
);
70 uint32_t slots
= count
- ObjectSlots::VALUES_PER_HEADER
;
71 MOZ_ASSERT(slots
>= ndynamic
);
75 /* static */ MOZ_ALWAYS_INLINE
uint32_t
76 js::NativeObject::calculateDynamicSlots(Shape
* shape
) {
77 return calculateDynamicSlots(shape
->numFixedSlots(), shape
->slotSpan(),
78 shape
->getObjectClass());
81 inline void JSObject::finalize(JSFreeOp
* fop
) {
82 js::probes::FinalizeObject(this);
85 MOZ_ASSERT(isTenured());
86 if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
87 /* Assert we're on the main thread. */
88 MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
92 const JSClass
* clasp
= getClass();
93 js::NativeObject
* nobj
=
94 clasp
->isNativeObject() ? &as
<js::NativeObject
>() : nullptr;
96 if (clasp
->hasFinalize()) {
97 clasp
->doFinalize(fop
, this);
104 if (nobj
->hasDynamicSlots()) {
105 js::ObjectSlots
* slotsHeader
= nobj
->getSlotsHeader();
106 size_t size
= js::ObjectSlots::allocSize(slotsHeader
->capacity());
107 fop
->free_(this, slotsHeader
, size
, js::MemoryUse::ObjectSlots
);
110 if (nobj
->hasDynamicElements()) {
111 js::ObjectElements
* elements
= nobj
->getElementsHeader();
112 size_t size
= elements
->numAllocatedElements() * sizeof(js::HeapSlot
);
113 fop
->free_(this, nobj
->getUnshiftedElementsHeader(), size
,
114 js::MemoryUse::ObjectElements
);
118 inline bool JSObject::isQualifiedVarObj() const {
119 if (is
<js::DebugEnvironmentProxy
>()) {
120 return as
<js::DebugEnvironmentProxy
>().environment().isQualifiedVarObj();
122 bool rv
= hasFlag(js::ObjectFlag::QualifiedVarObj
);
123 MOZ_ASSERT_IF(rv
, is
<js::GlobalObject
>() || is
<js::CallObject
>() ||
124 is
<js::VarEnvironmentObject
>() ||
125 is
<js::ModuleEnvironmentObject
>() ||
126 is
<js::NonSyntacticVariablesObject
>() ||
127 (is
<js::WithEnvironmentObject
>() &&
128 !as
<js::WithEnvironmentObject
>().isSyntactic()));
132 inline bool JSObject::isUnqualifiedVarObj() const {
133 if (is
<js::DebugEnvironmentProxy
>()) {
134 return as
<js::DebugEnvironmentProxy
>().environment().isUnqualifiedVarObj();
136 return is
<js::GlobalObject
>() || is
<js::NonSyntacticVariablesObject
>();
142 inline bool ClassCanHaveFixedData(const JSClass
* clasp
) {
143 // Normally, the number of fixed slots given an object is the maximum
144 // permitted for its size class. For array buffers and non-shared typed
145 // arrays we only use enough to cover the class reserved slots, so that
146 // the remaining space in the object's allocation is available for the
148 return !clasp
->isNativeObject() || clasp
== &js::ArrayBufferObject::class_
||
149 js::IsTypedArrayClass(clasp
);
153 class MOZ_RAII AutoSuppressAllocationMetadataBuilder
{
158 explicit AutoSuppressAllocationMetadataBuilder(JSContext
* cx
)
159 : zone(cx
->zone()), saved(zone
->suppressAllocationMetadataBuilder
) {
160 zone
->suppressAllocationMetadataBuilder
= true;
163 ~AutoSuppressAllocationMetadataBuilder() {
164 zone
->suppressAllocationMetadataBuilder
= saved
;
168 // This function is meant to be called from allocation fast paths.
170 // If we do have an allocation metadata builder, it can cause a GC, so the
171 // object must be rooted. The usual way to do this would be to make our callers
172 // pass a HandleObject, but that would require them to pay the cost of rooting
173 // the object unconditionally, even though collecting metadata is rare. Instead,
174 // SetNewObjectMetadata's contract is that the caller must use the pointer
175 // returned in place of the pointer passed. If a GC occurs, the returned pointer
176 // may be the passed pointer, relocated by GC. If no GC could occur, it's just
177 // passed through. We root nothing unless necessary.
178 template <typename T
>
179 [[nodiscard
]] static MOZ_ALWAYS_INLINE T
* SetNewObjectMetadata(JSContext
* cx
,
181 MOZ_ASSERT(!cx
->realm()->hasObjectPendingMetadata());
183 // The metadata builder is invoked for each object created on the active
184 // thread, except when analysis/compilation is active, to avoid recursion.
185 if (!cx
->isHelperThreadContext()) {
186 if (MOZ_UNLIKELY(cx
->realm()->hasAllocationMetadataBuilder()) &&
187 !cx
->zone()->suppressAllocationMetadataBuilder
) {
188 // Don't collect metadata on objects that represent metadata.
189 AutoSuppressAllocationMetadataBuilder
suppressMetadata(cx
);
191 Rooted
<T
*> rooted(cx
, obj
);
192 cx
->realm()->setNewObjectMetadata(cx
, rooted
);
202 inline js::GlobalObject
& JSObject::nonCCWGlobal() const {
204 * The global is read-barriered so that it is kept live by access through
205 * the Realm. When accessed through a JSObject, however, the global will be
206 * already kept live by the black JSObject's group pointer, so does not
207 * need to be read-barriered.
209 return *nonCCWRealm()->unsafeUnbarrieredMaybeGlobal();
212 inline bool JSObject::nonProxyIsExtensible() const {
213 MOZ_ASSERT(!uninlinedIsProxyObject());
215 #ifdef ENABLE_RECORD_TUPLE
216 if (js::IsExtendedPrimitiveWrapper(*this)) {
220 // [[Extensible]] for ordinary non-proxy objects is an object flag.
221 return !hasFlag(js::ObjectFlag::NotExtensible
);
224 inline bool JSObject::isBoundFunction() const {
225 return is
<JSFunction
>() && as
<JSFunction
>().isBoundFunction();
228 inline bool JSObject::hasInvalidatedTeleporting() const {
229 return hasFlag(js::ObjectFlag::InvalidatedTeleporting
);
232 MOZ_ALWAYS_INLINE
bool JSObject::maybeHasInterestingSymbolProperty() const {
233 if (is
<js::NativeObject
>()) {
234 return as
<js::NativeObject
>().hasInterestingSymbol();
239 inline bool JSObject::staticPrototypeIsImmutable() const {
240 MOZ_ASSERT(hasStaticPrototype());
241 return hasFlag(js::ObjectFlag::ImmutablePrototype
);
246 static MOZ_ALWAYS_INLINE
bool IsFunctionObject(const js::Value
& v
) {
247 return v
.isObject() && v
.toObject().is
<JSFunction
>();
250 static MOZ_ALWAYS_INLINE
bool IsFunctionObject(const js::Value
& v
,
252 if (v
.isObject() && v
.toObject().is
<JSFunction
>()) {
253 *fun
= &v
.toObject().as
<JSFunction
>();
259 static MOZ_ALWAYS_INLINE
bool IsNativeFunction(const js::Value
& v
,
262 return IsFunctionObject(v
, &fun
) && fun
->maybeNative() == native
;
265 static MOZ_ALWAYS_INLINE
bool IsNativeFunction(const JSObject
* obj
,
267 return obj
->is
<JSFunction
>() && obj
->as
<JSFunction
>().maybeNative() == native
;
270 // Return whether looking up a method on 'obj' definitely resolves to the
271 // original specified native function. The method may conservatively return
272 // 'false' in the case of proxies or other non-native objects.
273 static MOZ_ALWAYS_INLINE
bool HasNativeMethodPure(JSObject
* obj
,
278 if (!GetPropertyPure(cx
, obj
, NameToId(name
), &v
)) {
282 return IsNativeFunction(v
, native
);
285 // Return whether 'obj' definitely has no @@toPrimitive method.
286 static MOZ_ALWAYS_INLINE
bool HasNoToPrimitiveMethodPure(JSObject
* obj
,
288 JS::Symbol
* toPrimitive
= cx
->wellKnownSymbols().toPrimitive
;
290 if (!MaybeHasInterestingSymbolProperty(cx
, obj
, toPrimitive
, &holder
)) {
295 LookupPropertyPure(cx
, obj
, SYMBOL_TO_JSID(toPrimitive
), &pobj
, &prop
));
296 MOZ_ASSERT(prop
.isNotFound());
303 if (!LookupPropertyPure(cx
, holder
, SYMBOL_TO_JSID(toPrimitive
), &pobj
,
308 return prop
.isNotFound();
311 extern bool ToPropertyKeySlow(JSContext
* cx
, HandleValue argument
,
312 MutableHandleId result
);
314 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
315 MOZ_ALWAYS_INLINE
bool ToPropertyKey(JSContext
* cx
, HandleValue argument
,
316 MutableHandleId result
) {
317 if (MOZ_LIKELY(argument
.isPrimitive())) {
318 return PrimitiveValueToId
<CanGC
>(cx
, argument
, result
);
321 return ToPropertyKeySlow(cx
, argument
, result
);
325 * Return true if this is a compiler-created internal function accessed by
326 * its own object. Such a function object must not be accessible to script
329 inline bool IsInternalFunctionObject(JSObject
& funobj
) {
330 JSFunction
& fun
= funobj
.as
<JSFunction
>();
331 return fun
.isInterpreted() && !fun
.environment();
334 inline gc::InitialHeap
GetInitialHeap(NewObjectKind newKind
,
335 const JSClass
* clasp
,
336 gc::AllocSite
* site
= nullptr) {
337 if (newKind
!= GenericObject
) {
338 return gc::TenuredHeap
;
340 if (clasp
->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp
)) {
341 return gc::TenuredHeap
;
344 return site
->initialHeap();
346 return gc::DefaultHeap
;
350 * Make an object with the specified prototype. If parent is null, it will
351 * default to the prototype's global if the prototype is non-null.
353 NativeObject
* NewObjectWithGivenTaggedProto(JSContext
* cx
, const JSClass
* clasp
,
354 Handle
<TaggedProto
> proto
,
355 gc::AllocKind allocKind
,
356 NewObjectKind newKind
);
358 template <NewObjectKind NewKind
>
359 inline NativeObject
* NewObjectWithGivenTaggedProto(JSContext
* cx
,
360 const JSClass
* clasp
,
361 Handle
<TaggedProto
> proto
) {
362 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
363 return NewObjectWithGivenTaggedProto(cx
, clasp
, proto
, allocKind
, NewKind
);
368 template <typename T
, NewObjectKind NewKind
>
369 inline T
* NewObjectWithGivenTaggedProtoForKind(JSContext
* cx
,
370 Handle
<TaggedProto
> proto
) {
371 JSObject
* obj
= NewObjectWithGivenTaggedProto
<NewKind
>(cx
, &T::class_
, proto
);
372 return obj
? &obj
->as
<T
>() : nullptr;
375 } // namespace detail
377 template <typename T
>
378 inline T
* NewObjectWithGivenTaggedProto(JSContext
* cx
,
379 Handle
<TaggedProto
> proto
) {
380 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, GenericObject
>(cx
,
384 inline NativeObject
* NewObjectWithGivenProto(
385 JSContext
* cx
, const JSClass
* clasp
, HandleObject proto
,
386 gc::AllocKind allocKind
, NewObjectKind newKind
= GenericObject
) {
387 return NewObjectWithGivenTaggedProto(cx
, clasp
, AsTaggedProto(proto
),
391 inline NativeObject
* NewObjectWithGivenProto(JSContext
* cx
,
392 const JSClass
* clasp
,
393 HandleObject proto
) {
394 return NewObjectWithGivenTaggedProto
<GenericObject
>(cx
, clasp
,
395 AsTaggedProto(proto
));
398 inline NativeObject
* NewTenuredObjectWithGivenProto(JSContext
* cx
,
399 const JSClass
* clasp
,
400 HandleObject proto
) {
401 return NewObjectWithGivenTaggedProto
<TenuredObject
>(cx
, clasp
,
402 AsTaggedProto(proto
));
405 template <typename T
>
406 inline T
* NewObjectWithGivenProto(JSContext
* cx
, HandleObject proto
) {
407 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, GenericObject
>(
408 cx
, AsTaggedProto(proto
));
411 template <typename T
>
412 inline T
* NewTenuredObjectWithGivenProto(JSContext
* cx
, HandleObject proto
) {
413 return detail::NewObjectWithGivenTaggedProtoForKind
<T
, TenuredObject
>(
414 cx
, AsTaggedProto(proto
));
417 template <typename T
>
418 inline T
* NewObjectWithGivenProtoAndKinds(JSContext
* cx
, HandleObject proto
,
419 gc::AllocKind allocKind
,
420 NewObjectKind newKind
) {
421 JSObject
* obj
= NewObjectWithGivenTaggedProto(
422 cx
, &T::class_
, AsTaggedProto(proto
), allocKind
, newKind
);
423 return obj
? &obj
->as
<T
>() : nullptr;
426 // Make an object with the prototype set according to the cached prototype or
428 NativeObject
* NewObjectWithClassProto(JSContext
* cx
, const JSClass
* clasp
,
430 gc::AllocKind allocKind
,
431 NewObjectKind newKind
= GenericObject
);
433 inline NativeObject
* NewObjectWithClassProto(
434 JSContext
* cx
, const JSClass
* clasp
, HandleObject proto
,
435 NewObjectKind newKind
= GenericObject
) {
436 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
437 return NewObjectWithClassProto(cx
, clasp
, proto
, allocKind
, newKind
);
441 inline T
* NewObjectWithClassProto(JSContext
* cx
, HandleObject proto
) {
442 JSObject
* obj
= NewObjectWithClassProto(cx
, &T::class_
, proto
, GenericObject
);
443 return obj
? &obj
->as
<T
>() : nullptr;
447 inline T
* NewObjectWithClassProtoAndKind(JSContext
* cx
, HandleObject proto
,
448 NewObjectKind newKind
) {
449 JSObject
* obj
= NewObjectWithClassProto(cx
, &T::class_
, proto
, newKind
);
450 return obj
? &obj
->as
<T
>() : nullptr;
454 inline T
* NewObjectWithClassProto(JSContext
* cx
, HandleObject proto
,
455 gc::AllocKind allocKind
,
456 NewObjectKind newKind
= GenericObject
) {
458 NewObjectWithClassProto(cx
, &T::class_
, proto
, allocKind
, newKind
);
459 return obj
? &obj
->as
<T
>() : nullptr;
463 * Create a native instance of the given class with parent and proto set
464 * according to the context's active global.
466 inline NativeObject
* NewBuiltinClassInstance(
467 JSContext
* cx
, const JSClass
* clasp
, gc::AllocKind allocKind
,
468 NewObjectKind newKind
= GenericObject
) {
469 return NewObjectWithClassProto(cx
, clasp
, nullptr, allocKind
, newKind
);
472 inline NativeObject
* NewBuiltinClassInstance(
473 JSContext
* cx
, const JSClass
* clasp
,
474 NewObjectKind newKind
= GenericObject
) {
475 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
476 return NewBuiltinClassInstance(cx
, clasp
, allocKind
, newKind
);
479 template <typename T
>
480 inline T
* NewBuiltinClassInstance(JSContext
* cx
) {
481 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, GenericObject
);
482 return obj
? &obj
->as
<T
>() : nullptr;
485 template <typename T
>
486 inline T
* NewTenuredBuiltinClassInstance(JSContext
* cx
) {
487 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, TenuredObject
);
488 return obj
? &obj
->as
<T
>() : nullptr;
491 template <typename T
>
492 inline T
* NewBuiltinClassInstanceWithKind(JSContext
* cx
,
493 NewObjectKind newKind
) {
494 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, newKind
);
495 return obj
? &obj
->as
<T
>() : nullptr;
498 template <typename T
>
499 inline T
* NewBuiltinClassInstance(JSContext
* cx
, gc::AllocKind allocKind
,
500 NewObjectKind newKind
= GenericObject
) {
501 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, allocKind
, newKind
);
502 return obj
? &obj
->as
<T
>() : nullptr;
505 // Used to optimize calls to (new Object())
506 bool NewObjectScriptedCall(JSContext
* cx
, MutableHandleObject obj
);
508 static inline gc::AllocKind
GuessArrayGCKind(size_t numElements
) {
510 return gc::GetGCArrayKind(numElements
);
512 return gc::AllocKind::OBJECT8
;
515 // Returns ESClass::Other if the value isn't an object, or if the object
516 // isn't of one of the enumerated classes. Otherwise returns the appropriate
518 inline bool GetClassOfValue(JSContext
* cx
, HandleValue v
, ESClass
* cls
) {
520 *cls
= ESClass::Other
;
524 RootedObject
obj(cx
, &v
.toObject());
525 return JS::GetBuiltinClass(cx
, obj
, cls
);
528 extern NativeObject
* InitClass(JSContext
* cx
, HandleObject obj
,
529 HandleObject parent_proto
, const JSClass
* clasp
,
530 JSNative constructor
, unsigned nargs
,
531 const JSPropertySpec
* ps
,
532 const JSFunctionSpec
* fs
,
533 const JSPropertySpec
* static_ps
,
534 const JSFunctionSpec
* static_fs
,
535 NativeObject
** ctorp
= nullptr);
537 MOZ_ALWAYS_INLINE
const char* GetObjectClassName(JSContext
* cx
,
539 if (obj
->is
<ProxyObject
>()) {
540 return Proxy::className(cx
, obj
);
543 return obj
->getClass()->name
;
546 inline bool IsCallable(const Value
& v
) {
547 return v
.isObject() && v
.toObject().isCallable();
550 // ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
551 inline bool IsConstructor(const Value
& v
) {
552 return v
.isObject() && v
.toObject().isConstructor();
555 static inline bool MaybePreserveDOMWrapper(JSContext
* cx
, HandleObject obj
) {
556 if (!obj
->getClass()->isDOMClass()) {
560 MOZ_ASSERT(cx
->runtime()->preserveWrapperCallback
);
561 return cx
->runtime()->preserveWrapperCallback(cx
, obj
);
566 MOZ_ALWAYS_INLINE
bool JSObject::isCallable() const {
567 if (is
<JSFunction
>()) {
570 if (is
<js::ProxyObject
>()) {
571 const js::ProxyObject
& p
= as
<js::ProxyObject
>();
572 return p
.handler()->isCallable(const_cast<JSObject
*>(this));
574 return callHook() != nullptr;
577 MOZ_ALWAYS_INLINE
bool JSObject::isConstructor() const {
578 if (is
<JSFunction
>()) {
579 const JSFunction
& fun
= as
<JSFunction
>();
580 return fun
.isConstructor();
582 if (is
<js::ProxyObject
>()) {
583 const js::ProxyObject
& p
= as
<js::ProxyObject
>();
584 return p
.handler()->isConstructor(const_cast<JSObject
*>(this));
586 return constructHook() != nullptr;
589 MOZ_ALWAYS_INLINE JSNative
JSObject::callHook() const {
590 return getClass()->getCall();
593 MOZ_ALWAYS_INLINE JSNative
JSObject::constructHook() const {
594 return getClass()->getConstruct();
597 #endif /* vm_JSObject_inl_h */