1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef vm_JSObject_h
8 #define vm_JSObject_h
9 
10 #include "mozilla/Maybe.h"
11 #include "mozilla/MemoryReporting.h"
12 
13 #include "gc/Barrier.h"
14 #include "js/Conversions.h"
15 #include "js/friend/ErrorMessages.h"  // JSErrNum
16 #include "js/GCVector.h"
17 #include "js/HeapAPI.h"
18 #include "js/shadow/Zone.h"  // JS::shadow::Zone
19 #include "js/Wrapper.h"
20 #include "vm/BytecodeUtil.h"
21 #include "vm/Printer.h"
22 #include "vm/PropertyResult.h"
23 #include "vm/Shape.h"
24 #include "vm/StringType.h"
25 #include "vm/Xdr.h"
26 
27 namespace JS {
28 struct ClassInfo;
29 }  // namespace JS
30 
31 namespace js {
32 
33 using PropertyDescriptorVector = JS::GCVector<JS::PropertyDescriptor>;
34 class GCMarker;
35 class Nursery;
36 struct AutoEnterOOMUnsafeRegion;
37 
38 namespace gc {
39 class RelocationOverlay;
40 }  // namespace gc
41 
42 /****************************************************************************/
43 
44 class GlobalObject;
45 class NativeObject;
46 class NewObjectCache;
47 
48 enum class IntegrityLevel { Sealed, Frozen };
49 
50 /*
51  * The NewObjectKind allows an allocation site to specify the lifetime
52  * requirements that must be fixed at allocation time.
53  */
54 enum NewObjectKind {
55   /* This is the default. Most objects are generic. */
56   GenericObject,
57 
58   /*
59    * Objects which will not benefit from being allocated in the nursery
60    * (e.g. because they are known to have a long lifetime) may be allocated
61    * with this kind to place them immediately into the tenured generation.
62    */
63   TenuredObject
64 };
65 
66 // Forward declarations, required for later friend declarations.
67 bool PreventExtensions(JSContext* cx, JS::HandleObject obj,
68                        JS::ObjectOpResult& result);
69 bool SetImmutablePrototype(JSContext* cx, JS::HandleObject obj,
70                            bool* succeeded);
71 
72 } /* namespace js */
73 
74 /*
75  * [SMDOC] JSObject layout
76  *
77  * A JavaScript object.
78  *
79  * This is the base class for all objects exposed to JS script (as well as some
80  * objects that are only accessed indirectly). Subclasses add additional fields
81  * and execution semantics. The runtime class of an arbitrary JSObject is
82  * identified by JSObject::getClass().
83  *
84  * All objects have a non-null Shape, stored in the cell header, which describes
85  * the current layout and set of property keys of the object.
86  *
87  * Each Shape has a pointer to a BaseShape. The BaseShape contains the object's
88  * prototype object, its class, and its realm.
89  *
90  * NOTE: Some operations can change the contents of an object (including class)
91  *       in-place so avoid assuming an object with same pointer has same class
92  *       as before.
93  *       - JSObject::swap()
94  */
95 class JSObject
96     : public js::gc::CellWithTenuredGCPointer<js::gc::Cell, js::Shape> {
97  public:
98   // The Shape is stored in the cell header.
shape()99   js::Shape* shape() const { return headerPtr(); }
100 
101 #ifndef JS_64BIT
102   // Ensure fixed slots have 8-byte alignment on 32-bit platforms.
103   uint32_t padding_;
104 #endif
105 
106  private:
107   friend class js::GCMarker;
108   friend class js::GlobalObject;
109   friend class js::NewObjectCache;
110   friend class js::Nursery;
111   friend class js::gc::RelocationOverlay;
112   friend bool js::PreventExtensions(JSContext* cx, JS::HandleObject obj,
113                                     JS::ObjectOpResult& result);
114   friend bool js::SetImmutablePrototype(JSContext* cx, JS::HandleObject obj,
115                                         bool* succeeded);
116 
117  public:
getClass()118   const JSClass* getClass() const { return shape()->getObjectClass(); }
hasClass(const JSClass * c)119   bool hasClass(const JSClass* c) const { return getClass() == c; }
120 
getOpsLookupProperty()121   js::LookupPropertyOp getOpsLookupProperty() const {
122     return getClass()->getOpsLookupProperty();
123   }
getOpsDefineProperty()124   js::DefinePropertyOp getOpsDefineProperty() const {
125     return getClass()->getOpsDefineProperty();
126   }
getOpsHasProperty()127   js::HasPropertyOp getOpsHasProperty() const {
128     return getClass()->getOpsHasProperty();
129   }
getOpsGetProperty()130   js::GetPropertyOp getOpsGetProperty() const {
131     return getClass()->getOpsGetProperty();
132   }
getOpsSetProperty()133   js::SetPropertyOp getOpsSetProperty() const {
134     return getClass()->getOpsSetProperty();
135   }
getOpsGetOwnPropertyDescriptor()136   js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor() const {
137     return getClass()->getOpsGetOwnPropertyDescriptor();
138   }
getOpsDeleteProperty()139   js::DeletePropertyOp getOpsDeleteProperty() const {
140     return getClass()->getOpsDeleteProperty();
141   }
getOpsGetElements()142   js::GetElementsOp getOpsGetElements() const {
143     return getClass()->getOpsGetElements();
144   }
getOpsFunToString()145   JSFunToStringOp getOpsFunToString() const {
146     return getClass()->getOpsFunToString();
147   }
148 
compartment()149   JS::Compartment* compartment() const { return shape()->compartment(); }
maybeCompartment()150   JS::Compartment* maybeCompartment() const { return compartment(); }
151 
initShape(js::Shape * shape)152   void initShape(js::Shape* shape) {
153     // Note: use Cell::Zone() instead of zone() because zone() relies on the
154     // shape we still have to initialize.
155     MOZ_ASSERT(Cell::zone() == shape->zone());
156     initHeaderPtr(shape);
157   }
setShape(js::Shape * shape)158   void setShape(js::Shape* shape) {
159     MOZ_ASSERT(maybeCCWRealm() == shape->realm());
160     setHeaderPtr(shape);
161   }
162 
fromShapeFieldPointer(uintptr_t p)163   static JSObject* fromShapeFieldPointer(uintptr_t p) {
164     return reinterpret_cast<JSObject*>(p - JSObject::offsetOfShape());
165   }
166 
167   static bool setFlag(JSContext* cx, JS::HandleObject obj, js::ObjectFlag flag);
168 
hasFlag(js::ObjectFlag flag)169   bool hasFlag(js::ObjectFlag flag) const {
170     return shape()->hasObjectFlag(flag);
171   }
172 
173   // Change this object's shape for a prototype mutation.
174   //
175   // Note: this does not reshape the proto chain to invalidate shape
176   // teleporting, check for an immutable proto, etc.
177   static bool setProtoUnchecked(JSContext* cx, JS::HandleObject obj,
178                                 js::Handle<js::TaggedProto> proto);
179 
180   // An object is marked IsUsedAsPrototype if it is (or was) another object's
181   // prototype. Optimization heuristics will make use of this flag.
182   //
183   // This flag is only relevant for static prototypes. Proxy traps can return
184   // objects without this flag set.
185   //
186   // NOTE: it's important to call setIsUsedAsPrototype *after* initializing the
187   // object's properties, because that avoids unnecessary shadowing checks and
188   // reshaping.
189   //
190   // See: ReshapeForProtoMutation, ReshapeForShadowedProp
isUsedAsPrototype()191   bool isUsedAsPrototype() const {
192     return hasFlag(js::ObjectFlag::IsUsedAsPrototype);
193   }
setIsUsedAsPrototype(JSContext * cx,JS::HandleObject obj)194   static bool setIsUsedAsPrototype(JSContext* cx, JS::HandleObject obj) {
195     return setFlag(cx, obj, js::ObjectFlag::IsUsedAsPrototype);
196   }
197 
198   inline bool isBoundFunction() const;
199 
200   // A "qualified" varobj is the object on which "qualified" variable
201   // declarations (i.e., those defined with "var") are kept.
202   //
203   // Conceptually, when a var binding is defined, it is defined on the
204   // innermost qualified varobj on the scope chain.
205   //
206   // Function scopes (CallObjects) are qualified varobjs, and there can be
207   // no other qualified varobj that is more inner for var bindings in that
208   // function. As such, all references to local var bindings in a function
209   // may be statically bound to the function scope. This is subject to
210   // further optimization. Unaliased bindings inside functions reside
211   // entirely on the frame, not in CallObjects.
212   //
213   // Global scopes are also qualified varobjs. It is possible to statically
214   // know, for a given script, that are no more inner qualified varobjs, so
215   // free variable references can be statically bound to the global.
216   //
217   // Finally, there are non-syntactic qualified varobjs used by embedders
218   // (e.g., Gecko and XPConnect), as they often wish to run scripts under a
219   // scope that captures var bindings.
220   inline bool isQualifiedVarObj() const;
setQualifiedVarObj(JSContext * cx,JS::HandleObject obj)221   static bool setQualifiedVarObj(JSContext* cx, JS::HandleObject obj) {
222     return setFlag(cx, obj, js::ObjectFlag::QualifiedVarObj);
223   }
224 
225   // An "unqualified" varobj is the object on which "unqualified"
226   // assignments (i.e., bareword assignments for which the LHS does not
227   // exist on the scope chain) are kept.
228   inline bool isUnqualifiedVarObj() const;
229 
230   // An object with an "uncacheable proto" is a prototype object that either had
231   // its own proto mutated or it was on the proto chain of an object that had
232   // its proto mutated. This is used to opt-out of the shape teleporting
233   // optimization. See: ReshapeForProtoMutation, ProtoChainSupportsTeleporting.
234   inline bool hasUncacheableProto() const;
setUncacheableProto(JSContext * cx,JS::HandleObject obj)235   static bool setUncacheableProto(JSContext* cx, JS::HandleObject obj) {
236     MOZ_ASSERT(obj->isUsedAsPrototype());
237     MOZ_ASSERT(obj->hasStaticPrototype(),
238                "uncacheability as a concept is only applicable to static "
239                "(not dynamically-computed) prototypes");
240     return setFlag(cx, obj, js::ObjectFlag::UncacheableProto);
241   }
242 
243   /*
244    * Whether there may be "interesting symbol" properties on this object. An
245    * interesting symbol is a symbol for which symbol->isInterestingSymbol()
246    * returns true.
247    */
248   MOZ_ALWAYS_INLINE bool maybeHasInterestingSymbolProperty() const;
249 
250   /* GC support. */
251 
252   void traceChildren(JSTracer* trc);
253 
fixupAfterMovingGC()254   void fixupAfterMovingGC() {}
255 
256   static const JS::TraceKind TraceKind = JS::TraceKind::Object;
257 
zone()258   MOZ_ALWAYS_INLINE JS::Zone* zone() const {
259     MOZ_ASSERT_IF(!isTenured(), nurseryZone() == shape()->zone());
260     return shape()->zone();
261   }
shadowZone()262   MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZone() const {
263     return JS::shadow::Zone::from(zone());
264   }
zoneFromAnyThread()265   MOZ_ALWAYS_INLINE JS::Zone* zoneFromAnyThread() const {
266     MOZ_ASSERT_IF(!isTenured(),
267                   nurseryZoneFromAnyThread() == shape()->zoneFromAnyThread());
268     return shape()->zoneFromAnyThread();
269   }
shadowZoneFromAnyThread()270   MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZoneFromAnyThread() const {
271     return JS::shadow::Zone::from(zoneFromAnyThread());
272   }
postWriteBarrier(void * cellp,JSObject * prev,JSObject * next)273   static MOZ_ALWAYS_INLINE void postWriteBarrier(void* cellp, JSObject* prev,
274                                                  JSObject* next) {
275     js::gc::PostWriteBarrierImpl<JSObject>(cellp, prev, next);
276   }
277 
278   /* Return the allocKind we would use if we were to tenure this object. */
279   js::gc::AllocKind allocKindForTenure(const js::Nursery& nursery) const;
280 
tenuredSizeOfThis()281   size_t tenuredSizeOfThis() const {
282     MOZ_ASSERT(isTenured());
283     return js::gc::Arena::thingSize(asTenured().getAllocKind());
284   }
285 
286   void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
287                               JS::ClassInfo* info);
288 
289   // We can only use addSizeOfExcludingThis on tenured objects: it assumes it
290   // can apply mallocSizeOf to bits and pieces of the object, whereas objects
291   // in the nursery may have those bits and pieces allocated in the nursery
292   // along with them, and are not each their own malloc blocks.
293   size_t sizeOfIncludingThisInNursery() const;
294 
295 #ifdef DEBUG
296   static void debugCheckNewObject(js::Shape* shape, js::gc::AllocKind allocKind,
297                                   js::gc::InitialHeap heap);
298 #else
debugCheckNewObject(js::Shape * shape,js::gc::AllocKind allocKind,js::gc::InitialHeap heap)299   static void debugCheckNewObject(js::Shape* shape, js::gc::AllocKind allocKind,
300                                   js::gc::InitialHeap heap) {}
301 #endif
302 
303   /*
304    * We permit proxies to dynamically compute their prototype if desired.
305    * (Not all proxies will so desire: in particular, most DOM proxies can
306    * track their prototype with a single, nullable JSObject*.)  If a proxy
307    * so desires, we store (JSObject*)0x1 in the proto field of the object's
308    * group.
309    *
310    * We offer three ways to get an object's prototype:
311    *
312    * 1. obj->staticPrototype() returns the prototype, but it asserts if obj
313    *    is a proxy, and the proxy has opted to dynamically compute its
314    *    prototype using a getPrototype() handler.
315    * 2. obj->taggedProto() returns a TaggedProto, which can be tested to
316    *    check if the proto is an object, nullptr, or lazily computed.
317    * 3. js::GetPrototype(cx, obj, &proto) computes the proto of an object.
318    *    If obj is a proxy with dynamically-computed prototype, this code may
319    *    perform arbitrary behavior (allocation, GC, run JS) while computing
320    *    the proto.
321    */
322 
taggedProto()323   js::TaggedProto taggedProto() const { return shape()->proto(); }
324 
325   bool uninlinedIsProxyObject() const;
326 
staticPrototype()327   JSObject* staticPrototype() const {
328     MOZ_ASSERT(hasStaticPrototype());
329     return taggedProto().toObjectOrNull();
330   }
331 
332   // Normal objects and a subset of proxies have an uninteresting, static
333   // (albeit perhaps mutable) [[Prototype]].  For such objects the
334   // [[Prototype]] is just a value returned when needed for accesses, or
335   // modified in response to requests.  These objects store the
336   // [[Prototype]] directly within |obj->group()|.
hasStaticPrototype()337   bool hasStaticPrototype() const { return !hasDynamicPrototype(); }
338 
339   // The remaining proxies have a [[Prototype]] requiring dynamic computation
340   // for every access, going through the proxy handler {get,set}Prototype and
341   // setImmutablePrototype methods.  (Wrappers particularly use this to keep
342   // the wrapper/wrappee [[Prototype]]s consistent.)
hasDynamicPrototype()343   bool hasDynamicPrototype() const {
344     bool dynamic = taggedProto().isDynamic();
345     MOZ_ASSERT_IF(dynamic, uninlinedIsProxyObject());
346     return dynamic;
347   }
348 
349   // True iff this object's [[Prototype]] is immutable.  Must be called only
350   // on objects with a static [[Prototype]]!
351   inline bool staticPrototypeIsImmutable() const;
352 
353   /*
354    * Environment chains.
355    *
356    * The environment chain of an object is the link in the search path when
357    * a script does a name lookup on an environment object. For JS internal
358    * environment objects --- Call, LexicalEnvironment, and WithEnvironment
359    * --- the chain is stored in the first fixed slot of the object.  For
360    * other environment objects, the chain goes directly to the global.
361    *
362    * In code which is not marked hasNonSyntacticScope, environment chains
363    * can contain only syntactic environment objects (see
364    * IsSyntacticEnvironment) with a global object at the root as the
365    * environment of the outermost non-function script. In
366    * hasNonSyntacticScope code, the environment of the outermost
367    * non-function script might not be a global object, and can have a mix of
368    * other objects above it before the global object is reached.
369    */
370 
371   /*
372    * Get the enclosing environment of an object. When called on a
373    * non-EnvironmentObject, this will just be the global (the name
374    * "enclosing environment" still applies in this situation because
375    * non-EnvironmentObjects can be on the environment chain).
376    */
377   inline JSObject* enclosingEnvironment() const;
378 
379   // Cross-compartment wrappers are not associated with a single realm/global,
380   // so these methods assert the object is not a CCW.
381   inline js::GlobalObject& nonCCWGlobal() const;
382 
nonCCWRealm()383   JS::Realm* nonCCWRealm() const {
384     MOZ_ASSERT(!js::UninlinedIsCrossCompartmentWrapper(this));
385     return shape()->realm();
386   }
387   bool hasSameRealmAs(JSContext* cx) const;
388 
389   // Returns the object's realm even if the object is a CCW (be careful, in
390   // this case the realm is not very meaningful because wrappers are shared by
391   // all realms in the compartment).
maybeCCWRealm()392   JS::Realm* maybeCCWRealm() const { return shape()->realm(); }
393 
394   /*
395    * ES5 meta-object properties and operations.
396    */
397 
398  public:
399   // Indicates whether a non-proxy is extensible.  Don't call on proxies!
400   // This method really shouldn't exist -- but there are a few internal
401   // places that want it (JITs and the like), and it'd be a pain to mark them
402   // all as friends.
403   inline bool nonProxyIsExtensible() const;
404   bool uninlinedNonProxyIsExtensible() const;
405 
406  public:
407   /*
408    * Back to generic stuff.
409    */
410   MOZ_ALWAYS_INLINE bool isCallable() const;
411   MOZ_ALWAYS_INLINE bool isConstructor() const;
412   MOZ_ALWAYS_INLINE JSNative callHook() const;
413   MOZ_ALWAYS_INLINE JSNative constructHook() const;
414 
415   MOZ_ALWAYS_INLINE void finalize(JSFreeOp* fop);
416 
417  public:
418   static bool nonNativeSetProperty(JSContext* cx, js::HandleObject obj,
419                                    js::HandleId id, js::HandleValue v,
420                                    js::HandleValue receiver,
421                                    JS::ObjectOpResult& result);
422   static bool nonNativeSetElement(JSContext* cx, js::HandleObject obj,
423                                   uint32_t index, js::HandleValue v,
424                                   js::HandleValue receiver,
425                                   JS::ObjectOpResult& result);
426 
427   static void swap(JSContext* cx, JS::HandleObject a, JS::HandleObject b,
428                    js::AutoEnterOOMUnsafeRegion& oomUnsafe);
429 
430   /*
431    * In addition to the generic object interface provided by JSObject,
432    * specific types of objects may provide additional operations. To access,
433    * these addition operations, callers should use the pattern:
434    *
435    *   if (obj.is<XObject>()) {
436    *     XObject& x = obj.as<XObject>();
437    *     x.foo();
438    *   }
439    *
440    * These XObject classes form a hierarchy. For example, for a cloned block
441    * object, the following predicates are true: is<ClonedBlockObject>,
442    * is<NestedScopeObject> and is<ScopeObject>. Each of these has a
443    * respective class that derives and adds operations.
444    *
445    * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file
446    * triplet (along with any class YObject that derives XObject).
447    *
448    * Note that X represents a low-level representation and does not query the
449    * [[Class]] property of object defined by the spec: use |JS::GetBuiltinClass|
450    * for this.
451    */
452 
453   template <class T>
is()454   inline bool is() const {
455     return getClass() == &T::class_;
456   }
457 
458   template <class T>
as()459   T& as() {
460     MOZ_ASSERT(this->is<T>());
461     return *static_cast<T*>(this);
462   }
463 
464   template <class T>
as()465   const T& as() const {
466     MOZ_ASSERT(this->is<T>());
467     return *static_cast<const T*>(this);
468   }
469 
470   /*
471    * True if either this or CheckedUnwrap(this) is an object of class T.
472    * (Only two objects are checked, regardless of how many wrappers there
473    * are.)
474    *
475    * /!\ Note: This can be true at one point, but false later for the same
476    * object, thanks to js::NukeCrossCompartmentWrapper and friends.
477    */
478   template <class T>
479   bool canUnwrapAs();
480 
481   /*
482    * Unwrap and downcast to class T.
483    *
484    * Precondition: `this->canUnwrapAs<T>()`. Note that it's not enough to
485    * have checked this at some point in the past; if there's any doubt as to
486    * whether js::Nuke* could have been called in the meantime, check again.
487    */
488   template <class T>
489   T& unwrapAs();
490 
491   /*
492    * Tries to unwrap and downcast to class T. Returns nullptr if (and only if) a
493    * wrapper with a security policy is involved. Crashes in all builds if the
494    * (possibly unwrapped) object is not of class T (for example, because it's a
495    * dead wrapper).
496    */
497   template <class T>
498   inline T* maybeUnwrapAs();
499 
500   /*
501    * Tries to unwrap and downcast to an object with class |clasp|.  Returns
502    * nullptr if (and only if) a wrapper with a security policy is involved.
503    * Crashes in all builds if the (possibly unwrapped) object doesn't have class
504    * |clasp| (for example, because it's a dead wrapper).
505    */
506   inline JSObject* maybeUnwrapAs(const JSClass* clasp);
507 
508   /*
509    * Tries to unwrap and downcast to class T. Returns nullptr if a wrapper with
510    * a security policy is involved or if the object does not have class T.
511    */
512   template <class T>
513   T* maybeUnwrapIf();
514 
515 #if defined(DEBUG) || defined(JS_JITSPEW)
516   void dump(js::GenericPrinter& fp) const;
517   void dump() const;
518 #endif
519 
520   // Maximum size in bytes of a JSObject.
521 #ifdef JS_64BIT
522   static constexpr size_t MAX_BYTE_SIZE =
523       3 * sizeof(void*) + 16 * sizeof(JS::Value);
524 #else
525   static constexpr size_t MAX_BYTE_SIZE =
526       4 * sizeof(void*) + 16 * sizeof(JS::Value);
527 #endif
528 
529  protected:
530   // JIT Accessors.
531   //
532   // To help avoid writing Spectre-unsafe code, we only allow MacroAssembler
533   // to call the method below.
534   friend class js::jit::MacroAssembler;
535 
offsetOfShape()536   static constexpr size_t offsetOfShape() { return offsetOfHeaderPtr(); }
537 
538  private:
539   JSObject() = delete;
540   JSObject(const JSObject& other) = delete;
541   void operator=(const JSObject& other) = delete;
542 };
543 
544 template <>
545 inline bool JSObject::is<JSObject>() const {
546   return true;
547 }
548 
549 template <typename Wrapper>
550 template <typename U>
as()551 MOZ_ALWAYS_INLINE JS::Handle<U*> js::RootedBase<JSObject*, Wrapper>::as()
552     const {
553   const Wrapper& self = *static_cast<const Wrapper*>(this);
554   MOZ_ASSERT(self->template is<U>());
555   return Handle<U*>::fromMarkedLocation(
556       reinterpret_cast<U* const*>(self.address()));
557 }
558 
559 template <typename Wrapper>
560 template <class U>
as()561 MOZ_ALWAYS_INLINE JS::Handle<U*> js::HandleBase<JSObject*, Wrapper>::as()
562     const {
563   const JS::Handle<JSObject*>& self =
564       *static_cast<const JS::Handle<JSObject*>*>(this);
565   MOZ_ASSERT(self->template is<U>());
566   return Handle<U*>::fromMarkedLocation(
567       reinterpret_cast<U* const*>(self.address()));
568 }
569 
570 template <class T>
canUnwrapAs()571 bool JSObject::canUnwrapAs() {
572   static_assert(!std::is_convertible_v<T*, js::Wrapper*>,
573                 "T can't be a Wrapper type; this function discards wrappers");
574 
575   if (is<T>()) {
576     return true;
577   }
578   JSObject* obj = js::CheckedUnwrapStatic(this);
579   return obj && obj->is<T>();
580 }
581 
582 template <class T>
unwrapAs()583 T& JSObject::unwrapAs() {
584   static_assert(!std::is_convertible_v<T*, js::Wrapper*>,
585                 "T can't be a Wrapper type; this function discards wrappers");
586 
587   if (is<T>()) {
588     return as<T>();
589   }
590 
591   // Since the caller just called canUnwrapAs<T>(), which does a
592   // CheckedUnwrap, this does not need to repeat the security check.
593   JSObject* unwrapped = js::UncheckedUnwrap(this);
594   MOZ_ASSERT(js::CheckedUnwrapStatic(this) == unwrapped,
595              "check that the security check we skipped really is redundant");
596   return unwrapped->as<T>();
597 }
598 
599 template <class T>
maybeUnwrapAs()600 inline T* JSObject::maybeUnwrapAs() {
601   static_assert(!std::is_convertible_v<T*, js::Wrapper*>,
602                 "T can't be a Wrapper type; this function discards wrappers");
603 
604   if (is<T>()) {
605     return &as<T>();
606   }
607 
608   JSObject* unwrapped = js::CheckedUnwrapStatic(this);
609   if (!unwrapped) {
610     return nullptr;
611   }
612 
613   if (MOZ_LIKELY(unwrapped->is<T>())) {
614     return &unwrapped->as<T>();
615   }
616 
617   MOZ_CRASH("Invalid object. Dead wrapper?");
618 }
619 
maybeUnwrapAs(const JSClass * clasp)620 inline JSObject* JSObject::maybeUnwrapAs(const JSClass* clasp) {
621   if (hasClass(clasp)) {
622     return this;
623   }
624 
625   JSObject* unwrapped = js::CheckedUnwrapStatic(this);
626   if (!unwrapped) {
627     return nullptr;
628   }
629 
630   if (MOZ_LIKELY(unwrapped->hasClass(clasp))) {
631     return unwrapped;
632   }
633 
634   MOZ_CRASH("Invalid object. Dead wrapper?");
635 }
636 
637 template <class T>
maybeUnwrapIf()638 T* JSObject::maybeUnwrapIf() {
639   static_assert(!std::is_convertible_v<T*, js::Wrapper*>,
640                 "T can't be a Wrapper type; this function discards wrappers");
641 
642   if (is<T>()) {
643     return &as<T>();
644   }
645 
646   JSObject* unwrapped = js::CheckedUnwrapStatic(this);
647   return (unwrapped && unwrapped->is<T>()) ? &unwrapped->as<T>() : nullptr;
648 }
649 
650 /*
651  * The only sensible way to compare JSObject with == is by identity. We use
652  * const& instead of * as a syntactic way to assert non-null. This leads to an
653  * abundance of address-of operators to identity. Hence this overload.
654  */
655 static MOZ_ALWAYS_INLINE bool operator==(const JSObject& lhs,
656                                          const JSObject& rhs) {
657   return &lhs == &rhs;
658 }
659 
660 static MOZ_ALWAYS_INLINE bool operator!=(const JSObject& lhs,
661                                          const JSObject& rhs) {
662   return &lhs != &rhs;
663 }
664 
665 // Size of the various GC thing allocation sizes used for objects.
666 struct JSObject_Slots0 : JSObject {
667   void* data[2];
668 };
669 struct JSObject_Slots2 : JSObject {
670   void* data[2];
671   js::Value fslots[2];
672 };
673 struct JSObject_Slots4 : JSObject {
674   void* data[2];
675   js::Value fslots[4];
676 };
677 struct JSObject_Slots8 : JSObject {
678   void* data[2];
679   js::Value fslots[8];
680 };
681 struct JSObject_Slots12 : JSObject {
682   void* data[2];
683   js::Value fslots[12];
684 };
685 struct JSObject_Slots16 : JSObject {
686   void* data[2];
687   js::Value fslots[16];
688 };
689 
690 namespace js {
691 
692 // Returns true if object may possibly use JSObject::swap. The JITs may better
693 // optimize objects that can never swap (and thus change their type).
694 //
695 // If ObjectMayBeSwapped is false, it is safe to guard on pointer identity to
696 // test immutable features of the object. For example, the target of a
697 // JSFunction will not change. Note: the object can still be moved by GC.
698 extern bool ObjectMayBeSwapped(const JSObject* obj);
699 
700 /**
701  * This enum is used to select whether the defined functions should be marked as
702  * builtin native instrinsics for self-hosted code.
703  */
704 enum DefineAsIntrinsic { NotIntrinsic, AsIntrinsic };
705 
706 extern bool DefineFunctions(JSContext* cx, HandleObject obj,
707                             const JSFunctionSpec* fs,
708                             DefineAsIntrinsic intrinsic);
709 
710 /* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
711 extern bool ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
712 
ToPrimitive(JSContext * cx,MutableHandleValue vp)713 inline bool ToPrimitive(JSContext* cx, MutableHandleValue vp) {
714   if (vp.isPrimitive()) {
715     return true;
716   }
717   return ToPrimitiveSlow(cx, JSTYPE_UNDEFINED, vp);
718 }
719 
ToPrimitive(JSContext * cx,JSType preferredType,MutableHandleValue vp)720 inline bool ToPrimitive(JSContext* cx, JSType preferredType,
721                         MutableHandleValue vp) {
722   if (vp.isPrimitive()) {
723     return true;
724   }
725   return ToPrimitiveSlow(cx, preferredType, vp);
726 }
727 
728 /*
729  * toString support. (This isn't called GetClassName because there's a macro in
730  * <windows.h> with that name.)
731  */
732 MOZ_ALWAYS_INLINE const char* GetObjectClassName(JSContext* cx,
733                                                  HandleObject obj);
734 
735 /*
736  * Prepare a |this| object to be returned to script. This includes replacing
737  * Windows with their corresponding WindowProxy.
738  *
739  * Helpers are also provided to first extract the |this| from specific
740  * types of environment.
741  */
742 JSObject* GetThisObject(JSObject* obj);
743 
744 JSObject* GetThisObjectOfLexical(JSObject* env);
745 
746 JSObject* GetThisObjectOfWith(JSObject* env);
747 
748 } /* namespace js */
749 
750 namespace js {
751 
752 bool NewObjectWithTaggedProtoIsCachable(JSContext* cx,
753                                         Handle<TaggedProto> proto,
754                                         NewObjectKind newKind,
755                                         const JSClass* clasp);
756 
757 // ES6 9.1.15 GetPrototypeFromConstructor.
758 extern bool GetPrototypeFromConstructor(JSContext* cx,
759                                         js::HandleObject newTarget,
760                                         JSProtoKey intrinsicDefaultProto,
761                                         js::MutableHandleObject proto);
762 
763 // https://tc39.github.io/ecma262/#sec-getprototypefromconstructor
764 //
765 // Determine which [[Prototype]] to use when creating a new object using a
766 // builtin constructor.
767 //
768 // This sets `proto` to `nullptr` to mean "the builtin prototype object for
769 // this type in the current realm", the common case.
770 //
771 // We could set it to `cx->global()->getOrCreatePrototype(protoKey)`, but
772 // nullptr gets a fast path in e.g. js::NewObjectWithClassProtoCommon.
773 //
774 // intrinsicDefaultProto can be JSProto_Null if there's no appropriate
775 // JSProtoKey enum; but we then select the wrong prototype object in a
776 // multi-realm corner case (see bug 1515167).
GetPrototypeFromBuiltinConstructor(JSContext * cx,const CallArgs & args,JSProtoKey intrinsicDefaultProto,js::MutableHandleObject proto)777 MOZ_ALWAYS_INLINE bool GetPrototypeFromBuiltinConstructor(
778     JSContext* cx, const CallArgs& args, JSProtoKey intrinsicDefaultProto,
779     js::MutableHandleObject proto) {
780   // We can skip the "prototype" lookup in the two common cases:
781   // 1.  Builtin constructor called without `new`, as in `obj = Object();`.
782   // 2.  Builtin constructor called with `new`, as in `obj = new Object();`.
783   //
784   // Cases that can't take the fast path include `new MySubclassOfObject()`,
785   // `new otherGlobal.Object()`, and `Reflect.construct(Object, [], Date)`.
786   if (!args.isConstructing() ||
787       &args.newTarget().toObject() == &args.callee()) {
788     MOZ_ASSERT(args.callee().hasSameRealmAs(cx));
789     proto.set(nullptr);
790     return true;
791   }
792 
793   // We're calling this constructor from a derived class, retrieve the
794   // actual prototype from newTarget.
795   RootedObject newTarget(cx, &args.newTarget().toObject());
796   return GetPrototypeFromConstructor(cx, newTarget, intrinsicDefaultProto,
797                                      proto);
798 }
799 
800 // Generic call for constructing |this|.
801 extern JSObject* CreateThis(JSContext* cx, const JSClass* clasp,
802                             js::HandleObject callee);
803 
804 extern JSObject* DeepCloneObjectLiteral(JSContext* cx, HandleObject obj);
805 
806 /* ES6 draft rev 32 (2015 Feb 2) 6.2.4.5 ToPropertyDescriptor(Obj) */
807 bool ToPropertyDescriptor(JSContext* cx, HandleValue descval,
808                           bool checkAccessors,
809                           MutableHandle<JS::PropertyDescriptor> desc);
810 
811 /*
812  * Throw a TypeError if desc.getter() or setter() is not
813  * callable. This performs exactly the checks omitted by ToPropertyDescriptor
814  * when checkAccessors is false.
815  */
816 Result<> CheckPropertyDescriptorAccessors(JSContext* cx,
817                                           Handle<JS::PropertyDescriptor> desc);
818 
819 void CompletePropertyDescriptor(MutableHandle<JS::PropertyDescriptor> desc);
820 
821 /*
822  * Read property descriptors from props, as for Object.defineProperties. See
823  * ES5 15.2.3.7 steps 3-5.
824  */
825 extern bool ReadPropertyDescriptors(
826     JSContext* cx, HandleObject props, bool checkAccessors,
827     MutableHandleIdVector ids, MutableHandle<PropertyDescriptorVector> descs);
828 
829 /* Read the name using a dynamic lookup on the scopeChain. */
830 extern bool LookupName(JSContext* cx, HandlePropertyName name,
831                        HandleObject scopeChain, MutableHandleObject objp,
832                        MutableHandleObject pobjp, PropertyResult* propp);
833 
834 extern bool LookupNameNoGC(JSContext* cx, PropertyName* name,
835                            JSObject* scopeChain, JSObject** objp,
836                            NativeObject** pobjp, PropertyResult* propp);
837 
838 /*
839  * Like LookupName except returns the global object if 'name' is not found in
840  * any preceding scope.
841  *
842  * Additionally, pobjp and propp are not needed by callers so they are not
843  * returned.
844  */
845 extern bool LookupNameWithGlobalDefault(JSContext* cx, HandlePropertyName name,
846                                         HandleObject scopeChain,
847                                         MutableHandleObject objp);
848 
849 /*
850  * Like LookupName except returns the unqualified var object if 'name' is not
851  * found in any preceding scope. Normally the unqualified var object is the
852  * global. If the value for the name in the looked-up scope is an
853  * uninitialized lexical, an UninitializedLexicalObject is returned.
854  *
855  * Additionally, pobjp is not needed by callers so it is not returned.
856  */
857 extern bool LookupNameUnqualified(JSContext* cx, HandlePropertyName name,
858                                   HandleObject scopeChain,
859                                   MutableHandleObject objp);
860 
861 }  // namespace js
862 
863 namespace js {
864 
865 bool LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id,
866                         NativeObject** objp, PropertyResult* propp);
867 
868 bool LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id,
869                            PropertyResult* propp);
870 
871 bool GetPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp);
872 
873 bool GetOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, Value* vp,
874                         bool* found);
875 
876 bool GetGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp);
877 
878 bool GetOwnGetterPure(JSContext* cx, JSObject* obj, jsid id, JSFunction** fp);
879 
880 bool GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id,
881                             JSNative* native);
882 
883 bool HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id,
884                             bool* result);
885 
886 /*
887  * Like JS::FromPropertyDescriptor, but ignore desc.object() and always set vp
888  * to an object on success.
889  *
890  * Use JS::FromPropertyDescriptor for getOwnPropertyDescriptor, since
891  * desc.object() is used to indicate whether a result was found or not.  Use
892  * this instead for defineProperty: it would be senseless to define a "missing"
893  * property.
894  */
895 extern bool FromPropertyDescriptorToObject(JSContext* cx,
896                                            Handle<JS::PropertyDescriptor> desc,
897                                            MutableHandleValue vp);
898 
899 // obj is a JSObject*, but we root it immediately up front. We do it
900 // that way because we need a Rooted temporary in this method anyway.
901 extern bool IsPrototypeOf(JSContext* cx, HandleObject protoObj, JSObject* obj,
902                           bool* result);
903 
904 /* Wrap boolean, number or string as Boolean, Number or String object. */
905 extern JSObject* PrimitiveToObject(JSContext* cx, const Value& v);
906 extern JSProtoKey PrimitiveToProtoKey(JSContext* cx, const Value& v);
907 
908 } /* namespace js */
909 
910 namespace js {
911 
912 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
913                                         int valIndex, HandleId key);
914 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
915                                         int valIndex, HandlePropertyName key);
916 JSObject* ToObjectSlowForPropertyAccess(JSContext* cx, JS::HandleValue val,
917                                         int valIndex, HandleValue keyValue);
918 
ToObjectFromStackForPropertyAccess(JSContext * cx,HandleValue vp,int vpIndex,HandleId key)919 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess(JSContext* cx,
920                                                                HandleValue vp,
921                                                                int vpIndex,
922                                                                HandleId key) {
923   if (vp.isObject()) {
924     return &vp.toObject();
925   }
926   return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key);
927 }
ToObjectFromStackForPropertyAccess(JSContext * cx,HandleValue vp,int vpIndex,HandlePropertyName key)928 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess(
929     JSContext* cx, HandleValue vp, int vpIndex, HandlePropertyName key) {
930   if (vp.isObject()) {
931     return &vp.toObject();
932   }
933   return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key);
934 }
ToObjectFromStackForPropertyAccess(JSContext * cx,HandleValue vp,int vpIndex,HandleValue key)935 MOZ_ALWAYS_INLINE JSObject* ToObjectFromStackForPropertyAccess(
936     JSContext* cx, HandleValue vp, int vpIndex, HandleValue key) {
937   if (vp.isObject()) {
938     return &vp.toObject();
939   }
940   return js::ToObjectSlowForPropertyAccess(cx, vp, vpIndex, key);
941 }
942 
943 template <XDRMode mode>
944 XDRResult XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
945 
946 /*
947  * Report a TypeError: "so-and-so is not an object".
948  * Using NotNullObject is usually less code.
949  */
950 extern void ReportNotObject(JSContext* cx, const Value& v);
951 
RequireObject(JSContext * cx,HandleValue v)952 inline JSObject* RequireObject(JSContext* cx, HandleValue v) {
953   if (v.isObject()) {
954     return &v.toObject();
955   }
956   ReportNotObject(cx, v);
957   return nullptr;
958 }
959 
960 /*
961  * Report a TypeError: "SOMETHING must be an object, got VALUE".
962  * Using NotNullObject is usually less code.
963  *
964  * By default this function will attempt to report the expression which computed
965  * the value which given as argument. This can be disabled by using
966  * JSDVG_IGNORE_STACK.
967  */
968 extern void ReportNotObject(JSContext* cx, JSErrNum err, int spindex,
969                             HandleValue v);
970 
RequireObject(JSContext * cx,JSErrNum err,int spindex,HandleValue v)971 inline JSObject* RequireObject(JSContext* cx, JSErrNum err, int spindex,
972                                HandleValue v) {
973   if (v.isObject()) {
974     return &v.toObject();
975   }
976   ReportNotObject(cx, err, spindex, v);
977   return nullptr;
978 }
979 
980 extern void ReportNotObject(JSContext* cx, JSErrNum err, HandleValue v);
981 
RequireObject(JSContext * cx,JSErrNum err,HandleValue v)982 inline JSObject* RequireObject(JSContext* cx, JSErrNum err, HandleValue v) {
983   if (v.isObject()) {
984     return &v.toObject();
985   }
986   ReportNotObject(cx, err, v);
987   return nullptr;
988 }
989 
990 /*
991  * Report a TypeError: "N-th argument of FUN must be an object, got VALUE".
992  * Using NotNullObjectArg is usually less code.
993  */
994 extern void ReportNotObjectArg(JSContext* cx, const char* nth, const char* fun,
995                                HandleValue v);
996 
RequireObjectArg(JSContext * cx,const char * nth,const char * fun,HandleValue v)997 inline JSObject* RequireObjectArg(JSContext* cx, const char* nth,
998                                   const char* fun, HandleValue v) {
999   if (v.isObject()) {
1000     return &v.toObject();
1001   }
1002   ReportNotObjectArg(cx, nth, fun, v);
1003   return nullptr;
1004 }
1005 
1006 extern bool GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args,
1007                                      const char* method,
1008                                      MutableHandleObject objp);
1009 
1010 /* Helper for throwing, always returns false. */
1011 extern bool Throw(JSContext* cx, HandleId id, unsigned errorNumber,
1012                   const char* details = nullptr);
1013 
1014 /*
1015  * ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each
1016  * of obj's own properties' attributes appropriately: each property becomes
1017  * non-configurable, and if level == Frozen, data properties become
1018  * non-writable as well.
1019  */
1020 extern bool SetIntegrityLevel(JSContext* cx, HandleObject obj,
1021                               IntegrityLevel level);
1022 
FreezeObject(JSContext * cx,HandleObject obj)1023 inline bool FreezeObject(JSContext* cx, HandleObject obj) {
1024   return SetIntegrityLevel(cx, obj, IntegrityLevel::Frozen);
1025 }
1026 
1027 /*
1028  * ES6 rev 29 (6 Dec 2014) 7.3.14. Code shared by Object.isSealed and
1029  * Object.isFrozen.
1030  */
1031 extern bool TestIntegrityLevel(JSContext* cx, HandleObject obj,
1032                                IntegrityLevel level, bool* resultp);
1033 
1034 [[nodiscard]] extern JSObject* SpeciesConstructor(
1035     JSContext* cx, HandleObject obj, HandleObject defaultCtor,
1036     bool (*isDefaultSpecies)(JSContext*, JSFunction*));
1037 
1038 [[nodiscard]] extern JSObject* SpeciesConstructor(
1039     JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
1040     bool (*isDefaultSpecies)(JSContext*, JSFunction*));
1041 
1042 extern bool GetObjectFromIncumbentGlobal(JSContext* cx,
1043                                          MutableHandleObject obj);
1044 
1045 #ifdef DEBUG
IsObjectValueInCompartment(const Value & v,JS::Compartment * comp)1046 inline bool IsObjectValueInCompartment(const Value& v, JS::Compartment* comp) {
1047   if (!v.isObject()) {
1048     return true;
1049   }
1050   return v.toObject().compartment() == comp;
1051 }
1052 #endif
1053 
1054 /*
1055  * A generic trace hook that calls the object's 'trace' method.
1056  *
1057  * If you are introducing a new JSObject subclass, MyObject, that needs a custom
1058  * JSClassOps::trace function, it's often helpful to write `trace` as a
1059  * non-static member function, since `this` will the correct type. In this case,
1060  * you can use `CallTraceMethod<MyObject>` as your JSClassOps::trace value.
1061  */
1062 template <typename ObjectSubclass>
CallTraceMethod(JSTracer * trc,JSObject * obj)1063 void CallTraceMethod(JSTracer* trc, JSObject* obj) {
1064   obj->as<ObjectSubclass>().trace(trc);
1065 }
1066 
1067 #ifdef JS_HAS_CTYPES
1068 
1069 namespace ctypes {
1070 
1071 extern size_t SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf,
1072                                       JSObject* obj);
1073 
1074 }  // namespace ctypes
1075 
1076 #endif
1077 
1078 } /* namespace js */
1079 
1080 #endif /* vm_JSObject_h */
1081