Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / vm / JSObject.h
blob3785cbed493d0940c86f2ccf983854f8823ea1e7
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_h
8 #define vm_JSObject_h
10 #include "mozilla/MemoryReporting.h"
12 #include "jsfriendapi.h"
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"
20 namespace JS {
21 struct ClassInfo;
22 } // namespace JS
24 namespace js {
26 using PropertyDescriptorVector = JS::GCVector<JS::PropertyDescriptor>;
27 class GCMarker;
28 class Nursery;
29 struct AutoEnterOOMUnsafeRegion;
31 namespace gc {
32 class RelocationOverlay;
33 } // namespace gc
35 /****************************************************************************/
37 class GlobalObject;
38 class NativeObject;
40 enum class IntegrityLevel { Sealed, Frozen };
43 * The NewObjectKind allows an allocation site to specify the lifetime
44 * requirements that must be fixed at allocation time.
46 enum NewObjectKind {
47 /* This is the default. Most objects are generic. */
48 GenericObject,
51 * Objects which will not benefit from being allocated in the nursery
52 * (e.g. because they are known to have a long lifetime) may be allocated
53 * with this kind to place them immediately into the tenured generation.
55 TenuredObject
58 // Forward declarations, required for later friend declarations.
59 bool PreventExtensions(JSContext* cx, JS::HandleObject obj,
60 JS::ObjectOpResult& result);
61 bool SetImmutablePrototype(JSContext* cx, JS::HandleObject obj,
62 bool* succeeded);
64 } /* namespace js */
67 * [SMDOC] JSObject layout
69 * A JavaScript object.
71 * This is the base class for all objects exposed to JS script (as well as some
72 * objects that are only accessed indirectly). Subclasses add additional fields
73 * and execution semantics. The runtime class of an arbitrary JSObject is
74 * identified by JSObject::getClass().
76 * All objects have a non-null Shape, stored in the cell header, which describes
77 * the current layout and set of property keys of the object.
79 * Each Shape has a pointer to a BaseShape. The BaseShape contains the object's
80 * prototype object, its class, and its realm.
82 * NOTE: Some operations can change the contents of an object (including class)
83 * in-place so avoid assuming an object with same pointer has same class
84 * as before.
85 * - JSObject::swap()
87 class JSObject
88 : public js::gc::CellWithTenuredGCPointer<js::gc::Cell, js::Shape> {
89 public:
90 // The Shape is stored in the cell header.
91 js::Shape* shape() const { return headerPtr(); }
93 // Like shape(), but uses getAtomic to read the header word.
94 js::Shape* shapeMaybeForwarded() const { return headerPtrAtomic(); }
96 #ifndef JS_64BIT
97 // Ensure fixed slots have 8-byte alignment on 32-bit platforms.
98 uint32_t padding_;
99 #endif
101 private:
102 friend class js::GCMarker;
103 friend class js::GlobalObject;
104 friend class js::Nursery;
105 friend class js::gc::RelocationOverlay;
106 friend bool js::PreventExtensions(JSContext* cx, JS::HandleObject obj,
107 JS::ObjectOpResult& result);
108 friend bool js::SetImmutablePrototype(JSContext* cx, JS::HandleObject obj,
109 bool* succeeded);
111 public:
112 const JSClass* getClass() const { return shape()->getObjectClass(); }
113 bool hasClass(const JSClass* c) const { return getClass() == c; }
115 js::LookupPropertyOp getOpsLookupProperty() const {
116 return getClass()->getOpsLookupProperty();
118 js::DefinePropertyOp getOpsDefineProperty() const {
119 return getClass()->getOpsDefineProperty();
121 js::HasPropertyOp getOpsHasProperty() const {
122 return getClass()->getOpsHasProperty();
124 js::GetPropertyOp getOpsGetProperty() const {
125 return getClass()->getOpsGetProperty();
127 js::SetPropertyOp getOpsSetProperty() const {
128 return getClass()->getOpsSetProperty();
130 js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor() const {
131 return getClass()->getOpsGetOwnPropertyDescriptor();
133 js::DeletePropertyOp getOpsDeleteProperty() const {
134 return getClass()->getOpsDeleteProperty();
136 js::GetElementsOp getOpsGetElements() const {
137 return getClass()->getOpsGetElements();
139 JSFunToStringOp getOpsFunToString() const {
140 return getClass()->getOpsFunToString();
143 JS::Compartment* compartment() const { return shape()->compartment(); }
144 JS::Compartment* maybeCompartment() const { return compartment(); }
146 void initShape(js::Shape* shape) {
147 // Note: use Cell::Zone() instead of zone() because zone() relies on the
148 // shape we still have to initialize.
149 MOZ_ASSERT(Cell::zone() == shape->zone());
150 initHeaderPtr(shape);
152 void setShape(js::Shape* shape) {
153 MOZ_ASSERT(maybeCCWRealm() == shape->realm());
154 setHeaderPtr(shape);
157 static bool setFlag(JSContext* cx, JS::HandleObject obj, js::ObjectFlag flag);
159 bool hasFlag(js::ObjectFlag flag) const {
160 return shape()->hasObjectFlag(flag);
163 bool hasAnyFlag(js::ObjectFlags flags) const {
164 return shape()->objectFlags().hasAnyFlag(flags);
167 // Change this object's shape for a prototype mutation.
169 // Note: the caller must ensure the object has a mutable proto, is extensible,
170 // etc.
171 static bool setProtoUnchecked(JSContext* cx, JS::HandleObject obj,
172 js::Handle<js::TaggedProto> proto);
174 // An object is marked IsUsedAsPrototype if it is (or was) another object's
175 // prototype. Optimization heuristics will make use of this flag.
177 // This flag is only relevant for static prototypes. Proxy traps can return
178 // objects without this flag set.
180 // NOTE: it's important to call setIsUsedAsPrototype *after* initializing the
181 // object's properties, because that avoids unnecessary shadowing checks and
182 // reshaping.
184 // See: ReshapeForProtoMutation, ReshapeForShadowedProp
185 bool isUsedAsPrototype() const {
186 return hasFlag(js::ObjectFlag::IsUsedAsPrototype);
188 static bool setIsUsedAsPrototype(JSContext* cx, JS::HandleObject obj) {
189 return setFlag(cx, obj, js::ObjectFlag::IsUsedAsPrototype);
192 bool useWatchtowerTestingLog() const {
193 return hasFlag(js::ObjectFlag::UseWatchtowerTestingLog);
195 static bool setUseWatchtowerTestingLog(JSContext* cx, JS::HandleObject obj) {
196 return setFlag(cx, obj, js::ObjectFlag::UseWatchtowerTestingLog);
199 bool isGenerationCountedGlobal() const {
200 return hasFlag(js::ObjectFlag::GenerationCountedGlobal);
202 static bool setGenerationCountedGlobal(JSContext* cx, JS::HandleObject obj) {
203 return setFlag(cx, obj, js::ObjectFlag::GenerationCountedGlobal);
206 // A "qualified" varobj is the object on which "qualified" variable
207 // declarations (i.e., those defined with "var") are kept.
209 // Conceptually, when a var binding is defined, it is defined on the
210 // innermost qualified varobj on the scope chain.
212 // Function scopes (CallObjects) are qualified varobjs, and there can be
213 // no other qualified varobj that is more inner for var bindings in that
214 // function. As such, all references to local var bindings in a function
215 // may be statically bound to the function scope. This is subject to
216 // further optimization. Unaliased bindings inside functions reside
217 // entirely on the frame, not in CallObjects.
219 // Global scopes are also qualified varobjs. It is possible to statically
220 // know, for a given script, that are no more inner qualified varobjs, so
221 // free variable references can be statically bound to the global.
223 // Finally, there are non-syntactic qualified varobjs used by embedders
224 // (e.g., Gecko and XPConnect), as they often wish to run scripts under a
225 // scope that captures var bindings.
226 inline bool isQualifiedVarObj() const;
227 static bool setQualifiedVarObj(JSContext* cx, JS::HandleObject obj) {
228 return setFlag(cx, obj, js::ObjectFlag::QualifiedVarObj);
231 // An "unqualified" varobj is the object on which "unqualified"
232 // assignments (i.e., bareword assignments for which the LHS does not
233 // exist on the scope chain) are kept.
234 inline bool isUnqualifiedVarObj() const;
236 // Once the "invalidated teleporting" flag is set for an object, it is never
237 // cleared and it may cause the JITs to insert additional guards when
238 // accessing properties on this object. While the flag remains clear, the
239 // shape teleporting optimization can be used to avoid those extra checks.
241 // The flag is set on the object if either:
243 // * Its own proto was mutated or it was on the proto chain of an object that
244 // had its proto mutated.
246 // * It was on the proto chain of an object that started shadowing a property
247 // on this object.
249 // See:
250 // - ReshapeForProtoMutation
251 // - ReshapeForShadowedProp
252 // - ProtoChainSupportsTeleporting
253 inline bool hasInvalidatedTeleporting() const;
254 static bool setInvalidatedTeleporting(JSContext* cx, JS::HandleObject obj) {
255 MOZ_ASSERT(obj->isUsedAsPrototype());
256 MOZ_ASSERT(obj->hasStaticPrototype(),
257 "teleporting as a concept is only applicable to static "
258 "(not dynamically-computed) prototypes");
259 return setFlag(cx, obj, js::ObjectFlag::InvalidatedTeleporting);
263 * Whether there may be "interesting symbol" properties on this object. An
264 * interesting symbol is a symbol for which symbol->isInterestingSymbol()
265 * returns true.
267 MOZ_ALWAYS_INLINE bool maybeHasInterestingSymbolProperty() const;
269 inline bool needsProxyGetSetResultValidation() const;
271 /* GC support. */
273 void traceChildren(JSTracer* trc);
275 void fixupAfterMovingGC() {}
277 static const JS::TraceKind TraceKind = JS::TraceKind::Object;
279 static constexpr size_t thingSize(js::gc::AllocKind kind);
281 MOZ_ALWAYS_INLINE JS::Zone* zone() const {
282 MOZ_ASSERT_IF(!isTenured(), nurseryZone() == shape()->zone());
283 return shape()->zone();
285 MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZone() const {
286 return JS::shadow::Zone::from(zone());
288 MOZ_ALWAYS_INLINE JS::Zone* zoneFromAnyThread() const {
289 MOZ_ASSERT_IF(!isTenured(),
290 nurseryZoneFromAnyThread() == shape()->zoneFromAnyThread());
291 return shape()->zoneFromAnyThread();
293 MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZoneFromAnyThread() const {
294 return JS::shadow::Zone::from(zoneFromAnyThread());
296 static MOZ_ALWAYS_INLINE void postWriteBarrier(void* cellp, JSObject* prev,
297 JSObject* next) {
298 js::gc::PostWriteBarrierImpl<JSObject>(cellp, prev, next);
301 /* Return the allocKind we would use if we were to tenure this object. */
302 js::gc::AllocKind allocKindForTenure(const js::Nursery& nursery) const;
304 bool canHaveFixedElements() const;
306 size_t tenuredSizeOfThis() const {
307 MOZ_ASSERT(isTenured());
308 return js::gc::Arena::thingSize(asTenured().getAllocKind());
311 void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
312 JS::ClassInfo* info,
313 JS::RuntimeSizes* runtimeSizes);
315 // We can only use addSizeOfExcludingThis on tenured objects: it assumes it
316 // can apply mallocSizeOf to bits and pieces of the object, whereas objects
317 // in the nursery may have those bits and pieces allocated in the nursery
318 // along with them, and are not each their own malloc blocks.
319 size_t sizeOfIncludingThisInNursery() const;
321 #ifdef DEBUG
322 static void debugCheckNewObject(js::Shape* shape, js::gc::AllocKind allocKind,
323 js::gc::Heap heap);
324 #else
325 static void debugCheckNewObject(js::Shape* shape, js::gc::AllocKind allocKind,
326 js::gc::Heap heap) {}
327 #endif
330 * We permit proxies to dynamically compute their prototype if desired.
331 * (Not all proxies will so desire: in particular, most DOM proxies can
332 * track their prototype with a single, nullable JSObject*.) If a proxy
333 * so desires, we store (JSObject*)0x1 in the proto field of the object's
334 * group.
336 * We offer three ways to get an object's prototype:
338 * 1. obj->staticPrototype() returns the prototype, but it asserts if obj
339 * is a proxy, and the proxy has opted to dynamically compute its
340 * prototype using a getPrototype() handler.
341 * 2. obj->taggedProto() returns a TaggedProto, which can be tested to
342 * check if the proto is an object, nullptr, or lazily computed.
343 * 3. js::GetPrototype(cx, obj, &proto) computes the proto of an object.
344 * If obj is a proxy with dynamically-computed prototype, this code may
345 * perform arbitrary behavior (allocation, GC, run JS) while computing
346 * the proto.
349 js::TaggedProto taggedProto() const { return shape()->proto(); }
351 bool uninlinedIsProxyObject() const;
353 JSObject* staticPrototype() const {
354 MOZ_ASSERT(hasStaticPrototype());
355 return taggedProto().toObjectOrNull();
358 // Normal objects and a subset of proxies have an uninteresting, static
359 // (albeit perhaps mutable) [[Prototype]]. For such objects the
360 // [[Prototype]] is just a value returned when needed for accesses, or
361 // modified in response to requests. These objects store the
362 // [[Prototype]] directly within |obj->group()|.
363 bool hasStaticPrototype() const { return !hasDynamicPrototype(); }
365 // The remaining proxies have a [[Prototype]] requiring dynamic computation
366 // for every access, going through the proxy handler {get,set}Prototype and
367 // setImmutablePrototype methods. (Wrappers particularly use this to keep
368 // the wrapper/wrappee [[Prototype]]s consistent.)
369 bool hasDynamicPrototype() const {
370 bool dynamic = taggedProto().isDynamic();
371 MOZ_ASSERT_IF(dynamic, uninlinedIsProxyObject());
372 return dynamic;
375 // True iff this object's [[Prototype]] is immutable. Must be called only
376 // on objects with a static [[Prototype]]!
377 inline bool staticPrototypeIsImmutable() const;
380 * Environment chains.
382 * The environment chain of an object is the link in the search path when
383 * a script does a name lookup on an environment object. For JS internal
384 * environment objects --- Call, LexicalEnvironment, and WithEnvironment
385 * --- the chain is stored in the first fixed slot of the object. For
386 * other environment objects, the chain goes directly to the global.
388 * In code which is not marked hasNonSyntacticScope, environment chains
389 * can contain only syntactic environment objects (see
390 * IsSyntacticEnvironment) with a global object at the root as the
391 * environment of the outermost non-function script. In
392 * hasNonSyntacticScope code, the environment of the outermost
393 * non-function script might not be a global object, and can have a mix of
394 * other objects above it before the global object is reached.
398 * Get the enclosing environment of an object. When called on a
399 * non-EnvironmentObject, this will just be the global (the name
400 * "enclosing environment" still applies in this situation because
401 * non-EnvironmentObjects can be on the environment chain).
403 inline JSObject* enclosingEnvironment() const;
405 // Cross-compartment wrappers are not associated with a single realm/global,
406 // so these methods assert the object is not a CCW.
407 inline js::GlobalObject& nonCCWGlobal() const;
409 JS::Realm* nonCCWRealm() const {
410 MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(this));
411 return shape()->realm();
413 bool hasSameRealmAs(JSContext* cx) const;
415 // Returns the object's realm even if the object is a CCW (be careful, in
416 // this case the realm is not very meaningful because wrappers are shared by
417 // all realms in the compartment).
418 JS::Realm* maybeCCWRealm() const { return shape()->realm(); }
421 * ES5 meta-object properties and operations.
424 public:
425 // Indicates whether a non-proxy is extensible. Don't call on proxies!
426 // This method really shouldn't exist -- but there are a few internal
427 // places that want it (JITs and the like), and it'd be a pain to mark them
428 // all as friends.
429 inline bool nonProxyIsExtensible() const;
430 bool uninlinedNonProxyIsExtensible() const;
432 public:
434 * Back to generic stuff.
436 MOZ_ALWAYS_INLINE bool isCallable() const;
437 MOZ_ALWAYS_INLINE bool isConstructor() const;
438 MOZ_ALWAYS_INLINE JSNative callHook() const;
439 MOZ_ALWAYS_INLINE JSNative constructHook() const;
441 bool isBackgroundFinalized() const;
443 MOZ_ALWAYS_INLINE void finalize(JS::GCContext* gcx);
445 public:
446 static bool nonNativeSetProperty(JSContext* cx, js::HandleObject obj,
447 js::HandleId id, js::HandleValue v,
448 js::HandleValue receiver,
449 JS::ObjectOpResult& result);
450 static bool nonNativeSetElement(JSContext* cx, js::HandleObject obj,
451 uint32_t index, js::HandleValue v,
452 js::HandleValue receiver,
453 JS::ObjectOpResult& result);
455 static void swap(JSContext* cx, JS::HandleObject a, JS::HandleObject b,
456 js::AutoEnterOOMUnsafeRegion& oomUnsafe);
459 * In addition to the generic object interface provided by JSObject,
460 * specific types of objects may provide additional operations. To access,
461 * these addition operations, callers should use the pattern:
463 * if (obj.is<XObject>()) {
464 * XObject& x = obj.as<XObject>();
465 * x.foo();
468 * These XObject classes form a hierarchy. For example, for a cloned block
469 * object, the following predicates are true: is<ClonedBlockObject>,
470 * is<NestedScopeObject> and is<ScopeObject>. Each of these has a
471 * respective class that derives and adds operations.
473 * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file
474 * triplet (along with any class YObject that derives XObject).
476 * Note that X represents a low-level representation and does not query the
477 * [[Class]] property of object defined by the spec: use |JS::GetBuiltinClass|
478 * for this.
481 template <class T>
482 inline bool is() const {
483 return getClass() == &T::class_;
486 template <class T>
487 T& as() {
488 MOZ_ASSERT(this->is<T>());
489 return *static_cast<T*>(this);
492 template <class T>
493 const T& as() const {
494 MOZ_ASSERT(this->is<T>());
495 return *static_cast<const T*>(this);
499 * True if either this or CheckedUnwrap(this) is an object of class T.
500 * (Only two objects are checked, regardless of how many wrappers there
501 * are.)
503 * /!\ Note: This can be true at one point, but false later for the same
504 * object, thanks to js::NukeCrossCompartmentWrapper and friends.
506 template <class T>
507 bool canUnwrapAs();
510 * Unwrap and downcast to class T.
512 * Precondition: `this->canUnwrapAs<T>()`. Note that it's not enough to
513 * have checked this at some point in the past; if there's any doubt as to
514 * whether js::Nuke* could have been called in the meantime, check again.
516 template <class T>
517 T& unwrapAs();
520 * Tries to unwrap and downcast to class T. Returns nullptr if (and only if) a
521 * wrapper with a security policy is involved. Crashes in all builds if the
522 * (possibly unwrapped) object is not of class T (for example, because it's a
523 * dead wrapper).
525 template <class T>
526 inline T* maybeUnwrapAs();
529 * Tries to unwrap and downcast to an object with class |clasp|. Returns
530 * nullptr if (and only if) a wrapper with a security policy is involved.
531 * Crashes in all builds if the (possibly unwrapped) object doesn't have class
532 * |clasp| (for example, because it's a dead wrapper).
534 inline JSObject* maybeUnwrapAs(const JSClass* clasp);
537 * Tries to unwrap and downcast to class T. Returns nullptr if a wrapper with
538 * a security policy is involved or if the object does not have class T.
540 template <class T>
541 T* maybeUnwrapIf();
543 #if defined(DEBUG) || defined(JS_JITSPEW)
544 void dump(js::GenericPrinter& fp) const;
545 void dump() const;
546 #endif
548 // Maximum size in bytes of a JSObject.
549 #ifdef JS_64BIT
550 static constexpr size_t MAX_BYTE_SIZE =
551 3 * sizeof(void*) + 16 * sizeof(JS::Value);
552 #else
553 static constexpr size_t MAX_BYTE_SIZE =
554 4 * sizeof(void*) + 16 * sizeof(JS::Value);
555 #endif
557 protected:
558 // JIT Accessors.
560 // To help avoid writing Spectre-unsafe code, we only allow MacroAssembler
561 // to call the method below.
562 friend class js::jit::MacroAssembler;
564 static constexpr size_t offsetOfShape() { return offsetOfHeaderPtr(); }
566 private:
567 JSObject(const JSObject& other) = delete;
568 void operator=(const JSObject& other) = delete;
570 protected:
571 // For the allocator only, to be used with placement new.
572 friend class js::gc::GCRuntime;
573 JSObject() = default;
576 template <>
577 inline bool JSObject::is<JSObject>() const {
578 return true;
581 template <typename Wrapper>
582 template <typename U>
583 MOZ_ALWAYS_INLINE JS::Handle<U*> js::RootedOperations<JSObject*, Wrapper>::as()
584 const {
585 const Wrapper& self = *static_cast<const Wrapper*>(this);
586 MOZ_ASSERT(self->template is<U>());
587 return Handle<U*>::fromMarkedLocation(
588 reinterpret_cast<U* const*>(self.address()));
591 template <typename Wrapper>
592 template <class U>
593 MOZ_ALWAYS_INLINE JS::Handle<U*> js::HandleOperations<JSObject*, Wrapper>::as()
594 const {
595 const JS::Handle<JSObject*>& self =
596 *static_cast<const JS::Handle<JSObject*>*>(this);
597 MOZ_ASSERT(self->template is<U>());
598 return Handle<U*>::fromMarkedLocation(
599 reinterpret_cast<U* const*>(self.address()));
602 template <class T>
603 bool JSObject::canUnwrapAs() {
604 static_assert(!std::is_convertible_v<T*, js::Wrapper*>,
605 "T can't be a Wrapper type; this function discards wrappers");
607 if (is<T>()) {
608 return true;
610 JSObject* obj = js::CheckedUnwrapStatic(this);
611 return obj && obj->is<T>();
614 template <class T>
615 T& JSObject::unwrapAs() {
616 static_assert(!std::is_convertible_v<T*, js::Wrapper*>,
617 "T can't be a Wrapper type; this function discards wrappers");
619 if (is<T>()) {
620 return as<T>();
623 // Since the caller just called canUnwrapAs<T>(), which does a
624 // CheckedUnwrap, this does not need to repeat the security check.
625 JSObject* unwrapped = js::UncheckedUnwrap(this);
626 MOZ_ASSERT(js::CheckedUnwrapStatic(this) == unwrapped,
627 "check that the security check we skipped really is redundant");
628 return unwrapped->as<T>();
631 template <class T>
632 inline T* JSObject::maybeUnwrapAs() {
633 static_assert(!std::is_convertible_v<T*, js::Wrapper*>,
634 "T can't be a Wrapper type; this function discards wrappers");
636 if (is<T>()) {
637 return &as<T>();
640 JSObject* unwrapped = js::CheckedUnwrapStatic(this);
641 if (!unwrapped) {
642 return nullptr;
645 if (MOZ_LIKELY(unwrapped->is<T>())) {
646 return &unwrapped->as<T>();
649 MOZ_CRASH("Invalid object. Dead wrapper?");
652 inline JSObject* JSObject::maybeUnwrapAs(const JSClass* clasp) {
653 if (hasClass(clasp)) {
654 return this;
657 JSObject* unwrapped = js::CheckedUnwrapStatic(this);
658 if (!unwrapped) {
659 return nullptr;
662 if (MOZ_LIKELY(unwrapped->hasClass(clasp))) {
663 return unwrapped;
666 MOZ_CRASH("Invalid object. Dead wrapper?");
669 template <class T>
670 T* JSObject::maybeUnwrapIf() {
671 static_assert(!std::is_convertible_v<T*, js::Wrapper*>,
672 "T can't be a Wrapper type; this function discards wrappers");
674 if (is<T>()) {
675 return &as<T>();
678 JSObject* unwrapped = js::CheckedUnwrapStatic(this);
679 return (unwrapped && unwrapped->is<T>()) ? &unwrapped->as<T>() : nullptr;
683 * The only sensible way to compare JSObject with == is by identity. We use
684 * const& instead of * as a syntactic way to assert non-null. This leads to an
685 * abundance of address-of operators to identity. Hence this overload.
687 static MOZ_ALWAYS_INLINE bool operator==(const JSObject& lhs,
688 const JSObject& rhs) {
689 return &lhs == &rhs;
692 static MOZ_ALWAYS_INLINE bool operator!=(const JSObject& lhs,
693 const JSObject& rhs) {
694 return &lhs != &rhs;
697 // Size of the various GC thing allocation sizes used for objects.
698 struct JSObject_Slots0 : JSObject {
699 void* data[2];
701 struct JSObject_Slots2 : JSObject {
702 void* data[2];
703 js::Value fslots[2];
705 struct JSObject_Slots4 : JSObject {
706 void* data[2];
707 js::Value fslots[4];
709 struct JSObject_Slots7 : JSObject {
710 // Only used for extended functions which are required to have exactly seven
711 // fixed slots due to JIT assumptions.
712 void* data[2];
713 js::Value fslots[7];
715 struct JSObject_Slots8 : JSObject {
716 void* data[2];
717 js::Value fslots[8];
719 struct JSObject_Slots12 : JSObject {
720 void* data[2];
721 js::Value fslots[12];
723 struct JSObject_Slots16 : JSObject {
724 void* data[2];
725 js::Value fslots[16];
728 /* static */
729 constexpr size_t JSObject::thingSize(js::gc::AllocKind kind) {
730 MOZ_ASSERT(IsObjectAllocKind(kind));
731 constexpr uint8_t objectSizes[] = {
732 #define EXPAND_OJBECT_SIZE(_1, _2, _3, sizedType, _4, _5, _6) sizeof(sizedType),
733 FOR_EACH_OBJECT_ALLOCKIND(EXPAND_OJBECT_SIZE)};
734 return objectSizes[size_t(kind)];
737 namespace js {
739 // Returns true if object may possibly use JSObject::swap. The JITs may better
740 // optimize objects that can never swap (and thus change their type).
742 // If ObjectMayBeSwapped is false, it is safe to guard on pointer identity to
743 // test immutable features of the object. For example, the target of a
744 // JSFunction will not change. Note: the object can still be moved by GC.
745 extern bool ObjectMayBeSwapped(const JSObject* obj);
747 extern bool DefineFunctions(JSContext* cx, HandleObject obj,
748 const JSFunctionSpec* fs);
750 /* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
751 extern bool ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
753 inline bool ToPrimitive(JSContext* cx, MutableHandleValue vp) {
754 if (vp.isPrimitive()) {
755 return true;
757 return ToPrimitiveSlow(cx, JSTYPE_UNDEFINED, vp);
760 inline bool ToPrimitive(JSContext* cx, JSType preferredType,
761 MutableHandleValue vp) {
762 if (vp.isPrimitive()) {
763 return true;
765 return ToPrimitiveSlow(cx, preferredType, vp);
769 * toString support. (This isn't called GetClassName because there's a macro in
770 * <windows.h> with that name.)
772 MOZ_ALWAYS_INLINE const char* GetObjectClassName(JSContext* cx,
773 HandleObject obj);
776 * Prepare a |this| object to be returned to script. This includes replacing
777 * Windows with their corresponding WindowProxy.
779 * Helpers are also provided to first extract the |this| from specific
780 * types of environment.
782 JSObject* GetThisObject(JSObject* obj);
784 JSObject* GetThisObjectOfLexical(JSObject* env);
786 JSObject* GetThisObjectOfWith(JSObject* env);
788 } /* namespace js */
790 namespace js {
792 // ES6 9.1.15 GetPrototypeFromConstructor.
793 extern bool GetPrototypeFromConstructor(JSContext* cx,
794 js::HandleObject newTarget,
795 JSProtoKey intrinsicDefaultProto,
796 js::MutableHandleObject proto);
798 // https://tc39.github.io/ecma262/#sec-getprototypefromconstructor
800 // Determine which [[Prototype]] to use when creating a new object using a
801 // builtin constructor.
803 // This sets `proto` to `nullptr` to mean "the builtin prototype object for
804 // this type in the current realm", the common case.
806 // We could set it to `cx->global()->getOrCreatePrototype(protoKey)`, but
807 // nullptr gets a fast path in e.g. js::NewObjectWithClassProtoCommon.
809 // intrinsicDefaultProto can be JSProto_Null if there's no appropriate
810 // JSProtoKey enum; but we then select the wrong prototype object in a
811 // multi-realm corner case (see bug 1515167).
812 MOZ_ALWAYS_INLINE bool GetPrototypeFromBuiltinConstructor(
813 JSContext* cx, const CallArgs& args, JSProtoKey intrinsicDefaultProto,
814 js::MutableHandleObject proto) {
815 // We can skip the "prototype" lookup in the two common cases:
816 // 1. Builtin constructor called without `new`, as in `obj = Object();`.
817 // 2. Builtin constructor called with `new`, as in `obj = new Object();`.
819 // Cases that can't take the fast path include `new MySubclassOfObject()`,
820 // `new otherGlobal.Object()`, and `Reflect.construct(Object, [], Date)`.
821 if (!args.isConstructing() ||
822 &args.newTarget().toObject() == &args.callee()) {
823 MOZ_ASSERT(args.callee().hasSameRealmAs(cx));
824 proto.set(nullptr);
825 return true;
828 // We're calling this constructor from a derived class, retrieve the
829 // actual prototype from newTarget.
830 RootedObject newTarget(cx, &args.newTarget().toObject());
831 return GetPrototypeFromConstructor(cx, newTarget, intrinsicDefaultProto,
832 proto);
835 /* ES6 draft rev 32 (2015 Feb 2) 6.2.4.5 ToPropertyDescriptor(Obj) */
836 bool ToPropertyDescriptor(JSContext* cx, HandleValue descval,
837 bool checkAccessors,
838 MutableHandle<JS::PropertyDescriptor> desc);
841 * Throw a TypeError if desc.getter() or setter() is not
842 * callable. This performs exactly the checks omitted by ToPropertyDescriptor
843 * when checkAccessors is false.
845 Result<> CheckPropertyDescriptorAccessors(JSContext* cx,
846 Handle<JS::PropertyDescriptor> desc);
848 void CompletePropertyDescriptor(MutableHandle<JS::PropertyDescriptor> desc);
851 * Read property descriptors from props, as for Object.defineProperties. See
852 * ES5 15.2.3.7 steps 3-5.
854 extern bool ReadPropertyDescriptors(
855 JSContext* cx, HandleObject props, bool checkAccessors,
856 MutableHandleIdVector ids, MutableHandle<PropertyDescriptorVector> descs);
858 /* Read the name using a dynamic lookup on the scopeChain. */
859 extern bool LookupName(JSContext* cx, Handle<PropertyName*> name,
860 HandleObject scopeChain, MutableHandleObject objp,
861 MutableHandleObject pobjp, PropertyResult* propp);
863 extern bool LookupNameNoGC(JSContext* cx, PropertyName* name,
864 JSObject* scopeChain, JSObject** objp,
865 NativeObject** pobjp, PropertyResult* propp);
868 * Like LookupName except returns the global object if 'name' is not found in
869 * any preceding scope.
871 * Additionally, pobjp and propp are not needed by callers so they are not
872 * returned.
874 extern bool LookupNameWithGlobalDefault(JSContext* cx,
875 Handle<PropertyName*> name,
876 HandleObject scopeChain,
877 MutableHandleObject objp);
880 * Like LookupName except returns the unqualified var object if 'name' is not
881 * found in any preceding scope. Normally the unqualified var object is the
882 * global. If the value for the name in the looked-up scope is an
883 * uninitialized lexical, an UninitializedLexicalObject is returned.
885 * Additionally, pobjp is not needed by callers so it is not returned.
887 extern bool LookupNameUnqualified(JSContext* cx, Handle<PropertyName*> name,
888 HandleObject scopeChain,
889 MutableHandleObject objp);
891 } // namespace js
893 namespace js {
896 * Family of Pure property lookup functions. The bool return does NOT have the
897 * standard SpiderMonkey semantics. The return value means "can this operation
898 * be performed and produce a valid result without any side effects?". If any of
899 * these return true, then the outparam can be inspected to determine the
900 * result.
903 bool LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id,
904 NativeObject** objp, PropertyResult* propp);
906 bool LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id,
907 PropertyResult* propp);
909 bool GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp);
911 bool GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp,
912 bool* found);
914 bool GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp);
916 bool GetOwnGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp);
918 bool GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id,
919 JSNative* native);
921 bool HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id,
922 bool* result);
925 * Like JS::FromPropertyDescriptor, but ignore desc.object() and always set vp
926 * to an object on success.
928 * Use JS::FromPropertyDescriptor for getOwnPropertyDescriptor, since
929 * desc.object() is used to indicate whether a result was found or not. Use
930 * this instead for defineProperty: it would be senseless to define a "missing"
931 * property.
933 extern bool FromPropertyDescriptorToObject(JSContext* cx,
934 Handle<JS::PropertyDescriptor> desc,
935 MutableHandleValue vp);
937 // obj is a JSObject*, but we root it immediately up front. We do it
938 // that way because we need a Rooted temporary in this method anyway.
939 extern bool IsPrototypeOf(JSContext* cx, HandleObject protoObj, JSObject* obj,
940 bool* result);
942 /* Wrap boolean, number or string as Boolean, Number or String object. */
943 extern JSObject* PrimitiveToObject(JSContext* cx, const Value& v);
944 extern JSProtoKey PrimitiveToProtoKey(JSContext* cx, const Value& v);
946 } /* namespace js */
948 namespace js {
950 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
951 int valIndex, HandleId key);
952 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
953 int valIndex,
954 Handle<PropertyName*> key);
955 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
956 int valIndex, HandleValue keyValue);
958 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess(JSContext* cx,
959 HandleValue vp,
960 int vpIndex,
961 HandleId key) {
962 if (vp.isObject()) {
963 return &vp.toObject();
965 return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key);
967 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess(
968 JSContext* cx, HandleValue vp, int vpIndex, Handle<PropertyName*> key) {
969 if (vp.isObject()) {
970 return &vp.toObject();
972 return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key);
974 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess(
975 JSContext* cx, HandleValue vp, int vpIndex, HandleValue key) {
976 if (vp.isObject()) {
977 return &vp.toObject();
979 return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key);
983 * Report a TypeError: "so-and-so is not an object".
984 * Using NotNullObject is usually less code.
986 extern void ReportNotObject(JSContext* cx, const Value& v);
988 inline JSObject* RequireObject(JSContext* cx, HandleValue v) {
989 if (v.isObject()) {
990 return &v.toObject();
992 ReportNotObject(cx, v);
993 return nullptr;
997 * Report a TypeError: "SOMETHING must be an object, got VALUE".
998 * Using NotNullObject is usually less code.
1000 * By default this function will attempt to report the expression which computed
1001 * the value which given as argument. This can be disabled by using
1002 * JSDVG_IGNORE_STACK.
1004 extern void ReportNotObject(JSContext* cx, JSErrNum err, int spindex,
1005 HandleValue v);
1007 inline JSObject* RequireObject(JSContext* cx, JSErrNum err, int spindex,
1008 HandleValue v) {
1009 if (v.isObject()) {
1010 return &v.toObject();
1012 ReportNotObject(cx, err, spindex, v);
1013 return nullptr;
1016 extern void ReportNotObject(JSContext* cx, JSErrNum err, HandleValue v);
1018 inline JSObject* RequireObject(JSContext* cx, JSErrNum err, HandleValue v) {
1019 if (v.isObject()) {
1020 return &v.toObject();
1022 ReportNotObject(cx, err, v);
1023 return nullptr;
1027 * Report a TypeError: "N-th argument of FUN must be an object, got VALUE".
1028 * Using NotNullObjectArg is usually less code.
1030 extern void ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun,
1031 HandleValue v);
1033 inline JSObject* RequireObjectArg(JSContext* cx, const char* nth,
1034 const char* fun, HandleValue v) {
1035 if (v.isObject()) {
1036 return &v.toObject();
1038 ReportNotObjectArg(cx, nth, fun, v);
1039 return nullptr;
1042 extern bool GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args,
1043 const char* method,
1044 MutableHandleObject objp);
1046 /* Helper for throwing, always returns false. */
1047 extern bool Throw(JSContext* cx, HandleId id, unsigned errorNumber,
1048 const char* details = nullptr);
1051 * ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each
1052 * of obj's own properties' attributes appropriately: each property becomes
1053 * non-configurable, and if level == Frozen, data properties become
1054 * non-writable as well.
1056 extern bool SetIntegrityLevel(JSContext* cx, HandleObject obj,
1057 IntegrityLevel level);
1059 inline bool FreezeObject(JSContext* cx, HandleObject obj) {
1060 return SetIntegrityLevel(cx, obj, IntegrityLevel::Frozen);
1064 * ES6 rev 29 (6 Dec 2014) 7.3.14. Code shared by Object.isSealed and
1065 * Object.isFrozen.
1067 extern bool TestIntegrityLevel(JSContext* cx, HandleObject obj,
1068 IntegrityLevel level, bool* resultp);
1070 [[nodiscard]] extern JSObject* SpeciesConstructor(
1071 JSContext* cx, HandleObject obj, HandleObject defaultCtor,
1072 bool (*isDefaultSpecies)(JSContext*, JSFunction*));
1074 [[nodiscard]] extern JSObject* SpeciesConstructor(
1075 JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
1076 bool (*isDefaultSpecies)(JSContext*, JSFunction*));
1078 extern bool GetObjectFromIncumbentGlobal(JSContext* cx,
1079 MutableHandleObject obj);
1081 #ifdef DEBUG
1082 inline bool IsObjectValueInCompartment(const Value& v, JS::Compartment* comp) {
1083 if (!v.isObject()) {
1084 return true;
1086 return v.toObject().compartment() == comp;
1088 #endif
1091 * A generic trace hook that calls the object's 'trace' method.
1093 * If you are introducing a new JSObject subclass, MyObject, that needs a custom
1094 * JSClassOps::trace function, it's often helpful to write `trace` as a
1095 * non-static member function, since `this` will the correct type. In this case,
1096 * you can use `CallTraceMethod<MyObject>` as your JSClassOps::trace value.
1098 template <typename ObjectSubclass>
1099 void CallTraceMethod(JSTracer* trc, JSObject* obj) {
1100 obj->as<ObjectSubclass>().trace(trc);
1103 #ifdef JS_HAS_CTYPES
1105 namespace ctypes {
1107 extern size_t SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf,
1108 JSObject* obj);
1110 } // namespace ctypes
1112 #endif
1114 #ifdef DEBUG
1115 void AssertJSClassInvariants(const JSClass* clasp);
1116 #endif
1118 } /* namespace js */
1120 #endif /* vm_JSObject_h */