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_inl_h
8 #define vm_JSObject_inl_h
9
10 #include "vm/JSObject.h"
11
12 #include "vm/ArrayObject.h"
13 #include "vm/EnvironmentObject.h"
14 #include "vm/JSFunction.h"
15 #include "vm/Probes.h"
16
17 #include "gc/FreeOp-inl.h"
18 #include "gc/Marking-inl.h"
19 #include "gc/ObjectKind-inl.h"
20 #include "vm/ObjectOperations-inl.h" // js::MaybeHasInterestingSymbolProperty
21 #include "vm/Realm-inl.h"
22
23 namespace js {
24
25 /*
26 * Get the GC kind to use for scripted 'new' on the given class.
27 * FIXME bug 547327: estimate the size from the allocation site.
28 */
NewObjectGCKind(const JSClass * clasp)29 static inline gc::AllocKind NewObjectGCKind(const JSClass* clasp) {
30 if (clasp == &ArrayObject::class_) {
31 return gc::AllocKind::OBJECT8;
32 }
33 if (clasp == &JSFunction::class_) {
34 return gc::AllocKind::OBJECT2;
35 }
36 return gc::AllocKind::OBJECT4;
37 }
38
39 } // namespace js
40
numDynamicSlots()41 MOZ_ALWAYS_INLINE uint32_t js::NativeObject::numDynamicSlots() const {
42 return dynamicSlotsCount(numFixedSlots(), slotSpan(), getClass());
43 }
44
dynamicSlotsCount(uint32_t nfixed,uint32_t span,const JSClass * clasp)45 /* static */ MOZ_ALWAYS_INLINE uint32_t js::NativeObject::dynamicSlotsCount(
46 uint32_t nfixed, uint32_t span, const JSClass* clasp) {
47 if (span <= nfixed) {
48 return 0;
49 }
50 span -= nfixed;
51
52 // Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
53 // the dynamic slots need to get increased again. ArrayObjects ignore
54 // this because slots are uncommon in that case.
55 if (clasp != &ArrayObject::class_ && span <= SLOT_CAPACITY_MIN) {
56 return SLOT_CAPACITY_MIN;
57 }
58
59 uint32_t slots = mozilla::RoundUpPow2(span);
60 MOZ_ASSERT(slots >= span);
61 return slots;
62 }
63
64 /* static */ MOZ_ALWAYS_INLINE uint32_t
dynamicSlotsCount(Shape * shape)65 js::NativeObject::dynamicSlotsCount(Shape* shape) {
66 return dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(),
67 shape->getObjectClass());
68 }
69
finalize(JSFreeOp * fop)70 inline void JSObject::finalize(JSFreeOp* fop) {
71 js::probes::FinalizeObject(this);
72
73 #ifdef DEBUG
74 MOZ_ASSERT(isTenured());
75 if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
76 /* Assert we're on the main thread. */
77 MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
78 }
79 #endif
80
81 const JSClass* clasp = getClass();
82 js::NativeObject* nobj = nullptr;
83 if (clasp->isNative()) {
84 nobj = &as<js::NativeObject>();
85 }
86 if (clasp->hasFinalize()) {
87 clasp->doFinalize(fop, this);
88 }
89
90 if (!nobj) {
91 return;
92 }
93
94 if (nobj->hasDynamicSlots()) {
95 size_t size = nobj->numDynamicSlots() * sizeof(js::HeapSlot);
96 fop->free_(this, nobj->slots_, size, js::MemoryUse::ObjectSlots);
97 }
98
99 if (nobj->hasDynamicElements()) {
100 js::ObjectElements* elements = nobj->getElementsHeader();
101 size_t size = elements->numAllocatedElements() * sizeof(js::HeapSlot);
102 if (elements->isCopyOnWrite()) {
103 if (elements->ownerObject() == this) {
104 // Don't free the elements until object finalization finishes,
105 // so that other objects can access these elements while they
106 // are themselves finalized.
107 MOZ_ASSERT(elements->numShiftedElements() == 0);
108 fop->freeLater(this, elements, size, js::MemoryUse::ObjectElements);
109 }
110 } else {
111 fop->free_(this, nobj->getUnshiftedElementsHeader(), size,
112 js::MemoryUse::ObjectElements);
113 }
114 }
115 }
116
sweepDictionaryListPointer()117 MOZ_ALWAYS_INLINE void js::NativeObject::sweepDictionaryListPointer() {
118 // Dictionary mode shapes can have pointers to nursery-allocated
119 // objects. There's no postbarrier for this pointer so this method is called
120 // to clear it when such an object dies.
121 MOZ_ASSERT(inDictionaryMode());
122 if (shape()->dictNext == DictionaryShapeLink(this)) {
123 shape()->dictNext.setNone();
124 }
125 }
126
127 MOZ_ALWAYS_INLINE void
updateDictionaryListPointerAfterMinorGC(NativeObject * old)128 js::NativeObject::updateDictionaryListPointerAfterMinorGC(NativeObject* old) {
129 MOZ_ASSERT(this == Forwarded(old));
130
131 // Dictionary objects can be allocated in the nursery and when they are
132 // tenured the shape's pointer to the object needs to be updated.
133 if (shape()->dictNext == DictionaryShapeLink(old)) {
134 shape()->dictNext = DictionaryShapeLink(this);
135 }
136 }
137
getGroup(JSContext * cx,js::HandleObject obj)138 /* static */ inline js::ObjectGroup* JSObject::getGroup(JSContext* cx,
139 js::HandleObject obj) {
140 MOZ_ASSERT(cx->compartment() == obj->compartment());
141 if (obj->hasLazyGroup()) {
142 if (cx->compartment() != obj->compartment()) {
143 MOZ_CRASH();
144 }
145 return makeLazyGroup(cx, obj);
146 }
147 return obj->groupRaw();
148 }
149
setGroup(js::ObjectGroup * group)150 inline void JSObject::setGroup(js::ObjectGroup* group) {
151 MOZ_RELEASE_ASSERT(group);
152 MOZ_ASSERT(!isSingleton());
153 MOZ_ASSERT(maybeCCWRealm() == group->realm());
154 setGroupRaw(group);
155 }
156
157 /* * */
158
isQualifiedVarObj()159 inline bool JSObject::isQualifiedVarObj() const {
160 if (is<js::DebugEnvironmentProxy>()) {
161 return as<js::DebugEnvironmentProxy>().environment().isQualifiedVarObj();
162 }
163 bool rv = hasAllFlags(js::BaseShape::QUALIFIED_VAROBJ);
164 MOZ_ASSERT_IF(rv, is<js::GlobalObject>() || is<js::CallObject>() ||
165 is<js::VarEnvironmentObject>() ||
166 is<js::ModuleEnvironmentObject>() ||
167 is<js::NonSyntacticVariablesObject>() ||
168 (is<js::WithEnvironmentObject>() &&
169 !as<js::WithEnvironmentObject>().isSyntactic()));
170 return rv;
171 }
172
isUnqualifiedVarObj()173 inline bool JSObject::isUnqualifiedVarObj() const {
174 if (is<js::DebugEnvironmentProxy>()) {
175 return as<js::DebugEnvironmentProxy>().environment().isUnqualifiedVarObj();
176 }
177 return is<js::GlobalObject>() || is<js::NonSyntacticVariablesObject>();
178 }
179
180 namespace js {
181
ClassCanHaveFixedData(const JSClass * clasp)182 inline bool ClassCanHaveFixedData(const JSClass* clasp) {
183 // Normally, the number of fixed slots given an object is the maximum
184 // permitted for its size class. For array buffers and non-shared typed
185 // arrays we only use enough to cover the class reserved slots, so that
186 // the remaining space in the object's allocation is available for the
187 // buffer's data.
188 return !clasp->isNative() || clasp == &js::ArrayBufferObject::class_ ||
189 js::IsTypedArrayClass(clasp);
190 }
191
192 // This function is meant to be called from allocation fast paths.
193 //
194 // If we do have an allocation metadata builder, it can cause a GC, so the
195 // object must be rooted. The usual way to do this would be to make our callers
196 // pass a HandleObject, but that would require them to pay the cost of rooting
197 // the object unconditionally, even though collecting metadata is rare. Instead,
198 // SetNewObjectMetadata's contract is that the caller must use the pointer
199 // returned in place of the pointer passed. If a GC occurs, the returned pointer
200 // may be the passed pointer, relocated by GC. If no GC could occur, it's just
201 // passed through. We root nothing unless necessary.
202 template <typename T>
SetNewObjectMetadata(JSContext * cx,T * obj)203 static MOZ_ALWAYS_INLINE MOZ_MUST_USE T* SetNewObjectMetadata(JSContext* cx,
204 T* obj) {
205 MOZ_ASSERT(!cx->realm()->hasObjectPendingMetadata());
206
207 // The metadata builder is invoked for each object created on the active
208 // thread, except when analysis/compilation is active, to avoid recursion.
209 if (!cx->isHelperThreadContext()) {
210 if (MOZ_UNLIKELY(cx->realm()->hasAllocationMetadataBuilder()) &&
211 !cx->zone()->suppressAllocationMetadataBuilder) {
212 // Don't collect metadata on objects that represent metadata.
213 AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
214
215 Rooted<T*> rooted(cx, obj);
216 cx->realm()->setNewObjectMetadata(cx, rooted);
217 return rooted;
218 }
219 }
220
221 return obj;
222 }
223
224 } // namespace js
225
nonCCWGlobal()226 inline js::GlobalObject& JSObject::nonCCWGlobal() const {
227 /*
228 * The global is read-barriered so that it is kept live by access through
229 * the Realm. When accessed through a JSObject, however, the global will be
230 * already kept live by the black JSObject's group pointer, so does not
231 * need to be read-barriered.
232 */
233 return *nonCCWRealm()->unsafeUnbarrieredMaybeGlobal();
234 }
235
hasAllFlags(js::BaseShape::Flag flags)236 inline bool JSObject::hasAllFlags(js::BaseShape::Flag flags) const {
237 MOZ_ASSERT(flags);
238 return shape()->hasAllObjectFlags(flags);
239 }
240
nonProxyIsExtensible()241 inline bool JSObject::nonProxyIsExtensible() const {
242 MOZ_ASSERT(!uninlinedIsProxy());
243
244 // [[Extensible]] for ordinary non-proxy objects is an object flag.
245 return !hasAllFlags(js::BaseShape::NOT_EXTENSIBLE);
246 }
247
isBoundFunction()248 inline bool JSObject::isBoundFunction() const {
249 return is<JSFunction>() && as<JSFunction>().isBoundFunction();
250 }
251
isDelegate()252 inline bool JSObject::isDelegate() const {
253 return hasAllFlags(js::BaseShape::DELEGATE);
254 }
255
hasUncacheableProto()256 inline bool JSObject::hasUncacheableProto() const {
257 return hasAllFlags(js::BaseShape::UNCACHEABLE_PROTO);
258 }
259
maybeHasInterestingSymbolProperty()260 MOZ_ALWAYS_INLINE bool JSObject::maybeHasInterestingSymbolProperty() const {
261 if (isNative()) {
262 return as<js::NativeObject>().hasInterestingSymbol();
263 }
264 return true;
265 }
266
staticPrototypeIsImmutable()267 inline bool JSObject::staticPrototypeIsImmutable() const {
268 MOZ_ASSERT(hasStaticPrototype());
269 return hasAllFlags(js::BaseShape::IMMUTABLE_PROTOTYPE);
270 }
271
isIteratedSingleton()272 inline bool JSObject::isIteratedSingleton() const {
273 return hasAllFlags(js::BaseShape::ITERATED_SINGLETON);
274 }
275
isNewGroupUnknown()276 inline bool JSObject::isNewGroupUnknown() const {
277 return hasAllFlags(js::BaseShape::NEW_GROUP_UNKNOWN);
278 }
279
280 namespace js {
281
IsFunctionObject(const js::Value & v)282 static MOZ_ALWAYS_INLINE bool IsFunctionObject(const js::Value& v) {
283 return v.isObject() && v.toObject().is<JSFunction>();
284 }
285
IsFunctionObject(const js::Value & v,JSFunction ** fun)286 static MOZ_ALWAYS_INLINE bool IsFunctionObject(const js::Value& v,
287 JSFunction** fun) {
288 if (v.isObject() && v.toObject().is<JSFunction>()) {
289 *fun = &v.toObject().as<JSFunction>();
290 return true;
291 }
292 return false;
293 }
294
IsNativeFunction(const js::Value & v)295 static MOZ_ALWAYS_INLINE bool IsNativeFunction(const js::Value& v) {
296 JSFunction* fun;
297 return IsFunctionObject(v, &fun) && fun->isNative();
298 }
299
IsNativeFunction(const js::Value & v,JSFunction ** fun)300 static MOZ_ALWAYS_INLINE bool IsNativeFunction(const js::Value& v,
301 JSFunction** fun) {
302 return IsFunctionObject(v, fun) && (*fun)->isNative();
303 }
304
IsNativeFunction(const js::Value & v,JSNative native)305 static MOZ_ALWAYS_INLINE bool IsNativeFunction(const js::Value& v,
306 JSNative native) {
307 JSFunction* fun;
308 return IsFunctionObject(v, &fun) && fun->maybeNative() == native;
309 }
310
IsNativeFunction(const JSObject * obj,JSNative native)311 static MOZ_ALWAYS_INLINE bool IsNativeFunction(const JSObject* obj,
312 JSNative native) {
313 return obj->is<JSFunction>() && obj->as<JSFunction>().maybeNative() == native;
314 }
315
316 // Return whether looking up a method on 'obj' definitely resolves to the
317 // original specified native function. The method may conservatively return
318 // 'false' in the case of proxies or other non-native objects.
HasNativeMethodPure(JSObject * obj,PropertyName * name,JSNative native,JSContext * cx)319 static MOZ_ALWAYS_INLINE bool HasNativeMethodPure(JSObject* obj,
320 PropertyName* name,
321 JSNative native,
322 JSContext* cx) {
323 Value v;
324 if (!GetPropertyPure(cx, obj, NameToId(name), &v)) {
325 return false;
326 }
327
328 return IsNativeFunction(v, native);
329 }
330
331 // Return whether 'obj' definitely has no @@toPrimitive method.
HasNoToPrimitiveMethodPure(JSObject * obj,JSContext * cx)332 static MOZ_ALWAYS_INLINE bool HasNoToPrimitiveMethodPure(JSObject* obj,
333 JSContext* cx) {
334 Symbol* toPrimitive = cx->wellKnownSymbols().toPrimitive;
335 JSObject* holder;
336 if (!MaybeHasInterestingSymbolProperty(cx, obj, toPrimitive, &holder)) {
337 #ifdef DEBUG
338 JSObject* pobj;
339 PropertyResult prop;
340 MOZ_ASSERT(
341 LookupPropertyPure(cx, obj, SYMBOL_TO_JSID(toPrimitive), &pobj, &prop));
342 MOZ_ASSERT(!prop);
343 #endif
344 return true;
345 }
346
347 JSObject* pobj;
348 PropertyResult prop;
349 if (!LookupPropertyPure(cx, holder, SYMBOL_TO_JSID(toPrimitive), &pobj,
350 &prop)) {
351 return false;
352 }
353
354 return !prop;
355 }
356
357 extern bool ToPropertyKeySlow(JSContext* cx, HandleValue argument,
358 MutableHandleId result);
359
360 /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */
ToPropertyKey(JSContext * cx,HandleValue argument,MutableHandleId result)361 MOZ_ALWAYS_INLINE bool ToPropertyKey(JSContext* cx, HandleValue argument,
362 MutableHandleId result) {
363 if (MOZ_LIKELY(argument.isPrimitive())) {
364 return ValueToId<CanGC>(cx, argument, result);
365 }
366
367 return ToPropertyKeySlow(cx, argument, result);
368 }
369
370 /*
371 * Return true if this is a compiler-created internal function accessed by
372 * its own object. Such a function object must not be accessible to script
373 * or embedding code.
374 */
IsInternalFunctionObject(JSObject & funobj)375 inline bool IsInternalFunctionObject(JSObject& funobj) {
376 JSFunction& fun = funobj.as<JSFunction>();
377 return fun.isInterpreted() && !fun.environment();
378 }
379
GetInitialHeap(NewObjectKind newKind,const JSClass * clasp)380 inline gc::InitialHeap GetInitialHeap(NewObjectKind newKind,
381 const JSClass* clasp) {
382 if (newKind != GenericObject) {
383 return gc::TenuredHeap;
384 }
385 if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp)) {
386 return gc::TenuredHeap;
387 }
388 return gc::DefaultHeap;
389 }
390
GetInitialHeap(NewObjectKind newKind,ObjectGroup * group)391 inline gc::InitialHeap GetInitialHeap(NewObjectKind newKind,
392 ObjectGroup* group) {
393 AutoSweepObjectGroup sweep(group);
394 if (group->shouldPreTenure(sweep)) {
395 return gc::TenuredHeap;
396 }
397
398 return GetInitialHeap(newKind, group->clasp());
399 }
400
401 /*
402 * Make an object with the specified prototype. If parent is null, it will
403 * default to the prototype's global if the prototype is non-null.
404 */
405 JSObject* NewObjectWithGivenTaggedProto(JSContext* cx, const JSClass* clasp,
406 Handle<TaggedProto> proto,
407 gc::AllocKind allocKind,
408 NewObjectKind newKind,
409 uint32_t initialShapeFlags = 0);
410
411 template <NewObjectKind NewKind>
NewObjectWithGivenTaggedProto(JSContext * cx,const JSClass * clasp,Handle<TaggedProto> proto)412 inline JSObject* NewObjectWithGivenTaggedProto(JSContext* cx,
413 const JSClass* clasp,
414 Handle<TaggedProto> proto) {
415 gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
416 return NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, NewKind, 0);
417 }
418
419 namespace detail {
420
421 template <typename T, NewObjectKind NewKind>
NewObjectWithGivenTaggedProtoForKind(JSContext * cx,Handle<TaggedProto> proto)422 inline T* NewObjectWithGivenTaggedProtoForKind(JSContext* cx,
423 Handle<TaggedProto> proto) {
424 JSObject* obj = NewObjectWithGivenTaggedProto<NewKind>(cx, &T::class_, proto);
425 return obj ? &obj->as<T>() : nullptr;
426 }
427
428 } // namespace detail
429
430 template <typename T>
NewObjectWithGivenTaggedProto(JSContext * cx,Handle<TaggedProto> proto)431 inline T* NewObjectWithGivenTaggedProto(JSContext* cx,
432 Handle<TaggedProto> proto) {
433 return detail::NewObjectWithGivenTaggedProtoForKind<T, GenericObject>(cx,
434 proto);
435 }
436
437 template <typename T>
NewSingletonObjectWithGivenTaggedProtoAndKind(JSContext * cx,Handle<TaggedProto> proto,gc::AllocKind allocKind)438 inline T* NewSingletonObjectWithGivenTaggedProtoAndKind(
439 JSContext* cx, Handle<TaggedProto> proto, gc::AllocKind allocKind) {
440 JSObject* obj = NewObjectWithGivenTaggedProto(cx, &T::class_, proto,
441 allocKind, SingletonObject, 0);
442 return obj ? &obj->as<T>() : nullptr;
443 }
444
445 inline JSObject* NewObjectWithGivenProto(
446 JSContext* cx, const JSClass* clasp, HandleObject proto,
447 gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) {
448 return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(proto),
449 allocKind, newKind);
450 }
451
NewObjectWithGivenProto(JSContext * cx,const JSClass * clasp,HandleObject proto)452 inline JSObject* NewObjectWithGivenProto(JSContext* cx, const JSClass* clasp,
453 HandleObject proto) {
454 return NewObjectWithGivenTaggedProto<GenericObject>(cx, clasp,
455 AsTaggedProto(proto));
456 }
457
NewTenuredObjectWithGivenProto(JSContext * cx,const JSClass * clasp,HandleObject proto)458 inline JSObject* NewTenuredObjectWithGivenProto(JSContext* cx,
459 const JSClass* clasp,
460 HandleObject proto) {
461 return NewObjectWithGivenTaggedProto<TenuredObject>(cx, clasp,
462 AsTaggedProto(proto));
463 }
464
NewSingletonObjectWithGivenProto(JSContext * cx,const JSClass * clasp,HandleObject proto)465 inline JSObject* NewSingletonObjectWithGivenProto(JSContext* cx,
466 const JSClass* clasp,
467 HandleObject proto) {
468 return NewObjectWithGivenTaggedProto<SingletonObject>(cx, clasp,
469 AsTaggedProto(proto));
470 }
471
472 template <typename T>
NewObjectWithGivenProto(JSContext * cx,HandleObject proto)473 inline T* NewObjectWithGivenProto(JSContext* cx, HandleObject proto) {
474 return detail::NewObjectWithGivenTaggedProtoForKind<T, GenericObject>(
475 cx, AsTaggedProto(proto));
476 }
477
478 template <typename T>
NewSingletonObjectWithGivenProto(JSContext * cx,HandleObject proto)479 inline T* NewSingletonObjectWithGivenProto(JSContext* cx, HandleObject proto) {
480 return detail::NewObjectWithGivenTaggedProtoForKind<T, SingletonObject>(
481 cx, AsTaggedProto(proto));
482 }
483
484 template <typename T>
NewTenuredObjectWithGivenProto(JSContext * cx,HandleObject proto)485 inline T* NewTenuredObjectWithGivenProto(JSContext* cx, HandleObject proto) {
486 return detail::NewObjectWithGivenTaggedProtoForKind<T, TenuredObject>(
487 cx, AsTaggedProto(proto));
488 }
489
490 template <typename T>
NewObjectWithGivenProtoAndKinds(JSContext * cx,HandleObject proto,gc::AllocKind allocKind,NewObjectKind newKind)491 inline T* NewObjectWithGivenProtoAndKinds(JSContext* cx, HandleObject proto,
492 gc::AllocKind allocKind,
493 NewObjectKind newKind) {
494 JSObject* obj = NewObjectWithGivenTaggedProto(
495 cx, &T::class_, AsTaggedProto(proto), allocKind, newKind);
496 return obj ? &obj->as<T>() : nullptr;
497 }
498
499 // Make an object with the prototype set according to the cached prototype or
500 // Object.prototype.
501 JSObject* NewObjectWithClassProto(JSContext* cx, const JSClass* clasp,
502 HandleObject proto, gc::AllocKind allocKind,
503 NewObjectKind newKind = GenericObject);
504
505 inline JSObject* NewObjectWithClassProto(
506 JSContext* cx, const JSClass* clasp, HandleObject proto,
507 NewObjectKind newKind = GenericObject) {
508 gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
509 return NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind);
510 }
511
512 template <class T>
NewObjectWithClassProto(JSContext * cx,HandleObject proto)513 inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto) {
514 JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, GenericObject);
515 return obj ? &obj->as<T>() : nullptr;
516 }
517
518 template <class T>
NewObjectWithClassProtoAndKind(JSContext * cx,HandleObject proto,NewObjectKind newKind)519 inline T* NewObjectWithClassProtoAndKind(JSContext* cx, HandleObject proto,
520 NewObjectKind newKind) {
521 JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, newKind);
522 return obj ? &obj->as<T>() : nullptr;
523 }
524
525 template <class T>
526 inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto,
527 gc::AllocKind allocKind,
528 NewObjectKind newKind = GenericObject) {
529 JSObject* obj =
530 NewObjectWithClassProto(cx, &T::class_, proto, allocKind, newKind);
531 return obj ? &obj->as<T>() : nullptr;
532 }
533
534 /*
535 * Create a native instance of the given class with parent and proto set
536 * according to the context's active global.
537 */
538 inline JSObject* NewBuiltinClassInstance(
539 JSContext* cx, const JSClass* clasp, gc::AllocKind allocKind,
540 NewObjectKind newKind = GenericObject) {
541 return NewObjectWithClassProto(cx, clasp, nullptr, allocKind, newKind);
542 }
543
544 inline JSObject* NewBuiltinClassInstance(
545 JSContext* cx, const JSClass* clasp,
546 NewObjectKind newKind = GenericObject) {
547 gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
548 return NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
549 }
550
551 template <typename T>
NewBuiltinClassInstance(JSContext * cx)552 inline T* NewBuiltinClassInstance(JSContext* cx) {
553 JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, GenericObject);
554 return obj ? &obj->as<T>() : nullptr;
555 }
556
557 template <typename T>
NewTenuredBuiltinClassInstance(JSContext * cx)558 inline T* NewTenuredBuiltinClassInstance(JSContext* cx) {
559 JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, TenuredObject);
560 return obj ? &obj->as<T>() : nullptr;
561 }
562
563 template <typename T>
NewBuiltinClassInstanceWithKind(JSContext * cx,NewObjectKind newKind)564 inline T* NewBuiltinClassInstanceWithKind(JSContext* cx,
565 NewObjectKind newKind) {
566 JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
567 return obj ? &obj->as<T>() : nullptr;
568 }
569
570 template <typename T>
571 inline T* NewBuiltinClassInstance(JSContext* cx, gc::AllocKind allocKind,
572 NewObjectKind newKind = GenericObject) {
573 JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind);
574 return obj ? &obj->as<T>() : nullptr;
575 }
576
577 // Used to optimize calls to (new Object())
578 bool NewObjectScriptedCall(JSContext* cx, MutableHandleObject obj);
579
580 JSObject* NewObjectWithGroupCommon(JSContext* cx, HandleObjectGroup group,
581 gc::AllocKind allocKind,
582 NewObjectKind newKind);
583
584 template <typename T>
585 inline T* NewObjectWithGroup(JSContext* cx, HandleObjectGroup group,
586 gc::AllocKind allocKind,
587 NewObjectKind newKind = GenericObject) {
588 JSObject* obj = NewObjectWithGroupCommon(cx, group, allocKind, newKind);
589 return obj ? &obj->as<T>() : nullptr;
590 }
591
592 template <typename T>
593 inline T* NewObjectWithGroup(JSContext* cx, HandleObjectGroup group,
594 NewObjectKind newKind = GenericObject) {
595 gc::AllocKind allocKind = gc::GetGCObjectKind(group->clasp());
596 return NewObjectWithGroup<T>(cx, group, allocKind, newKind);
597 }
598
599 /*
600 * As for gc::GetGCObjectKind, where numElements is a guess at the final size of
601 * the object, zero if the final size is unknown. This should only be used for
602 * objects that do not require any fixed slots.
603 */
GuessObjectGCKind(size_t numElements)604 static inline gc::AllocKind GuessObjectGCKind(size_t numElements) {
605 if (numElements) {
606 return gc::GetGCObjectKind(numElements);
607 }
608 return gc::AllocKind::OBJECT4;
609 }
610
GuessArrayGCKind(size_t numElements)611 static inline gc::AllocKind GuessArrayGCKind(size_t numElements) {
612 if (numElements) {
613 return gc::GetGCArrayKind(numElements);
614 }
615 return gc::AllocKind::OBJECT8;
616 }
617
618 // Returns ESClass::Other if the value isn't an object, or if the object
619 // isn't of one of the enumerated classes. Otherwise returns the appropriate
620 // class.
GetClassOfValue(JSContext * cx,HandleValue v,ESClass * cls)621 inline bool GetClassOfValue(JSContext* cx, HandleValue v, ESClass* cls) {
622 if (!v.isObject()) {
623 *cls = ESClass::Other;
624 return true;
625 }
626
627 RootedObject obj(cx, &v.toObject());
628 return GetBuiltinClass(cx, obj, cls);
629 }
630
631 extern NativeObject* InitClass(JSContext* cx, HandleObject obj,
632 HandleObject parent_proto, const JSClass* clasp,
633 JSNative constructor, unsigned nargs,
634 const JSPropertySpec* ps,
635 const JSFunctionSpec* fs,
636 const JSPropertySpec* static_ps,
637 const JSFunctionSpec* static_fs,
638 NativeObject** ctorp = nullptr);
639
GetObjectClassName(JSContext * cx,HandleObject obj)640 MOZ_ALWAYS_INLINE const char* GetObjectClassName(JSContext* cx,
641 HandleObject obj) {
642 if (obj->is<ProxyObject>()) {
643 return Proxy::className(cx, obj);
644 }
645
646 return obj->getClass()->name;
647 }
648
IsCallable(const Value & v)649 inline bool IsCallable(const Value& v) {
650 return v.isObject() && v.toObject().isCallable();
651 }
652
653 // ES6 rev 24 (2014 April 27) 7.2.5 IsConstructor
IsConstructor(const Value & v)654 inline bool IsConstructor(const Value& v) {
655 return v.isObject() && v.toObject().isConstructor();
656 }
657
658 } /* namespace js */
659
isCallable()660 MOZ_ALWAYS_INLINE bool JSObject::isCallable() const {
661 if (is<JSFunction>()) {
662 return true;
663 }
664 if (is<js::ProxyObject>()) {
665 const js::ProxyObject& p = as<js::ProxyObject>();
666 return p.handler()->isCallable(const_cast<JSObject*>(this));
667 }
668 return callHook() != nullptr;
669 }
670
isConstructor()671 MOZ_ALWAYS_INLINE bool JSObject::isConstructor() const {
672 if (is<JSFunction>()) {
673 const JSFunction& fun = as<JSFunction>();
674 return fun.isConstructor();
675 }
676 if (is<js::ProxyObject>()) {
677 const js::ProxyObject& p = as<js::ProxyObject>();
678 return p.handler()->isConstructor(const_cast<JSObject*>(this));
679 }
680 return constructHook() != nullptr;
681 }
682
callHook()683 MOZ_ALWAYS_INLINE JSNative JSObject::callHook() const {
684 return getClass()->getCall();
685 }
686
constructHook()687 MOZ_ALWAYS_INLINE JSNative JSObject::constructHook() const {
688 return getClass()->getConstruct();
689 }
690
691 #endif /* vm_JSObject_inl_h */
692