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