| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef vm_ObjectImpl_inl_h |
| #define vm_ObjectImpl_inl_h |
| |
| #include "mozilla/Assertions.h" |
| |
| #include "jscompartment.h" |
| #include "jsgc.h" |
| #include "jsproxy.h" |
| |
| #include "gc/Heap.h" |
| #include "gc/Marking.h" |
| #include "js/TemplateLib.h" |
| #include "vm/Interpreter.h" |
| #include "vm/ObjectImpl.h" |
| |
| #include "gc/Barrier-inl.h" |
| |
| namespace js { |
| |
| static MOZ_ALWAYS_INLINE void |
| Debug_SetSlotRangeToCrashOnTouch(HeapSlot *vec, uint32_t len) |
| { |
| #ifdef DEBUG |
| Debug_SetValueRangeToCrashOnTouch((Value *) vec, len); |
| #endif |
| } |
| |
| static MOZ_ALWAYS_INLINE void |
| Debug_SetSlotRangeToCrashOnTouch(HeapSlot *begin, HeapSlot *end) |
| { |
| #ifdef DEBUG |
| Debug_SetValueRangeToCrashOnTouch((Value *) begin, end - begin); |
| #endif |
| } |
| |
| } // namespace js |
| |
| inline JSCompartment * |
| js::ObjectImpl::compartment() const |
| { |
| return lastProperty()->base()->compartment(); |
| } |
| |
| inline js::TaggedProto |
| js::ObjectImpl::getTaggedProto() const |
| { |
| return TaggedProto(getProto()); |
| } |
| |
| inline js::Shape * |
| js::ObjectImpl::nativeLookup(JSContext *cx, PropertyId pid) |
| { |
| return nativeLookup(cx, pid.asId()); |
| } |
| |
| inline js::Shape * |
| js::ObjectImpl::nativeLookup(JSContext *cx, PropertyName *name) |
| { |
| return nativeLookup(cx, NameToId(name)); |
| } |
| |
| inline bool |
| js::ObjectImpl::nativeContains(JSContext *cx, jsid id) |
| { |
| return nativeLookup(cx, id) != NULL; |
| } |
| |
| inline bool |
| js::ObjectImpl::nativeContains(JSContext *cx, PropertyName *name) |
| { |
| return nativeLookup(cx, name) != NULL; |
| } |
| |
| inline bool |
| js::ObjectImpl::nativeContains(JSContext *cx, Shape *shape) |
| { |
| return nativeLookup(cx, shape->propid()) == shape; |
| } |
| |
| inline js::Shape * |
| js::ObjectImpl::nativeLookupPure(PropertyId pid) |
| { |
| return nativeLookupPure(pid.asId()); |
| } |
| |
| inline js::Shape * |
| js::ObjectImpl::nativeLookupPure(PropertyName *name) |
| { |
| return nativeLookupPure(NameToId(name)); |
| } |
| |
| inline bool |
| js::ObjectImpl::nativeContainsPure(jsid id) |
| { |
| return nativeLookupPure(id) != NULL; |
| } |
| |
| inline bool |
| js::ObjectImpl::nativeContainsPure(PropertyName *name) |
| { |
| return nativeContainsPure(NameToId(name)); |
| } |
| |
| inline bool |
| js::ObjectImpl::nativeContainsPure(Shape *shape) |
| { |
| return nativeLookupPure(shape->propid()) == shape; |
| } |
| |
| inline bool |
| js::ObjectImpl::isExtensible() const |
| { |
| if (this->isProxy()) |
| return Proxy::isExtensible(const_cast<JSObject*>(this->asObjectPtr())); |
| |
| // [[Extensible]] for ordinary non-proxy objects is an object flag. |
| return !lastProperty()->hasObjectFlag(BaseShape::NOT_EXTENSIBLE); |
| } |
| |
| inline uint32_t |
| js::ObjectImpl::getDenseInitializedLength() |
| { |
| MOZ_ASSERT(isNative()); |
| return getElementsHeader()->initializedLength; |
| } |
| |
| inline uint32_t |
| js::ObjectImpl::getDenseCapacity() |
| { |
| MOZ_ASSERT(isNative()); |
| return getElementsHeader()->capacity; |
| } |
| |
| inline js::HeapSlotArray |
| js::ObjectImpl::getDenseElements() |
| { |
| MOZ_ASSERT(isNative()); |
| return HeapSlotArray(elements); |
| } |
| |
| inline const js::Value & |
| js::ObjectImpl::getDenseElement(uint32_t idx) |
| { |
| MOZ_ASSERT(isNative() && idx < getDenseInitializedLength()); |
| return elements[idx]; |
| } |
| |
| inline bool |
| js::ObjectImpl::containsDenseElement(uint32_t idx) |
| { |
| MOZ_ASSERT(isNative()); |
| return idx < getDenseInitializedLength() && !elements[idx].isMagic(JS_ELEMENTS_HOLE); |
| } |
| |
| inline void |
| js::ObjectImpl::getSlotRangeUnchecked(uint32_t start, uint32_t length, |
| HeapSlot **fixedStart, HeapSlot **fixedEnd, |
| HeapSlot **slotsStart, HeapSlot **slotsEnd) |
| { |
| MOZ_ASSERT(start + length >= start); |
| |
| uint32_t fixed = numFixedSlots(); |
| if (start < fixed) { |
| if (start + length < fixed) { |
| *fixedStart = &fixedSlots()[start]; |
| *fixedEnd = &fixedSlots()[start + length]; |
| *slotsStart = *slotsEnd = NULL; |
| } else { |
| uint32_t localCopy = fixed - start; |
| *fixedStart = &fixedSlots()[start]; |
| *fixedEnd = &fixedSlots()[start + localCopy]; |
| *slotsStart = &slots[0]; |
| *slotsEnd = &slots[length - localCopy]; |
| } |
| } else { |
| *fixedStart = *fixedEnd = NULL; |
| *slotsStart = &slots[start - fixed]; |
| *slotsEnd = &slots[start - fixed + length]; |
| } |
| } |
| |
| inline void |
| js::ObjectImpl::getSlotRange(uint32_t start, uint32_t length, |
| HeapSlot **fixedStart, HeapSlot **fixedEnd, |
| HeapSlot **slotsStart, HeapSlot **slotsEnd) |
| { |
| MOZ_ASSERT(slotInRange(start + length, SENTINEL_ALLOWED)); |
| getSlotRangeUnchecked(start, length, fixedStart, fixedEnd, slotsStart, slotsEnd); |
| } |
| |
| inline void |
| js::ObjectImpl::invalidateSlotRange(uint32_t start, uint32_t length) |
| { |
| #ifdef DEBUG |
| HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd; |
| getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd); |
| Debug_SetSlotRangeToCrashOnTouch(fixedStart, fixedEnd); |
| Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd); |
| #endif /* DEBUG */ |
| } |
| |
| inline bool |
| js::ObjectImpl::isNative() const |
| { |
| return lastProperty()->isNative(); |
| } |
| |
| inline bool |
| js::ObjectImpl::isProxy() const |
| { |
| return js::IsProxy(const_cast<JSObject*>(this->asObjectPtr())); |
| } |
| |
| inline js::HeapSlot & |
| js::ObjectImpl::nativeGetSlotRef(uint32_t slot) |
| { |
| MOZ_ASSERT(isNative()); |
| MOZ_ASSERT(slot < slotSpan()); |
| return getSlotRef(slot); |
| } |
| |
| inline const js::Value & |
| js::ObjectImpl::nativeGetSlot(uint32_t slot) const |
| { |
| MOZ_ASSERT(isNative()); |
| MOZ_ASSERT(slot < slotSpan()); |
| return getSlot(slot); |
| } |
| |
| #ifdef DEBUG |
| inline bool |
| IsObjectValueInCompartment(js::Value v, JSCompartment *comp) |
| { |
| if (!v.isObject()) |
| return true; |
| return v.toObject().compartment() == comp; |
| } |
| #endif |
| |
| inline void |
| js::ObjectImpl::setSlot(uint32_t slot, const js::Value &value) |
| { |
| MOZ_ASSERT(slotInRange(slot)); |
| MOZ_ASSERT(IsObjectValueInCompartment(value, asObjectPtr()->compartment())); |
| getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
| } |
| |
| inline void |
| js::ObjectImpl::setCrossCompartmentSlot(uint32_t slot, const js::Value &value) |
| { |
| MOZ_ASSERT(slotInRange(slot)); |
| getSlotRef(slot).set(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
| } |
| |
| inline void |
| js::ObjectImpl::initSlot(uint32_t slot, const js::Value &value) |
| { |
| MOZ_ASSERT(getSlot(slot).isUndefined()); |
| MOZ_ASSERT(slotInRange(slot)); |
| MOZ_ASSERT(IsObjectValueInCompartment(value, asObjectPtr()->compartment())); |
| initSlotUnchecked(slot, value); |
| } |
| |
| inline void |
| js::ObjectImpl::initCrossCompartmentSlot(uint32_t slot, const js::Value &value) |
| { |
| MOZ_ASSERT(getSlot(slot).isUndefined()); |
| MOZ_ASSERT(slotInRange(slot)); |
| initSlotUnchecked(slot, value); |
| } |
| |
| inline void |
| js::ObjectImpl::initSlotUnchecked(uint32_t slot, const js::Value &value) |
| { |
| getSlotAddressUnchecked(slot)->init(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
| } |
| |
| inline void |
| js::ObjectImpl::setFixedSlot(uint32_t slot, const js::Value &value) |
| { |
| MOZ_ASSERT(slot < numFixedSlots()); |
| fixedSlots()[slot].set(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
| } |
| |
| inline void |
| js::ObjectImpl::initFixedSlot(uint32_t slot, const js::Value &value) |
| { |
| MOZ_ASSERT(slot < numFixedSlots()); |
| fixedSlots()[slot].init(this->asObjectPtr(), HeapSlot::Slot, slot, value); |
| } |
| |
| inline uint32_t |
| js::ObjectImpl::slotSpan() const |
| { |
| if (inDictionaryMode()) |
| return lastProperty()->base()->slotSpan(); |
| return lastProperty()->slotSpan(); |
| } |
| |
| inline uint32_t |
| js::ObjectImpl::numDynamicSlots() const |
| { |
| return dynamicSlotsCount(numFixedSlots(), slotSpan()); |
| } |
| |
| inline JSClass * |
| js::ObjectImpl::getJSClass() const |
| { |
| return Jsvalify(getClass()); |
| } |
| |
| inline const js::ObjectOps * |
| js::ObjectImpl::getOps() const |
| { |
| return &getClass()->ops; |
| } |
| |
| inline bool |
| js::ObjectImpl::isDelegate() const |
| { |
| return lastProperty()->hasObjectFlag(BaseShape::DELEGATE); |
| } |
| |
| inline bool |
| js::ObjectImpl::inDictionaryMode() const |
| { |
| return lastProperty()->inDictionary(); |
| } |
| |
| /* static */ inline uint32_t |
| js::ObjectImpl::dynamicSlotsCount(uint32_t nfixed, uint32_t span) |
| { |
| if (span <= nfixed) |
| return 0; |
| span -= nfixed; |
| if (span <= SLOT_CAPACITY_MIN) |
| return SLOT_CAPACITY_MIN; |
| |
| uint32_t slots = RoundUpPow2(span); |
| MOZ_ASSERT(slots >= span); |
| return slots; |
| } |
| |
| inline size_t |
| js::ObjectImpl::tenuredSizeOfThis() const |
| { |
| return js::gc::Arena::thingSize(tenuredGetAllocKind()); |
| } |
| |
| JS_ALWAYS_INLINE JS::Zone * |
| js::ObjectImpl::zone() const |
| { |
| JS_ASSERT(InSequentialOrExclusiveParallelSection()); |
| return shape_->zone(); |
| } |
| |
| /* static */ inline void |
| js::ObjectImpl::readBarrier(ObjectImpl *obj) |
| { |
| #ifdef JSGC_INCREMENTAL |
| Zone *zone = obj->zone(); |
| if (zone->needsBarrier()) { |
| MOZ_ASSERT(!zone->rt->isHeapMajorCollecting()); |
| JSObject *tmp = obj->asObjectPtr(); |
| MarkObjectUnbarriered(zone->barrierTracer(), &tmp, "read barrier"); |
| MOZ_ASSERT(tmp == obj->asObjectPtr()); |
| } |
| #endif |
| } |
| |
| inline void |
| js::ObjectImpl::privateWriteBarrierPre(void **old) |
| { |
| #ifdef JSGC_INCREMENTAL |
| Zone *zone = this->zone(); |
| if (zone->needsBarrier()) { |
| if (*old && getClass()->trace) |
| getClass()->trace(zone->barrierTracer(), this->asObjectPtr()); |
| } |
| #endif |
| } |
| |
| inline void |
| js::ObjectImpl::privateWriteBarrierPost(void **pprivate) |
| { |
| #ifdef JSGC_GENERATIONAL |
| runtime()->gcStoreBuffer.putCell(reinterpret_cast<js::gc::Cell **>(pprivate)); |
| #endif |
| } |
| |
| /* static */ inline void |
| js::ObjectImpl::writeBarrierPre(ObjectImpl *obj) |
| { |
| #ifdef JSGC_INCREMENTAL |
| /* |
| * This would normally be a null test, but TypeScript::global uses 0x1 as a |
| * special value. |
| */ |
| if (IsNullTaggedPointer(obj) || !obj->runtime()->needsBarrier()) |
| return; |
| |
| Zone *zone = obj->zone(); |
| if (zone->needsBarrier()) { |
| MOZ_ASSERT(!zone->rt->isHeapMajorCollecting()); |
| JSObject *tmp = obj->asObjectPtr(); |
| MarkObjectUnbarriered(zone->barrierTracer(), &tmp, "write barrier"); |
| MOZ_ASSERT(tmp == obj->asObjectPtr()); |
| } |
| #endif |
| } |
| |
| /* static */ inline void |
| js::ObjectImpl::writeBarrierPost(ObjectImpl *obj, void *addr) |
| { |
| #ifdef JSGC_GENERATIONAL |
| if (IsNullTaggedPointer(obj)) |
| return; |
| obj->runtime()->gcStoreBuffer.putCell((Cell **)addr); |
| #endif |
| } |
| |
| inline void |
| js::ObjectImpl::setPrivate(void *data) |
| { |
| void **pprivate = &privateRef(numFixedSlots()); |
| privateWriteBarrierPre(pprivate); |
| *pprivate = data; |
| } |
| |
| inline void |
| js::ObjectImpl::setPrivateGCThing(js::gc::Cell *cell) |
| { |
| void **pprivate = &privateRef(numFixedSlots()); |
| privateWriteBarrierPre(pprivate); |
| *pprivate = reinterpret_cast<void *>(cell); |
| privateWriteBarrierPost(pprivate); |
| } |
| |
| inline void |
| js::ObjectImpl::setPrivateUnbarriered(void *data) |
| { |
| void **pprivate = &privateRef(numFixedSlots()); |
| *pprivate = data; |
| } |
| |
| inline void |
| js::ObjectImpl::initPrivate(void *data) |
| { |
| privateRef(numFixedSlots()) = data; |
| } |
| |
| #endif /* vm_ObjectImpl_inl_h */ |