michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jsobjinlines_h michael@0: #define jsobjinlines_h michael@0: michael@0: #include "jsobj.h" michael@0: michael@0: #include "vm/ArrayObject.h" michael@0: #include "vm/DateObject.h" michael@0: #include "vm/NumberObject.h" michael@0: #include "vm/Probes.h" michael@0: #include "vm/ScopeObject.h" michael@0: #include "vm/StringObject.h" michael@0: michael@0: #include "jsatominlines.h" michael@0: #include "jscompartmentinlines.h" michael@0: #include "jsgcinlines.h" michael@0: #include "jsinferinlines.h" michael@0: michael@0: #include "vm/ObjectImpl-inl.h" michael@0: michael@0: /* static */ inline bool michael@0: JSObject::setGenericAttributes(JSContext *cx, js::HandleObject obj, michael@0: js::HandleId id, unsigned *attrsp) michael@0: { michael@0: js::types::MarkTypePropertyNonData(cx, obj, id); michael@0: js::GenericAttributesOp op = obj->getOps()->setGenericAttributes; michael@0: return (op ? op : js::baseops::SetAttributes)(cx, obj, id, attrsp); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::changePropertyAttributes(JSContext *cx, js::HandleObject obj, michael@0: js::HandleShape shape, unsigned attrs) michael@0: { michael@0: return !!changeProperty(cx, obj, shape, attrs, 0, michael@0: shape->getter(), shape->setter()); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::deleteProperty(JSContext *cx, js::HandleObject obj, js::HandlePropertyName name, michael@0: bool *succeeded) michael@0: { michael@0: JS::RootedId id(cx, js::NameToId(name)); michael@0: js::types::MarkTypePropertyNonData(cx, obj, id); michael@0: js::DeletePropertyOp op = obj->getOps()->deleteProperty; michael@0: return (op ? op : js::baseops::DeleteProperty)(cx, obj, name, succeeded); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::deleteElement(JSContext *cx, js::HandleObject obj, uint32_t index, bool *succeeded) michael@0: { michael@0: JS::RootedId id(cx); michael@0: if (!js::IndexToId(cx, index, &id)) michael@0: return false; michael@0: js::types::MarkTypePropertyNonData(cx, obj, id); michael@0: js::DeleteElementOp op = obj->getOps()->deleteElement; michael@0: return (op ? op : js::baseops::DeleteElement)(cx, obj, index, succeeded); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, michael@0: JS::HandleObject callable) michael@0: { michael@0: js::WatchOp op = obj->getOps()->watch; michael@0: return (op ? op : js::baseops::Watch)(cx, obj, id, callable); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id) michael@0: { michael@0: js::UnwatchOp op = obj->getOps()->unwatch; michael@0: return (op ? op : js::baseops::Unwatch)(cx, obj, id); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::finalize(js::FreeOp *fop) michael@0: { michael@0: js::probes::FinalizeObject(this); michael@0: michael@0: #ifdef DEBUG michael@0: JS_ASSERT(isTenured()); michael@0: if (!IsBackgroundFinalized(tenuredGetAllocKind())) { michael@0: /* Assert we're on the main thread. */ michael@0: JS_ASSERT(CurrentThreadCanAccessRuntime(fop->runtime())); michael@0: } michael@0: #endif michael@0: const js::Class *clasp = getClass(); michael@0: if (clasp->finalize) michael@0: clasp->finalize(fop, this); michael@0: michael@0: finish(fop); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::setLastPropertyInfallible(js::Shape *shape) michael@0: { michael@0: JS_ASSERT(!shape->inDictionary()); michael@0: JS_ASSERT(shape->compartment() == compartment()); michael@0: JS_ASSERT(!inDictionaryMode()); michael@0: JS_ASSERT(slotSpan() == shape->slotSpan()); michael@0: JS_ASSERT(numFixedSlots() == shape->numFixedSlots()); michael@0: michael@0: shape_ = shape; michael@0: } michael@0: michael@0: inline void michael@0: JSObject::removeLastProperty(js::ExclusiveContext *cx) michael@0: { michael@0: JS_ASSERT(canRemoveLastProperty()); michael@0: JS::RootedObject self(cx, this); michael@0: js::RootedShape prev(cx, lastProperty()->previous()); michael@0: JS_ALWAYS_TRUE(setLastProperty(cx, self, prev)); michael@0: } michael@0: michael@0: inline bool michael@0: JSObject::canRemoveLastProperty() michael@0: { michael@0: /* michael@0: * Check that the information about the object stored in the last michael@0: * property's base shape is consistent with that stored in the previous michael@0: * shape. If not consistent, then the last property cannot be removed as it michael@0: * will induce a change in the object itself, and the object must be michael@0: * converted to dictionary mode instead. See BaseShape comment in jsscope.h michael@0: */ michael@0: JS_ASSERT(!inDictionaryMode()); michael@0: js::Shape *previous = lastProperty()->previous().get(); michael@0: return previous->getObjectParent() == lastProperty()->getObjectParent() michael@0: && previous->getObjectMetadata() == lastProperty()->getObjectMetadata() michael@0: && previous->getObjectFlags() == lastProperty()->getObjectFlags(); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::setShouldConvertDoubleElements() michael@0: { michael@0: JS_ASSERT(is() && !hasEmptyElements()); michael@0: getElementsHeader()->setShouldConvertDoubleElements(); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::clearShouldConvertDoubleElements() michael@0: { michael@0: JS_ASSERT(is() && !hasEmptyElements()); michael@0: getElementsHeader()->clearShouldConvertDoubleElements(); michael@0: } michael@0: michael@0: inline bool michael@0: JSObject::setDenseElementIfHasType(uint32_t index, const js::Value &val) michael@0: { michael@0: if (!js::types::HasTypePropertyId(this, JSID_VOID, val)) michael@0: return false; michael@0: setDenseElementMaybeConvertDouble(index, val); michael@0: return true; michael@0: } michael@0: michael@0: inline void michael@0: JSObject::setDenseElementWithType(js::ExclusiveContext *cx, uint32_t index, michael@0: const js::Value &val) michael@0: { michael@0: // Avoid a slow AddTypePropertyId call if the type is the same as the type michael@0: // of the previous element. michael@0: js::types::Type thisType = js::types::GetValueType(val); michael@0: if (index == 0 || js::types::GetValueType(elements[index - 1]) != thisType) michael@0: js::types::AddTypePropertyId(cx, this, JSID_VOID, thisType); michael@0: setDenseElementMaybeConvertDouble(index, val); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::initDenseElementWithType(js::ExclusiveContext *cx, uint32_t index, michael@0: const js::Value &val) michael@0: { michael@0: JS_ASSERT(!shouldConvertDoubleElements()); michael@0: js::types::AddTypePropertyId(cx, this, JSID_VOID, val); michael@0: initDenseElement(index, val); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::setDenseElementHole(js::ExclusiveContext *cx, uint32_t index) michael@0: { michael@0: js::types::MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED); michael@0: setDenseElement(index, js::MagicValue(JS_ELEMENTS_HOLE)); michael@0: } michael@0: michael@0: /* static */ inline void michael@0: JSObject::removeDenseElementForSparseIndex(js::ExclusiveContext *cx, michael@0: js::HandleObject obj, uint32_t index) michael@0: { michael@0: js::types::MarkTypeObjectFlags(cx, obj, michael@0: js::types::OBJECT_FLAG_NON_PACKED | michael@0: js::types::OBJECT_FLAG_SPARSE_INDEXES); michael@0: if (obj->containsDenseElement(index)) michael@0: obj->setDenseElement(index, js::MagicValue(JS_ELEMENTS_HOLE)); michael@0: } michael@0: michael@0: inline bool michael@0: JSObject::writeToIndexWouldMarkNotPacked(uint32_t index) michael@0: { michael@0: return getElementsHeader()->initializedLength < index; michael@0: } michael@0: michael@0: inline void michael@0: JSObject::markDenseElementsNotPacked(js::ExclusiveContext *cx) michael@0: { michael@0: JS_ASSERT(isNative()); michael@0: MarkTypeObjectFlags(cx, this, js::types::OBJECT_FLAG_NON_PACKED); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::ensureDenseInitializedLengthNoPackedCheck(js::ThreadSafeContext *cx, uint32_t index, michael@0: uint32_t extra) michael@0: { michael@0: JS_ASSERT(cx->isThreadLocal(this)); michael@0: michael@0: /* michael@0: * Ensure that the array's contents have been initialized up to index, and michael@0: * mark the elements through 'index + extra' as initialized in preparation michael@0: * for a write. michael@0: */ michael@0: JS_ASSERT(index + extra <= getDenseCapacity()); michael@0: uint32_t &initlen = getElementsHeader()->initializedLength; michael@0: michael@0: if (initlen < index + extra) { michael@0: JSRuntime *rt = runtimeFromAnyThread(); michael@0: size_t offset = initlen; michael@0: for (js::HeapSlot *sp = elements + initlen; michael@0: sp != elements + (index + extra); michael@0: sp++, offset++) michael@0: sp->init(rt, this, js::HeapSlot::Element, offset, js::MagicValue(JS_ELEMENTS_HOLE)); michael@0: initlen = index + extra; michael@0: } michael@0: } michael@0: michael@0: inline void michael@0: JSObject::ensureDenseInitializedLength(js::ExclusiveContext *cx, uint32_t index, uint32_t extra) michael@0: { michael@0: if (writeToIndexWouldMarkNotPacked(index)) michael@0: markDenseElementsNotPacked(cx); michael@0: ensureDenseInitializedLengthNoPackedCheck(cx, index, extra); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::ensureDenseInitializedLengthPreservePackedFlag(js::ThreadSafeContext *cx, michael@0: uint32_t index, uint32_t extra) michael@0: { michael@0: JS_ASSERT(!writeToIndexWouldMarkNotPacked(index)); michael@0: ensureDenseInitializedLengthNoPackedCheck(cx, index, extra); michael@0: } michael@0: michael@0: JSObject::EnsureDenseResult michael@0: JSObject::extendDenseElements(js::ThreadSafeContext *cx, michael@0: uint32_t requiredCapacity, uint32_t extra) michael@0: { michael@0: JS_ASSERT(cx->isThreadLocal(this)); michael@0: michael@0: /* michael@0: * Don't grow elements for non-extensible objects or watched objects. Dense michael@0: * elements can be added/written with no extensible or watchpoint checks as michael@0: * long as there is capacity for them. michael@0: */ michael@0: if (!nonProxyIsExtensible() || watched()) { michael@0: JS_ASSERT(getDenseCapacity() == 0); michael@0: return ED_SPARSE; michael@0: } michael@0: michael@0: /* michael@0: * Don't grow elements for objects which already have sparse indexes. michael@0: * This avoids needing to count non-hole elements in willBeSparseElements michael@0: * every time a new index is added. michael@0: */ michael@0: if (isIndexed()) michael@0: return ED_SPARSE; michael@0: michael@0: /* michael@0: * We use the extra argument also as a hint about number of non-hole michael@0: * elements to be inserted. michael@0: */ michael@0: if (requiredCapacity > MIN_SPARSE_INDEX && michael@0: willBeSparseElements(requiredCapacity, extra)) { michael@0: return ED_SPARSE; michael@0: } michael@0: michael@0: if (!growElements(cx, requiredCapacity)) michael@0: return ED_FAILED; michael@0: michael@0: return ED_OK; michael@0: } michael@0: michael@0: inline JSObject::EnsureDenseResult michael@0: JSObject::ensureDenseElementsNoPackedCheck(js::ThreadSafeContext *cx, uint32_t index, uint32_t extra) michael@0: { michael@0: JS_ASSERT(isNative()); michael@0: michael@0: uint32_t currentCapacity = getDenseCapacity(); michael@0: michael@0: uint32_t requiredCapacity; michael@0: if (extra == 1) { michael@0: /* Optimize for the common case. */ michael@0: if (index < currentCapacity) { michael@0: ensureDenseInitializedLengthNoPackedCheck(cx, index, 1); michael@0: return ED_OK; michael@0: } michael@0: requiredCapacity = index + 1; michael@0: if (requiredCapacity == 0) { michael@0: /* Overflow. */ michael@0: return ED_SPARSE; michael@0: } michael@0: } else { michael@0: requiredCapacity = index + extra; michael@0: if (requiredCapacity < index) { michael@0: /* Overflow. */ michael@0: return ED_SPARSE; michael@0: } michael@0: if (requiredCapacity <= currentCapacity) { michael@0: ensureDenseInitializedLengthNoPackedCheck(cx, index, extra); michael@0: return ED_OK; michael@0: } michael@0: } michael@0: michael@0: EnsureDenseResult edr = extendDenseElements(cx, requiredCapacity, extra); michael@0: if (edr != ED_OK) michael@0: return edr; michael@0: michael@0: ensureDenseInitializedLengthNoPackedCheck(cx, index, extra); michael@0: return ED_OK; michael@0: } michael@0: michael@0: inline JSObject::EnsureDenseResult michael@0: JSObject::ensureDenseElements(js::ExclusiveContext *cx, uint32_t index, uint32_t extra) michael@0: { michael@0: if (writeToIndexWouldMarkNotPacked(index)) michael@0: markDenseElementsNotPacked(cx); michael@0: return ensureDenseElementsNoPackedCheck(cx, index, extra); michael@0: } michael@0: michael@0: inline JSObject::EnsureDenseResult michael@0: JSObject::ensureDenseElementsPreservePackedFlag(js::ThreadSafeContext *cx, uint32_t index, michael@0: uint32_t extra) michael@0: { michael@0: JS_ASSERT(!writeToIndexWouldMarkNotPacked(index)); michael@0: return ensureDenseElementsNoPackedCheck(cx, index, extra); michael@0: } michael@0: michael@0: inline js::Value michael@0: JSObject::getDenseOrTypedArrayElement(uint32_t idx) michael@0: { michael@0: if (is()) michael@0: return as().getElement(idx); michael@0: return getDenseElement(idx); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj) michael@0: { michael@0: JS_ASSERT_IF(cx->isJSContext(), michael@0: !IsInsideNursery(cx->asJSContext()->runtime(), obj.get())); michael@0: michael@0: js::types::TypeObject *type = cx->getSingletonType(obj->getClass(), obj->getTaggedProto()); michael@0: if (!type) michael@0: return false; michael@0: michael@0: obj->type_ = type; michael@0: return true; michael@0: } michael@0: michael@0: inline js::types::TypeObject* michael@0: JSObject::getType(JSContext *cx) michael@0: { michael@0: JS_ASSERT(cx->compartment() == compartment()); michael@0: if (hasLazyType()) { michael@0: JS::RootedObject self(cx, this); michael@0: if (cx->compartment() != compartment()) michael@0: MOZ_CRASH(); michael@0: return makeLazyType(cx, self); michael@0: } michael@0: return static_cast(type_); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::clearType(JSContext *cx, js::HandleObject obj) michael@0: { michael@0: JS_ASSERT(!obj->hasSingletonType()); michael@0: JS_ASSERT(cx->compartment() == obj->compartment()); michael@0: michael@0: js::types::TypeObject *type = cx->getNewType(obj->getClass(), nullptr); michael@0: if (!type) michael@0: return false; michael@0: michael@0: obj->type_ = type; michael@0: return true; michael@0: } michael@0: michael@0: inline void michael@0: JSObject::setType(js::types::TypeObject *newType) michael@0: { michael@0: JS_ASSERT(newType); michael@0: JS_ASSERT(!hasSingletonType()); michael@0: type_ = newType; michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject protop) michael@0: { michael@0: if (obj->getTaggedProto().isLazy()) { michael@0: JS_ASSERT(obj->is()); michael@0: return js::Proxy::getPrototypeOf(cx, obj, protop); michael@0: } else { michael@0: protop.set(obj->getTaggedProto().toObjectOrNull()); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::setProto(JSContext *cx, JS::HandleObject obj, JS::HandleObject proto, bool *succeeded) michael@0: { michael@0: /* Proxies live in their own little world. */ michael@0: if (obj->getTaggedProto().isLazy()) { michael@0: JS_ASSERT(obj->is()); michael@0: return js::Proxy::setPrototypeOf(cx, obj, proto, succeeded); michael@0: } michael@0: michael@0: /* michael@0: * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which michael@0: * due to their complicated delegate-object shenanigans can't easily michael@0: * have a mutable [[Prototype]]. michael@0: */ michael@0: if (obj->is()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, michael@0: "incompatible ArrayBuffer"); michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * Disallow mutating the [[Prototype]] on Typed Objects, per the spec. michael@0: */ michael@0: if (obj->is()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, michael@0: "incompatible TypedObject"); michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * Explicitly disallow mutating the [[Prototype]] of Location objects michael@0: * for flash-related security reasons. michael@0: */ michael@0: if (!strcmp(obj->getClass()->name, "Location")) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, michael@0: "incompatible Location object"); michael@0: return false; michael@0: } michael@0: michael@0: /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ michael@0: bool extensible; michael@0: if (!JSObject::isExtensible(cx, obj, &extensible)) michael@0: return false; michael@0: if (!extensible) { michael@0: *succeeded = false; michael@0: return true; michael@0: } michael@0: michael@0: /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */ michael@0: js::RootedObject obj2(cx); michael@0: for (obj2 = proto; obj2; ) { michael@0: if (obj2 == obj) { michael@0: *succeeded = false; michael@0: return true; michael@0: } michael@0: michael@0: if (!JSObject::getProto(cx, obj2, &obj2)) michael@0: return false; michael@0: } michael@0: michael@0: return SetClassAndProto(cx, obj, obj->getClass(), proto, succeeded); michael@0: } michael@0: michael@0: inline bool michael@0: JSObject::isVarObj() michael@0: { michael@0: if (is()) michael@0: return as().scope().isVarObj(); michael@0: return lastProperty()->hasObjectFlag(js::BaseShape::VAROBJ); michael@0: } michael@0: michael@0: /* static */ inline JSObject * michael@0: JSObject::create(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, michael@0: js::HandleShape shape, js::HandleTypeObject type, michael@0: js::HeapSlot *extantSlots /* = nullptr */) michael@0: { michael@0: /* michael@0: * Callers must use dynamicSlotsCount to size the initial slot array of the michael@0: * object. We can't check the allocated capacity of the dynamic slots, but michael@0: * make sure their presence is consistent with the shape. michael@0: */ michael@0: JS_ASSERT(shape && type); michael@0: JS_ASSERT(type->clasp() == shape->getObjectClass()); michael@0: JS_ASSERT(type->clasp() != &js::ArrayObject::class_); michael@0: JS_ASSERT_IF(!ClassCanHaveFixedData(type->clasp()), michael@0: js::gc::GetGCKindSlots(kind, type->clasp()) == shape->numFixedSlots()); michael@0: JS_ASSERT_IF(type->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE, IsBackgroundFinalized(kind)); michael@0: JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap); michael@0: JS_ASSERT_IF(extantSlots, dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), michael@0: type->clasp())); michael@0: michael@0: const js::Class *clasp = type->clasp(); michael@0: size_t nDynamicSlots = 0; michael@0: if (!extantSlots) michael@0: nDynamicSlots = dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan(), clasp); michael@0: michael@0: JSObject *obj = js::NewGCObject(cx, kind, nDynamicSlots, heap); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: obj->shape_.init(shape); michael@0: obj->type_.init(type); michael@0: if (extantSlots) { michael@0: #ifdef JSGC_GENERATIONAL michael@0: if (cx->isJSContext()) michael@0: cx->asJSContext()->runtime()->gcNursery.notifyInitialSlots(obj, extantSlots); michael@0: #endif michael@0: obj->slots = extantSlots; michael@0: } michael@0: obj->elements = js::emptyObjectElements; michael@0: michael@0: if (clasp->hasPrivate()) michael@0: obj->privateRef(shape->numFixedSlots()) = nullptr; michael@0: michael@0: size_t span = shape->slotSpan(); michael@0: if (span) michael@0: obj->initializeSlotRange(0, span); michael@0: michael@0: // JSFunction's fixed slots expect POD-style initialization. michael@0: if (type->clasp()->isJSFunction()) michael@0: memset(obj->fixedSlots(), 0, sizeof(js::HeapSlot) * GetGCKindSlots(kind)); michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: /* static */ inline js::ArrayObject * michael@0: JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap, michael@0: js::HandleShape shape, js::HandleTypeObject type, michael@0: uint32_t length) michael@0: { michael@0: JS_ASSERT(shape && type); michael@0: JS_ASSERT(type->clasp() == shape->getObjectClass()); michael@0: JS_ASSERT(type->clasp() == &js::ArrayObject::class_); michael@0: JS_ASSERT_IF(type->clasp()->finalize, heap == js::gc::TenuredHeap); michael@0: michael@0: /* michael@0: * Arrays use their fixed slots to store elements, and must have enough michael@0: * space for the elements header and also be marked as having no space for michael@0: * named properties stored in those fixed slots. michael@0: */ michael@0: JS_ASSERT(shape->numFixedSlots() == 0); michael@0: size_t nDynamicSlots = dynamicSlotsCount(0, shape->slotSpan(), type->clasp()); michael@0: JSObject *obj = js::NewGCObject(cx, kind, nDynamicSlots, heap); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: uint32_t capacity = js::gc::GetGCKindSlots(kind) - js::ObjectElements::VALUES_PER_HEADER; michael@0: michael@0: obj->shape_.init(shape); michael@0: obj->type_.init(type); michael@0: obj->setFixedElements(); michael@0: new (obj->getElementsHeader()) js::ObjectElements(capacity, length); michael@0: michael@0: size_t span = shape->slotSpan(); michael@0: if (span) michael@0: obj->initializeSlotRange(0, span); michael@0: michael@0: return &obj->as(); michael@0: } michael@0: michael@0: inline void michael@0: JSObject::finish(js::FreeOp *fop) michael@0: { michael@0: if (hasDynamicSlots()) michael@0: fop->free_(slots); michael@0: michael@0: if (hasDynamicElements()) { michael@0: js::ObjectElements *elements = getElementsHeader(); michael@0: fop->free_(elements); michael@0: } michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::hasProperty(JSContext *cx, js::HandleObject obj, michael@0: js::HandleId id, bool *foundp) michael@0: { michael@0: JS::RootedObject pobj(cx); michael@0: js::RootedShape prop(cx); michael@0: if (!lookupGeneric(cx, obj, id, &pobj, &prop)) { michael@0: *foundp = false; /* initialize to shut GCC up */ michael@0: return false; michael@0: } michael@0: *foundp = !!prop; michael@0: return true; michael@0: } michael@0: michael@0: inline bool michael@0: JSObject::nativeSetSlotIfHasType(js::Shape *shape, const js::Value &value) michael@0: { michael@0: if (!js::types::HasTypePropertyId(this, shape->propid(), value)) michael@0: return false; michael@0: nativeSetSlot(shape->slot(), value); michael@0: return true; michael@0: } michael@0: michael@0: inline void michael@0: JSObject::nativeSetSlotWithType(js::ExclusiveContext *cx, js::Shape *shape, michael@0: const js::Value &value) michael@0: { michael@0: nativeSetSlot(shape->slot(), value); michael@0: js::types::AddTypePropertyId(cx, this, shape->propid(), value); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::getElement(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, michael@0: uint32_t index, js::MutableHandleValue vp) michael@0: { michael@0: js::ElementIdOp op = obj->getOps()->getElement; michael@0: if (op) michael@0: return op(cx, obj, receiver, index, vp); michael@0: michael@0: JS::RootedId id(cx); michael@0: if (!js::IndexToId(cx, index, &id)) michael@0: return false; michael@0: return getGeneric(cx, obj, receiver, id, vp); michael@0: } michael@0: michael@0: /* static */ inline bool michael@0: JSObject::getElementNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, michael@0: uint32_t index, js::Value *vp) michael@0: { michael@0: js::ElementIdOp op = obj->getOps()->getElement; michael@0: if (op) michael@0: return false; michael@0: michael@0: if (index > JSID_INT_MAX) michael@0: return false; michael@0: return getGenericNoGC(cx, obj, receiver, INT_TO_JSID(index), vp); michael@0: } michael@0: michael@0: inline js::GlobalObject & michael@0: JSObject::global() const michael@0: { michael@0: #ifdef DEBUG michael@0: JSObject *obj = const_cast(this); michael@0: while (JSObject *parent = obj->getParent()) michael@0: obj = parent; michael@0: #endif michael@0: return *compartment()->maybeGlobal(); michael@0: } michael@0: michael@0: inline bool michael@0: JSObject::isOwnGlobal() const michael@0: { michael@0: return &global() == this; michael@0: } michael@0: michael@0: namespace js { michael@0: michael@0: PropDesc::PropDesc(const Value &getter, const Value &setter, michael@0: Enumerability enumerable, Configurability configurable) michael@0: : pd_(UndefinedValue()), michael@0: value_(UndefinedValue()), michael@0: get_(getter), set_(setter), michael@0: attrs(JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED | michael@0: (enumerable ? JSPROP_ENUMERATE : 0) | michael@0: (configurable ? 0 : JSPROP_PERMANENT)), michael@0: hasGet_(true), hasSet_(true), michael@0: hasValue_(false), hasWritable_(false), hasEnumerable_(true), hasConfigurable_(true), michael@0: isUndefined_(false) michael@0: { michael@0: MOZ_ASSERT(getter.isUndefined() || js_IsCallable(getter)); michael@0: MOZ_ASSERT(setter.isUndefined() || js_IsCallable(setter)); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: IsFunctionObject(const js::Value &v) michael@0: { michael@0: return v.isObject() && v.toObject().is(); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: IsFunctionObject(const js::Value &v, JSFunction **fun) michael@0: { michael@0: if (v.isObject() && v.toObject().is()) { michael@0: *fun = &v.toObject().as(); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: IsNativeFunction(const js::Value &v) michael@0: { michael@0: JSFunction *fun; michael@0: return IsFunctionObject(v, &fun) && fun->isNative(); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: IsNativeFunction(const js::Value &v, JSFunction **fun) michael@0: { michael@0: return IsFunctionObject(v, fun) && (*fun)->isNative(); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: IsNativeFunction(const js::Value &v, JSNative native) michael@0: { michael@0: JSFunction *fun; michael@0: return IsFunctionObject(v, &fun) && fun->maybeNative() == native; michael@0: } michael@0: michael@0: /* michael@0: * When we have an object of a builtin class, we don't quite know what its michael@0: * valueOf/toString methods are, since these methods may have been overwritten michael@0: * or shadowed. However, we can still do better than the general case by michael@0: * hard-coding the necessary properties for us to find the native we expect. michael@0: * michael@0: * TODO: a per-thread shape-based cache would be faster and simpler. michael@0: */ michael@0: static MOZ_ALWAYS_INLINE bool michael@0: ClassMethodIsNative(JSContext *cx, JSObject *obj, const Class *clasp, jsid methodid, JSNative native) michael@0: { michael@0: JS_ASSERT(!obj->is()); michael@0: JS_ASSERT(obj->getClass() == clasp); michael@0: michael@0: Value v; michael@0: if (!HasDataProperty(cx, obj, methodid, &v)) { michael@0: JSObject *proto = obj->getProto(); michael@0: if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, proto, methodid, &v)) michael@0: return false; michael@0: } michael@0: michael@0: return js::IsNativeFunction(v, native); michael@0: } michael@0: michael@0: /* ES5 9.1 ToPrimitive(input). */ michael@0: static MOZ_ALWAYS_INLINE bool michael@0: ToPrimitive(JSContext *cx, MutableHandleValue vp) michael@0: { michael@0: if (vp.isPrimitive()) michael@0: return true; michael@0: michael@0: JSObject *obj = &vp.toObject(); michael@0: michael@0: /* Optimize new String(...).valueOf(). */ michael@0: if (obj->is()) { michael@0: jsid id = NameToId(cx->names().valueOf); michael@0: if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) { michael@0: vp.setString(obj->as().unbox()); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: /* Optimize new Number(...).valueOf(). */ michael@0: if (obj->is()) { michael@0: jsid id = NameToId(cx->names().valueOf); michael@0: if (ClassMethodIsNative(cx, obj, &NumberObject::class_, id, js_num_valueOf)) { michael@0: vp.setNumber(obj->as().unbox()); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: RootedObject objRoot(cx, obj); michael@0: return JSObject::defaultValue(cx, objRoot, JSTYPE_VOID, vp); michael@0: } michael@0: michael@0: /* ES5 9.1 ToPrimitive(input, PreferredType). */ michael@0: static MOZ_ALWAYS_INLINE bool michael@0: ToPrimitive(JSContext *cx, JSType preferredType, MutableHandleValue vp) michael@0: { michael@0: JS_ASSERT(preferredType != JSTYPE_VOID); /* Use the other ToPrimitive! */ michael@0: if (vp.isPrimitive()) michael@0: return true; michael@0: RootedObject obj(cx, &vp.toObject()); michael@0: return JSObject::defaultValue(cx, obj, preferredType, vp); michael@0: } michael@0: michael@0: /* michael@0: * Return true if this is a compiler-created internal function accessed by michael@0: * its own object. Such a function object must not be accessible to script michael@0: * or embedding code. michael@0: */ michael@0: inline bool michael@0: IsInternalFunctionObject(JSObject *funobj) michael@0: { michael@0: JSFunction *fun = &funobj->as(); michael@0: return fun->isLambda() && !funobj->getParent(); michael@0: } michael@0: michael@0: class AutoPropDescArrayRooter : private AutoGCRooter michael@0: { michael@0: public: michael@0: AutoPropDescArrayRooter(JSContext *cx) michael@0: : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx) michael@0: { } michael@0: michael@0: PropDesc *append() { michael@0: if (!descriptors.append(PropDesc())) michael@0: return nullptr; michael@0: return &descriptors.back(); michael@0: } michael@0: michael@0: bool reserve(size_t n) { michael@0: return descriptors.reserve(n); michael@0: } michael@0: michael@0: PropDesc& operator[](size_t i) { michael@0: JS_ASSERT(i < descriptors.length()); michael@0: return descriptors[i]; michael@0: } michael@0: michael@0: friend void AutoGCRooter::trace(JSTracer *trc); michael@0: michael@0: private: michael@0: PropDescArray descriptors; michael@0: }; michael@0: michael@0: /* michael@0: * Make an object with the specified prototype. If parent is null, it will michael@0: * default to the prototype's global if the prototype is non-null. michael@0: */ michael@0: JSObject * michael@0: NewObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp, TaggedProto proto, JSObject *parent, michael@0: gc::AllocKind allocKind, NewObjectKind newKind); michael@0: michael@0: inline JSObject * michael@0: NewObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp, TaggedProto proto, JSObject *parent, michael@0: NewObjectKind newKind = GenericObject) michael@0: { michael@0: gc::AllocKind allocKind = gc::GetGCObjectKind(clasp); michael@0: return NewObjectWithGivenProto(cx, clasp, proto, parent, allocKind, newKind); michael@0: } michael@0: michael@0: inline JSObject * michael@0: NewObjectWithGivenProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent, michael@0: NewObjectKind newKind = GenericObject) michael@0: { michael@0: return NewObjectWithGivenProto(cx, clasp, TaggedProto(proto), parent, newKind); michael@0: } michael@0: michael@0: inline JSProtoKey michael@0: GetClassProtoKey(const js::Class *clasp) michael@0: { michael@0: JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp); michael@0: if (key != JSProto_Null) michael@0: return key; michael@0: if (clasp->flags & JSCLASS_IS_ANONYMOUS) michael@0: return JSProto_Object; michael@0: return JSProto_Null; michael@0: } michael@0: michael@0: inline bool michael@0: FindProto(ExclusiveContext *cx, const js::Class *clasp, MutableHandleObject proto) michael@0: { michael@0: if (!FindClassPrototype(cx, proto, clasp)) michael@0: return false; michael@0: michael@0: if (!proto) { michael@0: // We're looking for the prototype of a class that is currently being michael@0: // resolved; the global object's resolve hook is on the michael@0: // stack. js::FindClassPrototype detects this goofy case and returns michael@0: // true with proto null. Fall back on Object.prototype. michael@0: JS_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == JSProto_Null); michael@0: return GetBuiltinPrototype(cx, JSProto_Object, proto); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Make an object with the prototype set according to the specified prototype or class: michael@0: * michael@0: * if proto is non-null: michael@0: * use the specified proto michael@0: * for a built-in class: michael@0: * use the memoized original value of the class constructor .prototype michael@0: * property object michael@0: * else if available michael@0: * the current value of .prototype michael@0: * else michael@0: * Object.prototype. michael@0: * michael@0: * The class prototype will be fetched from the parent's global. If global is michael@0: * null, the context's active global will be used, and the resulting object's michael@0: * parent will be that global. michael@0: */ michael@0: JSObject * michael@0: NewObjectWithClassProtoCommon(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent, michael@0: gc::AllocKind allocKind, NewObjectKind newKind); michael@0: michael@0: inline JSObject * michael@0: NewObjectWithClassProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent, michael@0: gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) michael@0: { michael@0: return NewObjectWithClassProtoCommon(cx, clasp, proto, parent, allocKind, newKind); michael@0: } michael@0: michael@0: inline JSObject * michael@0: NewObjectWithClassProto(ExclusiveContext *cx, const js::Class *clasp, JSObject *proto, JSObject *parent, michael@0: NewObjectKind newKind = GenericObject) michael@0: { michael@0: gc::AllocKind allocKind = gc::GetGCObjectKind(clasp); michael@0: return NewObjectWithClassProto(cx, clasp, proto, parent, allocKind, newKind); michael@0: } michael@0: michael@0: template michael@0: inline T * michael@0: NewObjectWithProto(ExclusiveContext *cx, JSObject *proto, JSObject *parent, michael@0: NewObjectKind newKind = GenericObject) michael@0: { michael@0: JSObject *obj = NewObjectWithClassProto(cx, &T::class_, proto, parent, newKind); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: return &obj->as(); michael@0: } michael@0: michael@0: /* michael@0: * Create a native instance of the given class with parent and proto set michael@0: * according to the context's active global. michael@0: */ michael@0: inline JSObject * michael@0: NewBuiltinClassInstance(ExclusiveContext *cx, const Class *clasp, gc::AllocKind allocKind, michael@0: NewObjectKind newKind = GenericObject) michael@0: { michael@0: return NewObjectWithClassProto(cx, clasp, nullptr, nullptr, allocKind, newKind); michael@0: } michael@0: michael@0: inline JSObject * michael@0: NewBuiltinClassInstance(ExclusiveContext *cx, const Class *clasp, NewObjectKind newKind = GenericObject) michael@0: { michael@0: gc::AllocKind allocKind = gc::GetGCObjectKind(clasp); michael@0: return NewBuiltinClassInstance(cx, clasp, allocKind, newKind); michael@0: } michael@0: michael@0: template michael@0: inline T * michael@0: NewBuiltinClassInstance(ExclusiveContext *cx, NewObjectKind newKind = GenericObject) michael@0: { michael@0: JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, newKind); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: return &obj->as(); michael@0: } michael@0: michael@0: template michael@0: inline T * michael@0: NewBuiltinClassInstance(ExclusiveContext *cx, gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) michael@0: { michael@0: JSObject *obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: return &obj->as(); michael@0: } michael@0: michael@0: // Used to optimize calls to (new Object()) michael@0: bool michael@0: NewObjectScriptedCall(JSContext *cx, MutableHandleObject obj); michael@0: michael@0: /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */ michael@0: static inline JSObject * michael@0: CopyInitializerObject(JSContext *cx, HandleObject baseobj, NewObjectKind newKind = GenericObject) michael@0: { michael@0: JS_ASSERT(baseobj->getClass() == &JSObject::class_); michael@0: JS_ASSERT(!baseobj->inDictionaryMode()); michael@0: michael@0: gc::AllocKind allocKind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots()); michael@0: allocKind = gc::GetBackgroundAllocKind(allocKind); michael@0: JS_ASSERT_IF(baseobj->isTenured(), allocKind == baseobj->tenuredGetAllocKind()); michael@0: RootedObject obj(cx); michael@0: obj = NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: RootedObject metadata(cx, obj->getMetadata()); michael@0: RootedShape lastProp(cx, baseobj->lastProperty()); michael@0: if (!JSObject::setLastProperty(cx, obj, lastProp)) michael@0: return nullptr; michael@0: if (metadata && !JSObject::setMetadata(cx, obj, metadata)) michael@0: return nullptr; michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: JSObject * michael@0: NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind, michael@0: NewObjectKind newKind = GenericObject); michael@0: michael@0: inline JSObject * michael@0: NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, michael@0: NewObjectKind newKind = GenericObject) michael@0: { michael@0: gc::AllocKind allocKind = gc::GetGCObjectKind(type->clasp()); michael@0: return NewObjectWithType(cx, type, parent, allocKind, newKind); michael@0: } michael@0: michael@0: JSObject * michael@0: NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent, michael@0: gc::AllocKind allocKind, HandleShape shape, michael@0: NewObjectKind newKind = GenericObject); michael@0: michael@0: /* michael@0: * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of michael@0: * the object, zero if the final size is unknown. This should only be used for michael@0: * objects that do not require any fixed slots. michael@0: */ michael@0: static inline gc::AllocKind michael@0: GuessObjectGCKind(size_t numSlots) michael@0: { michael@0: if (numSlots) michael@0: return gc::GetGCObjectKind(numSlots); michael@0: return gc::FINALIZE_OBJECT4; michael@0: } michael@0: michael@0: static inline gc::AllocKind michael@0: GuessArrayGCKind(size_t numSlots) michael@0: { michael@0: if (numSlots) michael@0: return gc::GetGCArrayKind(numSlots); michael@0: return gc::FINALIZE_OBJECT8; michael@0: } michael@0: michael@0: inline bool michael@0: ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) michael@0: { michael@0: if (MOZ_UNLIKELY(obj->is())) michael@0: return Proxy::objectClassIs(obj, classValue, cx); michael@0: michael@0: switch (classValue) { michael@0: case ESClass_Array: return obj->is(); michael@0: case ESClass_Number: return obj->is(); michael@0: case ESClass_String: return obj->is(); michael@0: case ESClass_Boolean: return obj->is(); michael@0: case ESClass_RegExp: return obj->is(); michael@0: case ESClass_ArrayBuffer: michael@0: return obj->is() || obj->is(); michael@0: case ESClass_Date: return obj->is(); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("bad classValue"); michael@0: } michael@0: michael@0: inline bool michael@0: IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: RootedObject obj(cx, &v.toObject()); michael@0: return ObjectClassIs(obj, classValue, cx); michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: NewObjectMetadata(ExclusiveContext *cxArg, JSObject **pmetadata) michael@0: { michael@0: // The metadata callback is invoked before each created object, except when michael@0: // analysis/compilation is active, to avoid recursion. michael@0: JS_ASSERT(!*pmetadata); michael@0: if (JSContext *cx = cxArg->maybeJSContext()) { michael@0: if (MOZ_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) && michael@0: !cx->compartment()->activeAnalysis) michael@0: { michael@0: // Use AutoEnterAnalysis to prohibit both any GC activity under the michael@0: // callback, and any reentering of JS via Invoke() etc. michael@0: types::AutoEnterAnalysis enter(cx); michael@0: michael@0: if (!cx->compartment()->callObjectMetadataCallback(cx, pmetadata)) michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: inline bool michael@0: DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, michael@0: PropertyOp getter, StrictPropertyOp setter, unsigned attrs) michael@0: { michael@0: Rooted id(cx, NameToId(name)); michael@0: return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs); michael@0: } michael@0: michael@0: namespace baseops { michael@0: michael@0: inline bool michael@0: LookupProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, michael@0: MutableHandleObject objp, MutableHandleShape propp) michael@0: { michael@0: Rooted id(cx, NameToId(name)); michael@0: return LookupProperty(cx, obj, id, objp, propp); michael@0: } michael@0: michael@0: inline bool michael@0: DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, michael@0: JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) michael@0: { michael@0: Rooted id(cx, NameToId(name)); michael@0: return DefineGeneric(cx, obj, id, value, getter, setter, attrs); michael@0: } michael@0: michael@0: } /* namespace baseops */ michael@0: michael@0: } /* namespace js */ michael@0: michael@0: extern JSObject * michael@0: js_InitClass(JSContext *cx, js::HandleObject obj, JSObject *parent_proto, michael@0: const js::Class *clasp, JSNative constructor, unsigned nargs, michael@0: const JSPropertySpec *ps, const JSFunctionSpec *fs, michael@0: const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs, michael@0: JSObject **ctorp = nullptr, michael@0: js::gc::AllocKind ctorKind = JSFunction::FinalizeKind); michael@0: michael@0: #endif /* jsobjinlines_h */