1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
12 #include "builtin/MapObject.h"
13 #include "builtin/TypedObject.h"
14 #include "vm/ArrayObject.h"
15 #include "vm/DateObject.h"
16 #include "vm/NumberObject.h"
17 #include "vm/Probes.h"
18 #include "vm/ScopeObject.h"
19 #include "vm/StringObject.h"
21 #include "jsatominlines.h"
22 #include "jscompartmentinlines.h"
23 #include "jsgcinlines.h"
24 #include "jsinferinlines.h"
26 #include "gc/ForkJoinNursery-inl.h"
27 #include "vm/ObjectImpl-inl.h"
29 /* static */ inline bool
30 JSObject::setGenericAttributes(JSContext
* cx
, js::HandleObject obj
,
31 js::HandleId id
, unsigned* attrsp
)
33 js::types::MarkTypePropertyNonData(cx
, obj
, id
);
34 js::GenericAttributesOp op
= obj
->getOps()->setGenericAttributes
;
35 return (op
? op
: js::baseops::SetAttributes
)(cx
, obj
, id
, attrsp
);
38 /* static */ inline bool
39 JSObject::changePropertyAttributes(JSContext
* cx
, js::HandleObject obj
,
40 js::HandleShape shape
, unsigned attrs
)
42 return !!changeProperty
<js::SequentialExecution
>(cx
, obj
, shape
, attrs
, 0,
43 shape
->getter(), shape
->setter());
46 /* static */ inline bool
47 JSObject::deleteGeneric(JSContext
* cx
, js::HandleObject obj
, js::HandleId id
,
50 js::types::MarkTypePropertyNonData(cx
, obj
, id
);
51 js::DeleteGenericOp op
= obj
->getOps()->deleteGeneric
;
52 return (op
? op
: js::baseops::DeleteGeneric
)(cx
, obj
, id
, succeeded
);
55 /* static */ inline bool
56 JSObject::deleteElement(JSContext
* cx
, js::HandleObject obj
, uint32_t index
, bool* succeeded
)
59 if (!js::IndexToId(cx
, index
, &id
))
61 return deleteGeneric(cx
, obj
, id
, succeeded
);
64 /* static */ inline bool
65 JSObject::watch(JSContext
* cx
, JS::HandleObject obj
, JS::HandleId id
,
66 JS::HandleObject callable
)
68 js::WatchOp op
= obj
->getOps()->watch
;
69 return (op
? op
: js::baseops::Watch
)(cx
, obj
, id
, callable
);
72 /* static */ inline bool
73 JSObject::unwatch(JSContext
* cx
, JS::HandleObject obj
, JS::HandleId id
)
75 js::UnwatchOp op
= obj
->getOps()->unwatch
;
76 return (op
? op
: js::baseops::Unwatch
)(cx
, obj
, id
);
80 JSObject::finalize(js::FreeOp
* fop
)
82 js::probes::FinalizeObject(this);
85 JS_ASSERT(isTenured());
86 if (!IsBackgroundFinalized(tenuredGetAllocKind())) {
87 /* Assert we're on the main thread. */
88 JS_ASSERT(CurrentThreadCanAccessRuntime(fop
->runtime()));
91 const js::Class
* clasp
= getClass();
93 clasp
->finalize(fop
, this);
99 JSObject::setLastPropertyInfallible(js::Shape
* shape
)
101 JS_ASSERT(!shape
->inDictionary());
102 JS_ASSERT(shape
->compartment() == compartment());
103 JS_ASSERT(!inDictionaryMode());
104 JS_ASSERT(slotSpan() == shape
->slotSpan());
105 JS_ASSERT(numFixedSlots() == shape
->numFixedSlots());
111 JSObject::removeLastProperty(js::ExclusiveContext
* cx
)
113 JS_ASSERT(canRemoveLastProperty());
114 JS::RootedObject
self(cx
, this);
115 js::RootedShape
prev(cx
, lastProperty()->previous());
116 JS_ALWAYS_TRUE(setLastProperty(cx
, self
, prev
));
120 JSObject::canRemoveLastProperty()
123 * Check that the information about the object stored in the last
124 * property's base shape is consistent with that stored in the previous
125 * shape. If not consistent, then the last property cannot be removed as it
126 * will induce a change in the object itself, and the object must be
127 * converted to dictionary mode instead. See BaseShape comment in jsscope.h
129 JS_ASSERT(!inDictionaryMode());
130 js::Shape
* previous
= lastProperty()->previous().get();
131 return previous
->getObjectParent() == lastProperty()->getObjectParent()
132 && previous
->getObjectMetadata() == lastProperty()->getObjectMetadata()
133 && previous
->getObjectFlags() == lastProperty()->getObjectFlags();
137 JSObject::setShouldConvertDoubleElements()
139 JS_ASSERT(is
<js::ArrayObject
>() && !hasEmptyElements());
140 getElementsHeader()->setShouldConvertDoubleElements();
144 JSObject::clearShouldConvertDoubleElements()
146 JS_ASSERT(is
<js::ArrayObject
>() && !hasEmptyElements());
147 getElementsHeader()->clearShouldConvertDoubleElements();
151 JSObject::setDenseElementIfHasType(uint32_t index
, const js::Value
& val
)
153 if (!js::types::HasTypePropertyId(this, JSID_VOID
, val
))
155 setDenseElementMaybeConvertDouble(index
, val
);
160 JSObject::setDenseElementWithType(js::ExclusiveContext
* cx
, uint32_t index
,
161 const js::Value
& val
)
163 // Avoid a slow AddTypePropertyId call if the type is the same as the type
164 // of the previous element.
165 js::types::Type thisType
= js::types::GetValueType(val
);
166 if (index
== 0 || js::types::GetValueType(elements
[index
- 1]) != thisType
)
167 js::types::AddTypePropertyId(cx
, this, JSID_VOID
, thisType
);
168 setDenseElementMaybeConvertDouble(index
, val
);
172 JSObject::initDenseElementWithType(js::ExclusiveContext
* cx
, uint32_t index
,
173 const js::Value
& val
)
175 JS_ASSERT(!shouldConvertDoubleElements());
176 js::types::AddTypePropertyId(cx
, this, JSID_VOID
, val
);
177 initDenseElement(index
, val
);
181 JSObject::setDenseElementHole(js::ExclusiveContext
* cx
, uint32_t index
)
183 js::types::MarkTypeObjectFlags(cx
, this, js::types::OBJECT_FLAG_NON_PACKED
);
184 setDenseElement(index
, js::MagicValue(JS_ELEMENTS_HOLE
));
187 /* static */ inline void
188 JSObject::removeDenseElementForSparseIndex(js::ExclusiveContext
* cx
,
189 js::HandleObject obj
, uint32_t index
)
191 js::types::MarkTypeObjectFlags(cx
, obj
,
192 js::types::OBJECT_FLAG_NON_PACKED
|
193 js::types::OBJECT_FLAG_SPARSE_INDEXES
);
194 if (obj
->containsDenseElement(index
))
195 obj
->setDenseElement(index
, js::MagicValue(JS_ELEMENTS_HOLE
));
199 JSObject::writeToIndexWouldMarkNotPacked(uint32_t index
)
201 return getElementsHeader()->initializedLength
< index
;
205 JSObject::markDenseElementsNotPacked(js::ExclusiveContext
* cx
)
207 JS_ASSERT(isNative());
208 MarkTypeObjectFlags(cx
, this, js::types::OBJECT_FLAG_NON_PACKED
);
212 JSObject::ensureDenseInitializedLengthNoPackedCheck(js::ThreadSafeContext
* cx
, uint32_t index
,
215 JS_ASSERT(cx
->isThreadLocal(this));
216 JS_ASSERT(!denseElementsAreCopyOnWrite());
219 * Ensure that the array's contents have been initialized up to index, and
220 * mark the elements through 'index + extra' as initialized in preparation
223 JS_ASSERT(index
+ extra
<= getDenseCapacity());
224 uint32_t& initlen
= getElementsHeader()->initializedLength
;
226 if (initlen
< index
+ extra
) {
227 size_t offset
= initlen
;
228 for (js::HeapSlot
* sp
= elements
+ initlen
;
229 sp
!= elements
+ (index
+ extra
);
232 sp
->init(this, js::HeapSlot::Element
, offset
, js::MagicValue(JS_ELEMENTS_HOLE
));
234 initlen
= index
+ extra
;
239 JSObject::ensureDenseInitializedLength(js::ExclusiveContext
* cx
, uint32_t index
, uint32_t extra
)
241 if (writeToIndexWouldMarkNotPacked(index
))
242 markDenseElementsNotPacked(cx
);
243 ensureDenseInitializedLengthNoPackedCheck(cx
, index
, extra
);
247 JSObject::ensureDenseInitializedLengthPreservePackedFlag(js::ThreadSafeContext
* cx
,
248 uint32_t index
, uint32_t extra
)
250 JS_ASSERT(!writeToIndexWouldMarkNotPacked(index
));
251 ensureDenseInitializedLengthNoPackedCheck(cx
, index
, extra
);
254 JSObject::EnsureDenseResult
255 JSObject::extendDenseElements(js::ThreadSafeContext
* cx
,
256 uint32_t requiredCapacity
, uint32_t extra
)
258 JS_ASSERT(cx
->isThreadLocal(this));
259 JS_ASSERT(!denseElementsAreCopyOnWrite());
262 * Don't grow elements for non-extensible objects or watched objects. Dense
263 * elements can be added/written with no extensible or watchpoint checks as
264 * long as there is capacity for them.
266 if (!nonProxyIsExtensible() || watched()) {
267 JS_ASSERT(getDenseCapacity() == 0);
272 * Don't grow elements for objects which already have sparse indexes.
273 * This avoids needing to count non-hole elements in willBeSparseElements
274 * every time a new index is added.
280 * We use the extra argument also as a hint about number of non-hole
281 * elements to be inserted.
283 if (requiredCapacity
> MIN_SPARSE_INDEX
&&
284 willBeSparseElements(requiredCapacity
, extra
)) {
288 if (!growElements(cx
, requiredCapacity
))
294 inline JSObject::EnsureDenseResult
295 JSObject::ensureDenseElementsNoPackedCheck(js::ThreadSafeContext
* cx
, uint32_t index
, uint32_t extra
)
297 JS_ASSERT(isNative());
299 if (!maybeCopyElementsForWrite(cx
))
302 uint32_t currentCapacity
= getDenseCapacity();
304 uint32_t requiredCapacity
;
306 /* Optimize for the common case. */
307 if (index
< currentCapacity
) {
308 ensureDenseInitializedLengthNoPackedCheck(cx
, index
, 1);
311 requiredCapacity
= index
+ 1;
312 if (requiredCapacity
== 0) {
317 requiredCapacity
= index
+ extra
;
318 if (requiredCapacity
< index
) {
322 if (requiredCapacity
<= currentCapacity
) {
323 ensureDenseInitializedLengthNoPackedCheck(cx
, index
, extra
);
328 EnsureDenseResult edr
= extendDenseElements(cx
, requiredCapacity
, extra
);
332 ensureDenseInitializedLengthNoPackedCheck(cx
, index
, extra
);
336 inline JSObject::EnsureDenseResult
337 JSObject::ensureDenseElements(js::ExclusiveContext
* cx
, uint32_t index
, uint32_t extra
)
339 if (writeToIndexWouldMarkNotPacked(index
))
340 markDenseElementsNotPacked(cx
);
341 return ensureDenseElementsNoPackedCheck(cx
, index
, extra
);
344 inline JSObject::EnsureDenseResult
345 JSObject::ensureDenseElementsPreservePackedFlag(js::ThreadSafeContext
* cx
, uint32_t index
,
348 JS_ASSERT(!writeToIndexWouldMarkNotPacked(index
));
349 return ensureDenseElementsNoPackedCheck(cx
, index
, extra
);
353 JSObject::getDenseOrTypedArrayElement(uint32_t idx
)
355 if (is
<js::TypedArrayObject
>())
356 return as
<js::TypedArrayObject
>().getElement(idx
);
357 return getDenseElement(idx
);
361 JSObject::initDenseElementsUnbarriered(uint32_t dstStart
, const js::Value
* src
, uint32_t count
) {
363 * For use by parallel threads, which since they cannot see nursery
364 * things do not require a barrier.
366 JS_ASSERT(dstStart
+ count
<= getDenseCapacity());
367 JS_ASSERT(!denseElementsAreCopyOnWrite());
368 #if defined(DEBUG) && defined(JSGC_GENERATIONAL)
370 * This asserts a global invariant: parallel code does not
371 * observe objects inside the generational GC's nursery.
373 JS_ASSERT(!js::gc::IsInsideGGCNursery(this));
374 for (uint32_t index
= 0; index
< count
; ++index
) {
375 const JS::Value
& value
= src
[index
];
376 if (value
.isMarkable())
377 JS_ASSERT(!js::gc::IsInsideGGCNursery(static_cast<js::gc::Cell
*>(value
.toGCThing())));
380 memcpy(&elements
[dstStart
], src
, count
* sizeof(js::HeapSlot
));
383 /* static */ inline bool
384 JSObject::setSingletonType(js::ExclusiveContext
* cx
, js::HandleObject obj
)
386 JS_ASSERT_IF(cx
->isJSContext(), !IsInsideNursery(obj
));
388 js::types::TypeObject
* type
= cx
->getSingletonType(obj
->getClass(), obj
->getTaggedProto());
396 inline js::types::TypeObject
*
397 JSObject::getType(JSContext
* cx
)
399 JS_ASSERT(cx
->compartment() == compartment());
401 JS::RootedObject
self(cx
, this);
402 if (cx
->compartment() != compartment())
404 return makeLazyType(cx
, self
);
406 return static_cast<js::types::TypeObject
*>(type_
);
409 /* static */ inline bool
410 JSObject::clearType(JSContext
* cx
, js::HandleObject obj
)
412 JS_ASSERT(!obj
->hasSingletonType());
413 JS_ASSERT(cx
->compartment() == obj
->compartment());
415 js::types::TypeObject
* type
= cx
->getNewType(obj
->getClass(), js::TaggedProto(nullptr));
424 JSObject::setType(js::types::TypeObject
* newType
)
427 JS_ASSERT(!hasSingletonType());
431 /* static */ inline bool
432 JSObject::getProto(JSContext
* cx
, js::HandleObject obj
, js::MutableHandleObject protop
)
434 if (obj
->getTaggedProto().isLazy()) {
435 JS_ASSERT(obj
->is
<js::ProxyObject
>());
436 return js::Proxy::getPrototypeOf(cx
, obj
, protop
);
438 protop
.set(obj
->getTaggedProto().toObjectOrNull());
443 /* static */ inline bool
444 JSObject::setProto(JSContext
* cx
, JS::HandleObject obj
, JS::HandleObject proto
, bool* succeeded
)
446 /* Proxies live in their own little world. */
447 if (obj
->getTaggedProto().isLazy()) {
448 JS_ASSERT(obj
->is
<js::ProxyObject
>());
449 return js::Proxy::setPrototypeOf(cx
, obj
, proto
, succeeded
);
453 * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which
454 * due to their complicated delegate-object shenanigans can't easily
455 * have a mutable [[Prototype]].
457 if (obj
->is
<js::ArrayBufferObject
>()) {
458 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_SETPROTOTYPEOF_FAIL
,
459 "incompatible ArrayBuffer");
464 * Disallow mutating the [[Prototype]] on Typed Objects, per the spec.
466 if (obj
->is
<js::TypedObject
>()) {
467 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_SETPROTOTYPEOF_FAIL
,
468 "incompatible TypedObject");
473 * Explicitly disallow mutating the [[Prototype]] of Location objects
474 * for flash-related security reasons.
476 if (!strcmp(obj
->getClass()->name
, "Location")) {
477 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, nullptr, JSMSG_SETPROTOTYPEOF_FAIL
,
478 "incompatible Location object");
482 /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
484 if (!JSObject::isExtensible(cx
, obj
, &extensible
))
491 /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */
492 js::RootedObject
obj2(cx
);
493 for (obj2
= proto
; obj2
; ) {
499 if (!JSObject::getProto(cx
, obj2
, &obj2
))
503 JS::Rooted
<js::TaggedProto
> taggedProto(cx
, js::TaggedProto(proto
));
504 return SetClassAndProto(cx
, obj
, obj
->getClass(), taggedProto
, succeeded
);
508 JSObject::isQualifiedVarObj()
510 if (is
<js::DebugScopeObject
>())
511 return as
<js::DebugScopeObject
>().scope().isQualifiedVarObj();
512 return lastProperty()->hasObjectFlag(js::BaseShape::QUALIFIED_VAROBJ
);
516 JSObject::isUnqualifiedVarObj()
518 if (is
<js::DebugScopeObject
>())
519 return as
<js::DebugScopeObject
>().scope().isUnqualifiedVarObj();
520 return lastProperty()->hasObjectFlag(js::BaseShape::UNQUALIFIED_VAROBJ
);
523 /* static */ inline JSObject
*
524 JSObject::create(js::ExclusiveContext
* cx
, js::gc::AllocKind kind
, js::gc::InitialHeap heap
,
525 js::HandleShape shape
, js::HandleTypeObject type
)
527 JS_ASSERT(shape
&& type
);
528 JS_ASSERT(type
->clasp() == shape
->getObjectClass());
529 JS_ASSERT(type
->clasp() != &js::ArrayObject::class_
);
530 JS_ASSERT_IF(!ClassCanHaveFixedData(type
->clasp()),
531 js::gc::GetGCKindSlots(kind
, type
->clasp()) == shape
->numFixedSlots());
532 JS_ASSERT_IF(type
->clasp()->flags
& JSCLASS_BACKGROUND_FINALIZE
, IsBackgroundFinalized(kind
));
533 JS_ASSERT_IF(type
->clasp()->finalize
, heap
== js::gc::TenuredHeap
);
535 const js::Class
* clasp
= type
->clasp();
536 size_t nDynamicSlots
= dynamicSlotsCount(shape
->numFixedSlots(), shape
->slotSpan(), clasp
);
538 JSObject
* obj
= js::NewGCObject
<js::CanGC
>(cx
, kind
, nDynamicSlots
, heap
);
542 obj
->shape_
.init(shape
);
543 obj
->type_
.init(type
);
544 // Note: slots are created and assigned internally by NewGCObject.
545 obj
->elements
= js::emptyObjectElements
;
547 if (clasp
->hasPrivate())
548 obj
->privateRef(shape
->numFixedSlots()) = nullptr;
550 size_t span
= shape
->slotSpan();
552 obj
->initializeSlotRange(0, span
);
554 // JSFunction's fixed slots expect POD-style initialization.
555 if (type
->clasp()->isJSFunction())
556 memset(obj
->fixedSlots(), 0, sizeof(js::HeapSlot
) * GetGCKindSlots(kind
));
558 js::gc::TraceCreateObject(obj
);
563 /* static */ inline JSObject
*
564 JSObject::createArrayInternal(js::ExclusiveContext
* cx
, js::gc::AllocKind kind
, js::gc::InitialHeap heap
,
565 js::HandleShape shape
, js::HandleTypeObject type
)
567 // Create a new array and initialize everything except for its elements.
568 JS_ASSERT(shape
&& type
);
569 JS_ASSERT(type
->clasp() == shape
->getObjectClass());
570 JS_ASSERT(type
->clasp() == &js::ArrayObject::class_
);
571 JS_ASSERT_IF(type
->clasp()->finalize
, heap
== js::gc::TenuredHeap
);
573 // Arrays can use their fixed slots to store elements, so can't have shapes
574 // which allow named properties to be stored in the fixed slots.
575 JS_ASSERT(shape
->numFixedSlots() == 0);
577 size_t nDynamicSlots
= dynamicSlotsCount(0, shape
->slotSpan(), type
->clasp());
578 JSObject
* obj
= js::NewGCObject
<js::CanGC
>(cx
, kind
, nDynamicSlots
, heap
);
582 obj
->shape_
.init(shape
);
583 obj
->type_
.init(type
);
588 /* static */ inline js::ArrayObject
*
589 JSObject::createArray(js::ExclusiveContext
* cx
, js::gc::AllocKind kind
, js::gc::InitialHeap heap
,
590 js::HandleShape shape
, js::HandleTypeObject type
,
593 JSObject
* obj
= createArrayInternal(cx
, kind
, heap
, shape
, type
);
597 uint32_t capacity
= js::gc::GetGCKindSlots(kind
) - js::ObjectElements::VALUES_PER_HEADER
;
599 obj
->setFixedElements();
600 new (obj
->getElementsHeader()) js::ObjectElements(capacity
, length
);
602 size_t span
= shape
->slotSpan();
604 obj
->initializeSlotRange(0, span
);
606 js::gc::TraceCreateObject(obj
);
608 return &obj
->as
<js::ArrayObject
>();
611 /* static */ inline js::ArrayObject
*
612 JSObject::createArray(js::ExclusiveContext
* cx
, js::gc::InitialHeap heap
,
613 js::HandleShape shape
, js::HandleTypeObject type
,
614 js::HeapSlot
* elements
)
616 // Use the smallest allocation kind for the array, as it can't have any
617 // fixed slots (see assert in the above function) and will not be using its
619 js::gc::AllocKind kind
= js::gc::FINALIZE_OBJECT0_BACKGROUND
;
621 JSObject
* obj
= createArrayInternal(cx
, kind
, heap
, shape
, type
);
625 obj
->elements
= elements
;
627 size_t span
= shape
->slotSpan();
629 obj
->initializeSlotRange(0, span
);
631 js::gc::TraceCreateObject(obj
);
633 return &obj
->as
<js::ArrayObject
>();
637 JSObject::finish(js::FreeOp
* fop
)
639 if (hasDynamicSlots())
642 if (hasDynamicElements()) {
643 js::ObjectElements
* elements
= getElementsHeader();
644 if (elements
->isCopyOnWrite()) {
645 if (elements
->ownerObject() == this) {
646 // Don't free the elements until object finalization finishes,
647 // so that other objects can access these elements while they
648 // are themselves finalized.
649 fop
->freeLater(elements
);
652 fop
->free_(elements
);
657 /* static */ inline bool
658 JSObject::hasProperty(JSContext
* cx
, js::HandleObject obj
,
659 js::HandleId id
, bool* foundp
)
661 JS::RootedObject
pobj(cx
);
662 js::RootedShape
prop(cx
);
663 if (!lookupGeneric(cx
, obj
, id
, &pobj
, &prop
)) {
664 *foundp
= false; /* initialize to shut GCC up */
672 JSObject::nativeSetSlotIfHasType(js::Shape
* shape
, const js::Value
& value
, bool overwriting
)
674 if (!js::types::HasTypePropertyId(this, shape
->propid(), value
))
676 nativeSetSlot(shape
->slot(), value
);
679 shape
->setOverwritten();
685 JSObject::nativeSetSlotWithType(js::ExclusiveContext
* cx
, js::Shape
* shape
,
686 const js::Value
& value
, bool overwriting
)
688 nativeSetSlot(shape
->slot(), value
);
691 shape
->setOverwritten();
693 js::types::AddTypePropertyId(cx
, this, shape
->propid(), value
);
696 /* static */ inline bool
697 JSObject::getElement(JSContext
* cx
, js::HandleObject obj
, js::HandleObject receiver
,
698 uint32_t index
, js::MutableHandleValue vp
)
700 js::ElementIdOp op
= obj
->getOps()->getElement
;
702 return op(cx
, obj
, receiver
, index
, vp
);
705 if (!js::IndexToId(cx
, index
, &id
))
707 return getGeneric(cx
, obj
, receiver
, id
, vp
);
710 /* static */ inline bool
711 JSObject::getElementNoGC(JSContext
* cx
, JSObject
* obj
, JSObject
* receiver
,
712 uint32_t index
, js::Value
* vp
)
714 js::ElementIdOp op
= obj
->getOps()->getElement
;
718 if (index
> JSID_INT_MAX
)
720 return getGenericNoGC(cx
, obj
, receiver
, INT_TO_JSID(index
), vp
);
723 inline js::GlobalObject
&
724 JSObject::global() const
727 JSObject
* obj
= const_cast<JSObject
*>(this);
728 while (JSObject
* parent
= obj
->getParent())
732 * The global is read-barriered so that it is kept live by access through
733 * the JSCompartment. When accessed through a JSObject, however, the global
734 * will be already be kept live by the black JSObject's parent pointer, so
735 * does not need to be read-barriered.
737 return *compartment()->unsafeUnbarrieredMaybeGlobal();
741 JSObject::isOwnGlobal() const
743 return &global() == this;
748 PropDesc::PropDesc(const Value
& getter
, const Value
& setter
,
749 Enumerability enumerable
, Configurability configurable
)
750 : value_(UndefinedValue()),
751 get_(getter
), set_(setter
),
752 attrs(JSPROP_GETTER
| JSPROP_SETTER
| JSPROP_SHARED
|
753 (enumerable
? JSPROP_ENUMERATE
: 0) |
754 (configurable
? 0 : JSPROP_PERMANENT
)),
755 hasGet_(true), hasSet_(true),
756 hasValue_(false), hasWritable_(false), hasEnumerable_(true), hasConfigurable_(true),
759 MOZ_ASSERT(getter
.isUndefined() || IsCallable(getter
));
760 MOZ_ASSERT(setter
.isUndefined() || IsCallable(setter
));
763 static MOZ_ALWAYS_INLINE
bool
764 IsFunctionObject(const js::Value
& v
)
766 return v
.isObject() && v
.toObject().is
<JSFunction
>();
769 static MOZ_ALWAYS_INLINE
bool
770 IsFunctionObject(const js::Value
& v
, JSFunction
** fun
)
772 if (v
.isObject() && v
.toObject().is
<JSFunction
>()) {
773 *fun
= &v
.toObject().as
<JSFunction
>();
779 static MOZ_ALWAYS_INLINE
bool
780 IsNativeFunction(const js::Value
& v
)
783 return IsFunctionObject(v
, &fun
) && fun
->isNative();
786 static MOZ_ALWAYS_INLINE
bool
787 IsNativeFunction(const js::Value
& v
, JSFunction
** fun
)
789 return IsFunctionObject(v
, fun
) && (*fun
)->isNative();
792 static MOZ_ALWAYS_INLINE
bool
793 IsNativeFunction(const js::Value
& v
, JSNative native
)
796 return IsFunctionObject(v
, &fun
) && fun
->maybeNative() == native
;
800 * When we have an object of a builtin class, we don't quite know what its
801 * valueOf/toString methods are, since these methods may have been overwritten
802 * or shadowed. However, we can still do better than the general case by
803 * hard-coding the necessary properties for us to find the native we expect.
805 * TODO: a per-thread shape-based cache would be faster and simpler.
807 static MOZ_ALWAYS_INLINE
bool
808 ClassMethodIsNative(JSContext
* cx
, JSObject
* obj
, const Class
* clasp
, jsid methodid
, JSNative native
)
810 JS_ASSERT(!obj
->is
<ProxyObject
>());
811 JS_ASSERT(obj
->getClass() == clasp
);
814 if (!HasDataProperty(cx
, obj
, methodid
, &v
)) {
815 JSObject
* proto
= obj
->getProto();
816 if (!proto
|| proto
->getClass() != clasp
|| !HasDataProperty(cx
, proto
, methodid
, &v
))
820 return IsNativeFunction(v
, native
);
823 // Return whether looking up 'valueOf' on 'obj' definitely resolves to the
824 // original Object.prototype.valueOf. The method may conservatively return
825 // 'false' in the case of proxies or other non-native objects.
826 static MOZ_ALWAYS_INLINE
bool
827 HasObjectValueOf(JSObject
* obj
, JSContext
* cx
)
829 if (obj
->is
<ProxyObject
>() || !obj
->isNative())
832 jsid valueOf
= NameToId(cx
->names().valueOf
);
835 while (!HasDataProperty(cx
, obj
, valueOf
, &v
)) {
836 obj
= obj
->getProto();
837 if (!obj
|| obj
->is
<ProxyObject
>() || !obj
->isNative())
841 return IsNativeFunction(v
, obj_valueOf
);
844 /* ES5 9.1 ToPrimitive(input). */
845 static MOZ_ALWAYS_INLINE
bool
846 ToPrimitive(JSContext
* cx
, MutableHandleValue vp
)
848 if (vp
.isPrimitive())
851 JSObject
* obj
= &vp
.toObject();
853 /* Optimize new String(...).valueOf(). */
854 if (obj
->is
<StringObject
>()) {
855 jsid id
= NameToId(cx
->names().valueOf
);
856 if (ClassMethodIsNative(cx
, obj
, &StringObject::class_
, id
, js_str_toString
)) {
857 vp
.setString(obj
->as
<StringObject
>().unbox());
862 /* Optimize new Number(...).valueOf(). */
863 if (obj
->is
<NumberObject
>()) {
864 jsid id
= NameToId(cx
->names().valueOf
);
865 if (ClassMethodIsNative(cx
, obj
, &NumberObject::class_
, id
, js_num_valueOf
)) {
866 vp
.setNumber(obj
->as
<NumberObject
>().unbox());
871 RootedObject
objRoot(cx
, obj
);
872 return JSObject::defaultValue(cx
, objRoot
, JSTYPE_VOID
, vp
);
875 /* ES5 9.1 ToPrimitive(input, PreferredType). */
876 static MOZ_ALWAYS_INLINE
bool
877 ToPrimitive(JSContext
* cx
, JSType preferredType
, MutableHandleValue vp
)
879 JS_ASSERT(preferredType
!= JSTYPE_VOID
); /* Use the other ToPrimitive! */
880 if (vp
.isPrimitive())
882 RootedObject
obj(cx
, &vp
.toObject());
883 return JSObject::defaultValue(cx
, obj
, preferredType
, vp
);
887 * Return true if this is a compiler-created internal function accessed by
888 * its own object. Such a function object must not be accessible to script
892 IsInternalFunctionObject(JSObject
* funobj
)
894 JSFunction
* fun
= &funobj
->as
<JSFunction
>();
895 return fun
->isLambda() && !funobj
->getParent();
898 class AutoPropDescVector
: public AutoVectorRooter
<PropDesc
>
901 explicit AutoPropDescVector(JSContext
* cx
902 MOZ_GUARD_OBJECT_NOTIFIER_PARAM
)
903 : AutoVectorRooter
<PropDesc
>(cx
, DESCVECTOR
)
905 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
908 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
912 * Make an object with the specified prototype. If parent is null, it will
913 * default to the prototype's global if the prototype is non-null.
916 NewObjectWithGivenProto(ExclusiveContext
* cx
, const js::Class
* clasp
, TaggedProto proto
, JSObject
* parent
,
917 gc::AllocKind allocKind
, NewObjectKind newKind
);
920 NewObjectWithGivenProto(ExclusiveContext
* cx
, const js::Class
* clasp
, TaggedProto proto
, JSObject
* parent
,
921 NewObjectKind newKind
= GenericObject
)
923 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
924 return NewObjectWithGivenProto(cx
, clasp
, proto
, parent
, allocKind
, newKind
);
928 NewObjectWithGivenProto(ExclusiveContext
* cx
, const js::Class
* clasp
, JSObject
* proto
, JSObject
* parent
,
929 NewObjectKind newKind
= GenericObject
)
931 return NewObjectWithGivenProto(cx
, clasp
, TaggedProto(proto
), parent
, newKind
);
935 FindProto(ExclusiveContext
* cx
, const js::Class
* clasp
, MutableHandleObject proto
)
937 if (!FindClassPrototype(cx
, proto
, clasp
))
941 // We're looking for the prototype of a class that is currently being
942 // resolved; the global object's resolve hook is on the
943 // stack. js::FindClassPrototype detects this goofy case and returns
944 // true with proto null. Fall back on Object.prototype.
945 JS_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp
) == JSProto_Null
);
946 return GetBuiltinPrototype(cx
, JSProto_Object
, proto
);
952 * Make an object with the prototype set according to the specified prototype or class:
954 * if proto is non-null:
955 * use the specified proto
956 * for a built-in class:
957 * use the memoized original value of the class constructor .prototype
960 * the current value of .prototype
964 * The class prototype will be fetched from the parent's global. If global is
965 * null, the context's active global will be used, and the resulting object's
966 * parent will be that global.
969 NewObjectWithClassProtoCommon(ExclusiveContext
* cx
, const js::Class
* clasp
, JSObject
* proto
, JSObject
* parent
,
970 gc::AllocKind allocKind
, NewObjectKind newKind
);
973 NewObjectWithClassProto(ExclusiveContext
* cx
, const js::Class
* clasp
, JSObject
* proto
, JSObject
* parent
,
974 gc::AllocKind allocKind
, NewObjectKind newKind
= GenericObject
)
976 return NewObjectWithClassProtoCommon(cx
, clasp
, proto
, parent
, allocKind
, newKind
);
980 NewObjectWithClassProto(ExclusiveContext
* cx
, const js::Class
* clasp
, JSObject
* proto
, JSObject
* parent
,
981 NewObjectKind newKind
= GenericObject
)
983 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
984 return NewObjectWithClassProto(cx
, clasp
, proto
, parent
, allocKind
, newKind
);
989 NewObjectWithProto(ExclusiveContext
* cx
, JSObject
* proto
, JSObject
* parent
,
990 NewObjectKind newKind
= GenericObject
)
992 JSObject
* obj
= NewObjectWithClassProto(cx
, &T::class_
, proto
, parent
, newKind
);
996 return &obj
->as
<T
>();
1000 * Create a native instance of the given class with parent and proto set
1001 * according to the context's active global.
1004 NewBuiltinClassInstance(ExclusiveContext
* cx
, const Class
* clasp
, gc::AllocKind allocKind
,
1005 NewObjectKind newKind
= GenericObject
)
1007 return NewObjectWithClassProto(cx
, clasp
, nullptr, nullptr, allocKind
, newKind
);
1011 NewBuiltinClassInstance(ExclusiveContext
* cx
, const Class
* clasp
, NewObjectKind newKind
= GenericObject
)
1013 gc::AllocKind allocKind
= gc::GetGCObjectKind(clasp
);
1014 return NewBuiltinClassInstance(cx
, clasp
, allocKind
, newKind
);
1017 template<typename T
>
1019 NewBuiltinClassInstance(ExclusiveContext
* cx
, NewObjectKind newKind
= GenericObject
)
1021 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, newKind
);
1025 return &obj
->as
<T
>();
1028 template<typename T
>
1030 NewBuiltinClassInstance(ExclusiveContext
* cx
, gc::AllocKind allocKind
, NewObjectKind newKind
= GenericObject
)
1032 JSObject
* obj
= NewBuiltinClassInstance(cx
, &T::class_
, allocKind
, newKind
);
1036 return &obj
->as
<T
>();
1039 // Used to optimize calls to (new Object())
1041 NewObjectScriptedCall(JSContext
* cx
, MutableHandleObject obj
);
1043 /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
1044 static inline JSObject
*
1045 CopyInitializerObject(JSContext
* cx
, HandleObject baseobj
, NewObjectKind newKind
= GenericObject
)
1047 JS_ASSERT(baseobj
->getClass() == &JSObject::class_
);
1048 JS_ASSERT(!baseobj
->inDictionaryMode());
1050 gc::AllocKind allocKind
= gc::GetGCObjectFixedSlotsKind(baseobj
->numFixedSlots());
1051 allocKind
= gc::GetBackgroundAllocKind(allocKind
);
1052 JS_ASSERT_IF(baseobj
->isTenured(), allocKind
== baseobj
->tenuredGetAllocKind());
1053 RootedObject
obj(cx
);
1054 obj
= NewBuiltinClassInstance(cx
, &JSObject::class_
, allocKind
, newKind
);
1058 RootedObject
metadata(cx
, obj
->getMetadata());
1059 RootedShape
lastProp(cx
, baseobj
->lastProperty());
1060 if (!JSObject::setLastProperty(cx
, obj
, lastProp
))
1062 if (metadata
&& !JSObject::setMetadata(cx
, obj
, metadata
))
1069 NewObjectWithType(JSContext
* cx
, HandleTypeObject type
, JSObject
* parent
, gc::AllocKind allocKind
,
1070 NewObjectKind newKind
= GenericObject
);
1073 NewObjectWithType(JSContext
* cx
, HandleTypeObject type
, JSObject
* parent
,
1074 NewObjectKind newKind
= GenericObject
)
1076 gc::AllocKind allocKind
= gc::GetGCObjectKind(type
->clasp());
1077 return NewObjectWithType(cx
, type
, parent
, allocKind
, newKind
);
1081 NewReshapedObject(JSContext
* cx
, HandleTypeObject type
, JSObject
* parent
,
1082 gc::AllocKind allocKind
, HandleShape shape
,
1083 NewObjectKind newKind
= GenericObject
);
1086 * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
1087 * the object, zero if the final size is unknown. This should only be used for
1088 * objects that do not require any fixed slots.
1090 static inline gc::AllocKind
1091 GuessObjectGCKind(size_t numSlots
)
1094 return gc::GetGCObjectKind(numSlots
);
1095 return gc::FINALIZE_OBJECT4
;
1098 static inline gc::AllocKind
1099 GuessArrayGCKind(size_t numSlots
)
1102 return gc::GetGCArrayKind(numSlots
);
1103 return gc::FINALIZE_OBJECT8
;
1107 ObjectClassIs(HandleObject obj
, ESClassValue classValue
, JSContext
* cx
)
1109 if (MOZ_UNLIKELY(obj
->is
<ProxyObject
>()))
1110 return Proxy::objectClassIs(obj
, classValue
, cx
);
1112 switch (classValue
) {
1113 case ESClass_Object
: return obj
->is
<JSObject
>();
1115 case ESClass_IsArray
:
1116 // There difference between those is only relevant for proxies.
1117 return obj
->is
<ArrayObject
>();
1118 case ESClass_Number
: return obj
->is
<NumberObject
>();
1119 case ESClass_String
: return obj
->is
<StringObject
>();
1120 case ESClass_Boolean
: return obj
->is
<BooleanObject
>();
1121 case ESClass_RegExp
: return obj
->is
<RegExpObject
>();
1122 case ESClass_ArrayBuffer
:
1123 return obj
->is
<ArrayBufferObject
>() || obj
->is
<SharedArrayBufferObject
>();
1124 case ESClass_Date
: return obj
->is
<DateObject
>();
1125 case ESClass_Set
: return obj
->is
<SetObject
>();
1126 case ESClass_Map
: return obj
->is
<MapObject
>();
1128 MOZ_CRASH("bad classValue");
1132 IsObjectWithClass(const Value
& v
, ESClassValue classValue
, JSContext
* cx
)
1136 RootedObject
obj(cx
, &v
.toObject());
1137 return ObjectClassIs(obj
, classValue
, cx
);
1142 IsArray(HandleObject obj
, JSContext
* cx
)
1144 if (obj
->is
<ArrayObject
>())
1147 return ObjectClassIs(obj
, ESClass_IsArray
, cx
);
1151 Unbox(JSContext
* cx
, HandleObject obj
, MutableHandleValue vp
)
1153 if (MOZ_UNLIKELY(obj
->is
<ProxyObject
>()))
1154 return Proxy::boxedValue_unbox(cx
, obj
, vp
);
1156 if (obj
->is
<BooleanObject
>())
1157 vp
.setBoolean(obj
->as
<BooleanObject
>().unbox());
1158 else if (obj
->is
<NumberObject
>())
1159 vp
.setNumber(obj
->as
<NumberObject
>().unbox());
1160 else if (obj
->is
<StringObject
>())
1161 vp
.setString(obj
->as
<StringObject
>().unbox());
1168 static MOZ_ALWAYS_INLINE
bool
1169 NewObjectMetadata(ExclusiveContext
* cxArg
, JSObject
** pmetadata
)
1171 // The metadata callback is invoked before each created object, except when
1172 // analysis/compilation is active, to avoid recursion. It is also skipped
1173 // when we allocate objects during a bailout, to prevent stack iterations.
1174 JS_ASSERT(!*pmetadata
);
1175 if (JSContext
* cx
= cxArg
->maybeJSContext()) {
1176 if (MOZ_UNLIKELY((size_t)cx
->compartment()->hasObjectMetadataCallback()) &&
1177 !cx
->compartment()->activeAnalysis
)
1179 // Use AutoEnterAnalysis to prohibit both any GC activity under the
1180 // callback, and any reentering of JS via Invoke() etc.
1181 types::AutoEnterAnalysis
enter(cx
);
1183 if (!cx
->compartment()->callObjectMetadataCallback(cx
, pmetadata
))
1191 DefineNativeProperty(ExclusiveContext
* cx
, HandleObject obj
, PropertyName
* name
, HandleValue value
,
1192 PropertyOp getter
, StrictPropertyOp setter
, unsigned attrs
)
1194 Rooted
<jsid
> id(cx
, NameToId(name
));
1195 return DefineNativeProperty(cx
, obj
, id
, value
, getter
, setter
, attrs
);
1201 LookupProperty(ExclusiveContext
* cx
, HandleObject obj
, PropertyName
* name
,
1202 MutableHandleObject objp
, MutableHandleShape propp
)
1204 Rooted
<jsid
> id(cx
, NameToId(name
));
1205 return LookupProperty
<CanGC
>(cx
, obj
, id
, objp
, propp
);
1209 DefineProperty(ExclusiveContext
* cx
, HandleObject obj
, PropertyName
* name
, HandleValue value
,
1210 JSPropertyOp getter
, JSStrictPropertyOp setter
, unsigned attrs
)
1212 Rooted
<jsid
> id(cx
, NameToId(name
));
1213 return DefineGeneric(cx
, obj
, id
, value
, getter
, setter
, attrs
);
1216 } /* namespace baseops */
1218 } /* namespace js */
1221 js_InitClass(JSContext
* cx
, js::HandleObject obj
, JSObject
* parent_proto
,
1222 const js::Class
* clasp
, JSNative constructor
, unsigned nargs
,
1223 const JSPropertySpec
* ps
, const JSFunctionSpec
* fs
,
1224 const JSPropertySpec
* static_ps
, const JSFunctionSpec
* static_fs
,
1225 JSObject
** ctorp
= nullptr,
1226 js::gc::AllocKind ctorKind
= JSFunction::FinalizeKind
);
1228 #endif /* jsobjinlines_h */