Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsobjinlines.h
blobf43c5dfd6d0d75750b09dcb99d346e9b488bc669
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/. */
7 #ifndef jsobjinlines_h
8 #define jsobjinlines_h
10 #include "jsobj.h"
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,
48 bool* succeeded)
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)
58 JS::RootedId id(cx);
59 if (!js::IndexToId(cx, index, &id))
60 return false;
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);
79 inline void
80 JSObject::finalize(js::FreeOp* fop)
82 js::probes::FinalizeObject(this);
84 #ifdef DEBUG
85 JS_ASSERT(isTenured());
86 if (!IsBackgroundFinalized(tenuredGetAllocKind())) {
87 /* Assert we're on the main thread. */
88 JS_ASSERT(CurrentThreadCanAccessRuntime(fop->runtime()));
90 #endif
91 const js::Class* clasp = getClass();
92 if (clasp->finalize)
93 clasp->finalize(fop, this);
95 finish(fop);
98 inline void
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());
107 shape_ = shape;
110 inline void
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));
119 inline bool
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();
136 inline void
137 JSObject::setShouldConvertDoubleElements()
139 JS_ASSERT(is<js::ArrayObject>() && !hasEmptyElements());
140 getElementsHeader()->setShouldConvertDoubleElements();
143 inline void
144 JSObject::clearShouldConvertDoubleElements()
146 JS_ASSERT(is<js::ArrayObject>() && !hasEmptyElements());
147 getElementsHeader()->clearShouldConvertDoubleElements();
150 inline bool
151 JSObject::setDenseElementIfHasType(uint32_t index, const js::Value& val)
153 if (!js::types::HasTypePropertyId(this, JSID_VOID, val))
154 return false;
155 setDenseElementMaybeConvertDouble(index, val);
156 return true;
159 inline void
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);
171 inline void
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);
180 inline void
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));
198 inline bool
199 JSObject::writeToIndexWouldMarkNotPacked(uint32_t index)
201 return getElementsHeader()->initializedLength < index;
204 inline void
205 JSObject::markDenseElementsNotPacked(js::ExclusiveContext* cx)
207 JS_ASSERT(isNative());
208 MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED);
211 inline void
212 JSObject::ensureDenseInitializedLengthNoPackedCheck(js::ThreadSafeContext* cx, uint32_t index,
213 uint32_t extra)
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
221 * for a write.
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);
230 sp++, offset++)
232 sp->init(this, js::HeapSlot::Element, offset, js::MagicValue(JS_ELEMENTS_HOLE));
234 initlen = index + extra;
238 inline void
239 JSObject::ensureDenseInitializedLength(js::ExclusiveContext* cx, uint32_t index, uint32_t extra)
241 if (writeToIndexWouldMarkNotPacked(index))
242 markDenseElementsNotPacked(cx);
243 ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
246 inline void
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);
268 return ED_SPARSE;
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.
276 if (isIndexed())
277 return ED_SPARSE;
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)) {
285 return ED_SPARSE;
288 if (!growElements(cx, requiredCapacity))
289 return ED_FAILED;
291 return ED_OK;
294 inline JSObject::EnsureDenseResult
295 JSObject::ensureDenseElementsNoPackedCheck(js::ThreadSafeContext* cx, uint32_t index, uint32_t extra)
297 JS_ASSERT(isNative());
299 if (!maybeCopyElementsForWrite(cx))
300 return ED_FAILED;
302 uint32_t currentCapacity = getDenseCapacity();
304 uint32_t requiredCapacity;
305 if (extra == 1) {
306 /* Optimize for the common case. */
307 if (index < currentCapacity) {
308 ensureDenseInitializedLengthNoPackedCheck(cx, index, 1);
309 return ED_OK;
311 requiredCapacity = index + 1;
312 if (requiredCapacity == 0) {
313 /* Overflow. */
314 return ED_SPARSE;
316 } else {
317 requiredCapacity = index + extra;
318 if (requiredCapacity < index) {
319 /* Overflow. */
320 return ED_SPARSE;
322 if (requiredCapacity <= currentCapacity) {
323 ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
324 return ED_OK;
328 EnsureDenseResult edr = extendDenseElements(cx, requiredCapacity, extra);
329 if (edr != ED_OK)
330 return edr;
332 ensureDenseInitializedLengthNoPackedCheck(cx, index, extra);
333 return ED_OK;
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,
346 uint32_t extra)
348 JS_ASSERT(!writeToIndexWouldMarkNotPacked(index));
349 return ensureDenseElementsNoPackedCheck(cx, index, extra);
352 inline js::Value
353 JSObject::getDenseOrTypedArrayElement(uint32_t idx)
355 if (is<js::TypedArrayObject>())
356 return as<js::TypedArrayObject>().getElement(idx);
357 return getDenseElement(idx);
360 inline void
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())));
379 #endif
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());
389 if (!type)
390 return false;
392 obj->type_ = type;
393 return true;
396 inline js::types::TypeObject*
397 JSObject::getType(JSContext* cx)
399 JS_ASSERT(cx->compartment() == compartment());
400 if (hasLazyType()) {
401 JS::RootedObject self(cx, this);
402 if (cx->compartment() != compartment())
403 MOZ_CRASH();
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));
416 if (!type)
417 return false;
419 obj->type_ = type;
420 return true;
423 inline void
424 JSObject::setType(js::types::TypeObject* newType)
426 JS_ASSERT(newType);
427 JS_ASSERT(!hasSingletonType());
428 type_ = newType;
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);
437 } else {
438 protop.set(obj->getTaggedProto().toObjectOrNull());
439 return true;
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");
460 return false;
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");
469 return false;
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");
479 return false;
482 /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
483 bool extensible;
484 if (!JSObject::isExtensible(cx, obj, &extensible))
485 return false;
486 if (!extensible) {
487 *succeeded = false;
488 return true;
491 /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */
492 js::RootedObject obj2(cx);
493 for (obj2 = proto; obj2; ) {
494 if (obj2 == obj) {
495 *succeeded = false;
496 return true;
499 if (!JSObject::getProto(cx, obj2, &obj2))
500 return false;
503 JS::Rooted<js::TaggedProto> taggedProto(cx, js::TaggedProto(proto));
504 return SetClassAndProto(cx, obj, obj->getClass(), taggedProto, succeeded);
507 inline bool
508 JSObject::isQualifiedVarObj()
510 if (is<js::DebugScopeObject>())
511 return as<js::DebugScopeObject>().scope().isQualifiedVarObj();
512 return lastProperty()->hasObjectFlag(js::BaseShape::QUALIFIED_VAROBJ);
515 inline bool
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);
539 if (!obj)
540 return nullptr;
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();
551 if (span)
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);
560 return 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);
579 if (!obj)
580 return nullptr;
582 obj->shape_.init(shape);
583 obj->type_.init(type);
585 return obj;
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,
591 uint32_t length)
593 JSObject* obj = createArrayInternal(cx, kind, heap, shape, type);
594 if (!obj)
595 return nullptr;
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();
603 if (span)
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
618 // fixed elements.
619 js::gc::AllocKind kind = js::gc::FINALIZE_OBJECT0_BACKGROUND;
621 JSObject* obj = createArrayInternal(cx, kind, heap, shape, type);
622 if (!obj)
623 return nullptr;
625 obj->elements = elements;
627 size_t span = shape->slotSpan();
628 if (span)
629 obj->initializeSlotRange(0, span);
631 js::gc::TraceCreateObject(obj);
633 return &obj->as<js::ArrayObject>();
636 inline void
637 JSObject::finish(js::FreeOp* fop)
639 if (hasDynamicSlots())
640 fop->free_(slots);
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);
651 } else {
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 */
665 return false;
667 *foundp = !!prop;
668 return true;
671 inline bool
672 JSObject::nativeSetSlotIfHasType(js::Shape* shape, const js::Value& value, bool overwriting)
674 if (!js::types::HasTypePropertyId(this, shape->propid(), value))
675 return false;
676 nativeSetSlot(shape->slot(), value);
678 if (overwriting)
679 shape->setOverwritten();
681 return true;
684 inline void
685 JSObject::nativeSetSlotWithType(js::ExclusiveContext* cx, js::Shape* shape,
686 const js::Value& value, bool overwriting)
688 nativeSetSlot(shape->slot(), value);
690 if (overwriting)
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;
701 if (op)
702 return op(cx, obj, receiver, index, vp);
704 JS::RootedId id(cx);
705 if (!js::IndexToId(cx, index, &id))
706 return false;
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;
715 if (op)
716 return false;
718 if (index > JSID_INT_MAX)
719 return false;
720 return getGenericNoGC(cx, obj, receiver, INT_TO_JSID(index), vp);
723 inline js::GlobalObject&
724 JSObject::global() const
726 #ifdef DEBUG
727 JSObject* obj = const_cast<JSObject*>(this);
728 while (JSObject* parent = obj->getParent())
729 obj = parent;
730 #endif
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();
740 inline bool
741 JSObject::isOwnGlobal() const
743 return &global() == this;
746 namespace js {
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),
757 isUndefined_(false)
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>();
774 return true;
776 return false;
779 static MOZ_ALWAYS_INLINE bool
780 IsNativeFunction(const js::Value& v)
782 JSFunction* fun;
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)
795 JSFunction* fun;
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);
813 Value v;
814 if (!HasDataProperty(cx, obj, methodid, &v)) {
815 JSObject* proto = obj->getProto();
816 if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, proto, methodid, &v))
817 return false;
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())
830 return false;
832 jsid valueOf = NameToId(cx->names().valueOf);
834 Value v;
835 while (!HasDataProperty(cx, obj, valueOf, &v)) {
836 obj = obj->getProto();
837 if (!obj || obj->is<ProxyObject>() || !obj->isNative())
838 return false;
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())
849 return true;
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());
858 return true;
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());
867 return true;
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())
881 return true;
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
889 * or embedding code.
891 inline bool
892 IsInternalFunctionObject(JSObject* funobj)
894 JSFunction* fun = &funobj->as<JSFunction>();
895 return fun->isLambda() && !funobj->getParent();
898 class AutoPropDescVector : public AutoVectorRooter<PropDesc>
900 public:
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.
915 JSObject*
916 NewObjectWithGivenProto(ExclusiveContext* cx, const js::Class* clasp, TaggedProto proto, JSObject* parent,
917 gc::AllocKind allocKind, NewObjectKind newKind);
919 inline JSObject*
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);
927 inline JSObject*
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);
934 inline bool
935 FindProto(ExclusiveContext* cx, const js::Class* clasp, MutableHandleObject proto)
937 if (!FindClassPrototype(cx, proto, clasp))
938 return false;
940 if (!proto) {
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);
948 return true;
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
958 * property object
959 * else if available
960 * the current value of .prototype
961 * else
962 * Object.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.
968 JSObject*
969 NewObjectWithClassProtoCommon(ExclusiveContext* cx, const js::Class* clasp, JSObject* proto, JSObject* parent,
970 gc::AllocKind allocKind, NewObjectKind newKind);
972 inline JSObject*
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);
979 inline JSObject*
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);
987 template<typename T>
988 inline T*
989 NewObjectWithProto(ExclusiveContext* cx, JSObject* proto, JSObject* parent,
990 NewObjectKind newKind = GenericObject)
992 JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, parent, newKind);
993 if (!obj)
994 return nullptr;
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.
1003 inline JSObject*
1004 NewBuiltinClassInstance(ExclusiveContext* cx, const Class* clasp, gc::AllocKind allocKind,
1005 NewObjectKind newKind = GenericObject)
1007 return NewObjectWithClassProto(cx, clasp, nullptr, nullptr, allocKind, newKind);
1010 inline JSObject*
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>
1018 inline T*
1019 NewBuiltinClassInstance(ExclusiveContext* cx, NewObjectKind newKind = GenericObject)
1021 JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
1022 if (!obj)
1023 return nullptr;
1025 return &obj->as<T>();
1028 template<typename T>
1029 inline T*
1030 NewBuiltinClassInstance(ExclusiveContext* cx, gc::AllocKind allocKind, NewObjectKind newKind = GenericObject)
1032 JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind);
1033 if (!obj)
1034 return nullptr;
1036 return &obj->as<T>();
1039 // Used to optimize calls to (new Object())
1040 bool
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);
1055 if (!obj)
1056 return nullptr;
1058 RootedObject metadata(cx, obj->getMetadata());
1059 RootedShape lastProp(cx, baseobj->lastProperty());
1060 if (!JSObject::setLastProperty(cx, obj, lastProp))
1061 return nullptr;
1062 if (metadata && !JSObject::setMetadata(cx, obj, metadata))
1063 return nullptr;
1065 return obj;
1068 JSObject*
1069 NewObjectWithType(JSContext* cx, HandleTypeObject type, JSObject* parent, gc::AllocKind allocKind,
1070 NewObjectKind newKind = GenericObject);
1072 inline JSObject*
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);
1080 JSObject*
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)
1093 if (numSlots)
1094 return gc::GetGCObjectKind(numSlots);
1095 return gc::FINALIZE_OBJECT4;
1098 static inline gc::AllocKind
1099 GuessArrayGCKind(size_t numSlots)
1101 if (numSlots)
1102 return gc::GetGCArrayKind(numSlots);
1103 return gc::FINALIZE_OBJECT8;
1106 inline bool
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>();
1114 case ESClass_Array:
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");
1131 inline bool
1132 IsObjectWithClass(const Value& v, ESClassValue classValue, JSContext* cx)
1134 if (!v.isObject())
1135 return false;
1136 RootedObject obj(cx, &v.toObject());
1137 return ObjectClassIs(obj, classValue, cx);
1140 // ES6 7.2.2
1141 inline bool
1142 IsArray(HandleObject obj, JSContext* cx)
1144 if (obj->is<ArrayObject>())
1145 return true;
1147 return ObjectClassIs(obj, ESClass_IsArray, cx);
1150 inline bool
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());
1162 else
1163 vp.setUndefined();
1165 return true;
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))
1184 return false;
1187 return true;
1190 inline bool
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);
1198 namespace baseops {
1200 inline bool
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);
1208 inline bool
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 */
1220 extern JSObject*
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 */