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 jsobj_h michael@0: #define jsobj_h michael@0: michael@0: /* michael@0: * JS object definitions. michael@0: * michael@0: * A JS object consists of a possibly-shared object descriptor containing michael@0: * ordered property names, called the map; and a dense vector of property michael@0: * values, called slots. The map/slot pointer pair is GC'ed, while the map michael@0: * is reference counted and the slot vector is malloc'ed. michael@0: */ michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "gc/Barrier.h" michael@0: #include "gc/Marking.h" michael@0: #include "js/GCAPI.h" michael@0: #include "vm/ObjectImpl.h" michael@0: #include "vm/Shape.h" michael@0: #include "vm/Xdr.h" michael@0: michael@0: namespace JS { michael@0: struct ObjectsExtraSizes; michael@0: } michael@0: michael@0: namespace js { michael@0: michael@0: class AutoPropDescArrayRooter; michael@0: struct GCMarker; michael@0: struct NativeIterator; michael@0: class Nursery; michael@0: struct StackShape; michael@0: michael@0: inline JSObject * michael@0: CastAsObject(PropertyOp op) michael@0: { michael@0: return JS_FUNC_TO_DATA_PTR(JSObject *, op); michael@0: } michael@0: michael@0: inline JSObject * michael@0: CastAsObject(StrictPropertyOp op) michael@0: { michael@0: return JS_FUNC_TO_DATA_PTR(JSObject *, op); michael@0: } michael@0: michael@0: inline Value michael@0: CastAsObjectJsval(PropertyOp op) michael@0: { michael@0: return ObjectOrNullValue(CastAsObject(op)); michael@0: } michael@0: michael@0: inline Value michael@0: CastAsObjectJsval(StrictPropertyOp op) michael@0: { michael@0: return ObjectOrNullValue(CastAsObject(op)); michael@0: } michael@0: michael@0: /******************************************************************************/ michael@0: michael@0: typedef Vector PropDescArray; michael@0: michael@0: /* michael@0: * The baseops namespace encapsulates the default behavior when performing michael@0: * various operations on an object, irrespective of hooks installed in the michael@0: * object's class. In general, instance methods on the object itself should be michael@0: * called instead of calling these methods directly. michael@0: */ michael@0: namespace baseops { michael@0: michael@0: /* michael@0: * On success, and if id was found, return true with *objp non-null and with a michael@0: * property of *objp stored in *propp. If successful but id was not found, michael@0: * return true with both *objp and *propp null. michael@0: */ michael@0: template michael@0: extern bool michael@0: LookupProperty(ExclusiveContext *cx, michael@0: typename MaybeRooted::HandleType obj, michael@0: typename MaybeRooted::HandleType id, michael@0: typename MaybeRooted::MutableHandleType objp, michael@0: typename MaybeRooted::MutableHandleType propp); michael@0: michael@0: extern bool michael@0: LookupElement(JSContext *cx, HandleObject obj, uint32_t index, michael@0: MutableHandleObject objp, MutableHandleShape propp); michael@0: michael@0: extern bool michael@0: DefineGeneric(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, michael@0: JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); michael@0: michael@0: extern bool michael@0: DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value, michael@0: JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); michael@0: michael@0: extern bool michael@0: GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp); michael@0: michael@0: extern bool michael@0: GetPropertyNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp); michael@0: michael@0: extern bool michael@0: GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index, MutableHandleValue vp); michael@0: michael@0: inline bool michael@0: GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) michael@0: { michael@0: return GetProperty(cx, obj, obj, id, vp); michael@0: } michael@0: michael@0: inline bool michael@0: GetElement(JSContext *cx, HandleObject obj, uint32_t index, MutableHandleValue vp) michael@0: { michael@0: return GetElement(cx, obj, obj, index, vp); michael@0: } michael@0: michael@0: /* michael@0: * Indicates whether an assignment operation is qualified (`x.y = 0`) or michael@0: * unqualified (`y = 0`). In strict mode, the latter is an error if no such michael@0: * variable already exists. michael@0: * michael@0: * Used as an argument to baseops::SetPropertyHelper. michael@0: */ michael@0: enum QualifiedBool { michael@0: Unqualified = 0, michael@0: Qualified = 1 michael@0: }; michael@0: michael@0: template michael@0: extern bool michael@0: SetPropertyHelper(typename ExecutionModeTraits::ContextType cx, HandleObject obj, michael@0: HandleObject receiver, HandleId id, QualifiedBool qualified, michael@0: MutableHandleValue vp, bool strict); michael@0: michael@0: extern bool michael@0: SetElementHelper(JSContext *cx, HandleObject obj, HandleObject Receiver, uint32_t index, michael@0: MutableHandleValue vp, bool strict); michael@0: michael@0: extern bool michael@0: GetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp); michael@0: michael@0: extern bool michael@0: SetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp); michael@0: michael@0: extern bool michael@0: DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, bool *succeeded); michael@0: michael@0: extern bool michael@0: DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded); michael@0: michael@0: extern bool michael@0: DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded); michael@0: michael@0: extern bool michael@0: Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable); michael@0: michael@0: extern bool michael@0: Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id); michael@0: michael@0: } /* namespace js::baseops */ michael@0: michael@0: extern const Class IntlClass; michael@0: extern const Class JSONClass; michael@0: extern const Class MathClass; michael@0: michael@0: class GlobalObject; michael@0: class MapObject; michael@0: class NewObjectCache; michael@0: class NormalArgumentsObject; michael@0: class SetObject; michael@0: class StrictArgumentsObject; michael@0: michael@0: /* michael@0: * NOTE: This is a placeholder for bug 619558. michael@0: * michael@0: * Run a post write barrier that encompasses multiple contiguous slots in a michael@0: * single step. michael@0: */ michael@0: inline void michael@0: DenseRangeWriteBarrierPost(JSRuntime *rt, JSObject *obj, uint32_t start, uint32_t count) michael@0: { michael@0: #ifdef JSGC_GENERATIONAL michael@0: if (count > 0) { michael@0: JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt); michael@0: shadowRuntime->gcStoreBufferPtr()->putSlot(obj, HeapSlot::Element, start, count); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: } /* namespace js */ michael@0: michael@0: /* michael@0: * The public interface for an object. michael@0: * michael@0: * Implementation of the underlying structure occurs in ObjectImpl, from which michael@0: * this struct inherits. This inheritance is currently public, but it will michael@0: * eventually be made protected. For full details, see vm/ObjectImpl.{h,cpp}. michael@0: * michael@0: * The JSFunction struct is an extension of this struct allocated from a larger michael@0: * GC size-class. michael@0: */ michael@0: class JSObject : public js::ObjectImpl michael@0: { michael@0: private: michael@0: friend class js::Shape; michael@0: friend struct js::GCMarker; michael@0: friend class js::NewObjectCache; michael@0: friend class js::Nursery; michael@0: michael@0: /* Make the type object to use for LAZY_TYPE objects. */ michael@0: static js::types::TypeObject *makeLazyType(JSContext *cx, js::HandleObject obj); michael@0: michael@0: public: michael@0: static const js::Class class_; michael@0: michael@0: /* michael@0: * Update the last property, keeping the number of allocated slots in sync michael@0: * with the object's new slot span. michael@0: */ michael@0: static bool setLastProperty(js::ThreadSafeContext *cx, michael@0: JS::HandleObject obj, js::HandleShape shape); michael@0: michael@0: /* As above, but does not change the slot span. */ michael@0: inline void setLastPropertyInfallible(js::Shape *shape); michael@0: michael@0: /* michael@0: * Make a non-array object with the specified initial state. This method michael@0: * takes ownership of any extantSlots it is passed. michael@0: */ michael@0: static inline JSObject *create(js::ExclusiveContext *cx, michael@0: js::gc::AllocKind kind, michael@0: js::gc::InitialHeap heap, michael@0: js::HandleShape shape, michael@0: js::HandleTypeObject type, michael@0: js::HeapSlot *extantSlots = nullptr); michael@0: michael@0: /* Make an array object with the specified initial state. */ michael@0: static inline js::ArrayObject *createArray(js::ExclusiveContext *cx, michael@0: js::gc::AllocKind kind, michael@0: js::gc::InitialHeap heap, michael@0: js::HandleShape shape, michael@0: js::HandleTypeObject type, michael@0: uint32_t length); michael@0: michael@0: /* michael@0: * Remove the last property of an object, provided that it is safe to do so michael@0: * (the shape and previous shape do not carry conflicting information about michael@0: * the object itself). michael@0: */ michael@0: inline void removeLastProperty(js::ExclusiveContext *cx); michael@0: inline bool canRemoveLastProperty(); michael@0: michael@0: /* michael@0: * Update the slot span directly for a dictionary object, and allocate michael@0: * slots to cover the new span if necessary. michael@0: */ michael@0: static bool setSlotSpan(js::ThreadSafeContext *cx, JS::HandleObject obj, uint32_t span); michael@0: michael@0: /* Upper bound on the number of elements in an object. */ michael@0: static const uint32_t NELEMENTS_LIMIT = JS_BIT(28); michael@0: michael@0: public: michael@0: bool setDelegate(js::ExclusiveContext *cx) { michael@0: return setFlag(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE); michael@0: } michael@0: michael@0: bool isBoundFunction() const { michael@0: return lastProperty()->hasObjectFlag(js::BaseShape::BOUND_FUNCTION); michael@0: } michael@0: michael@0: inline bool hasSpecialEquality() const; michael@0: michael@0: bool watched() const { michael@0: return lastProperty()->hasObjectFlag(js::BaseShape::WATCHED); michael@0: } michael@0: bool setWatched(js::ExclusiveContext *cx) { michael@0: return setFlag(cx, js::BaseShape::WATCHED, GENERATE_SHAPE); michael@0: } michael@0: michael@0: /* See InterpreterFrame::varObj. */ michael@0: inline bool isVarObj(); michael@0: bool setVarObj(js::ExclusiveContext *cx) { michael@0: return setFlag(cx, js::BaseShape::VAROBJ); michael@0: } michael@0: michael@0: /* michael@0: * Objects with an uncacheable proto can have their prototype mutated michael@0: * without inducing a shape change on the object. Property cache entries michael@0: * and JIT inline caches should not be filled for lookups across prototype michael@0: * lookups on the object. michael@0: */ michael@0: bool hasUncacheableProto() const { michael@0: return lastProperty()->hasObjectFlag(js::BaseShape::UNCACHEABLE_PROTO); michael@0: } michael@0: bool setUncacheableProto(js::ExclusiveContext *cx) { michael@0: return setFlag(cx, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE); michael@0: } michael@0: michael@0: /* michael@0: * Whether SETLELEM was used to access this object. See also the comment near michael@0: * PropertyTree::MAX_HEIGHT. michael@0: */ michael@0: bool hadElementsAccess() const { michael@0: return lastProperty()->hasObjectFlag(js::BaseShape::HAD_ELEMENTS_ACCESS); michael@0: } michael@0: bool setHadElementsAccess(js::ExclusiveContext *cx) { michael@0: return setFlag(cx, js::BaseShape::HAD_ELEMENTS_ACCESS); michael@0: } michael@0: michael@0: public: michael@0: bool nativeEmpty() const { michael@0: return lastProperty()->isEmptyShape(); michael@0: } michael@0: michael@0: bool shadowingShapeChange(js::ExclusiveContext *cx, const js::Shape &shape); michael@0: michael@0: /* michael@0: * Whether there may be indexed properties on this object, excluding any in michael@0: * the object's elements. michael@0: */ michael@0: bool isIndexed() const { michael@0: return lastProperty()->hasObjectFlag(js::BaseShape::INDEXED); michael@0: } michael@0: michael@0: uint32_t propertyCount() const { michael@0: return lastProperty()->entryCount(); michael@0: } michael@0: michael@0: bool hasShapeTable() const { michael@0: return lastProperty()->hasTable(); michael@0: } michael@0: michael@0: void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes); michael@0: michael@0: bool hasIdempotentProtoChain() const; michael@0: michael@0: // MAX_FIXED_SLOTS is the biggest number of fixed slots our GC michael@0: // size classes will give an object. michael@0: static const uint32_t MAX_FIXED_SLOTS = 16; michael@0: michael@0: public: michael@0: michael@0: /* Accessors for properties. */ michael@0: michael@0: /* Whether a slot is at a fixed offset from this object. */ michael@0: bool isFixedSlot(size_t slot) { michael@0: return slot < numFixedSlots(); michael@0: } michael@0: michael@0: /* Index into the dynamic slots array to use for a dynamic slot. */ michael@0: size_t dynamicSlotIndex(size_t slot) { michael@0: JS_ASSERT(slot >= numFixedSlots()); michael@0: return slot - numFixedSlots(); michael@0: } michael@0: michael@0: /* michael@0: * Grow or shrink slots immediately before changing the slot span. michael@0: * The number of allocated slots is not stored explicitly, and changes to michael@0: * the slots must track changes in the slot span. michael@0: */ michael@0: static bool growSlots(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount, michael@0: uint32_t newCount); michael@0: static void shrinkSlots(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount, michael@0: uint32_t newCount); michael@0: michael@0: bool hasDynamicSlots() const { return !!slots; } michael@0: michael@0: protected: michael@0: static inline bool updateSlotsForSpan(js::ThreadSafeContext *cx, michael@0: js::HandleObject obj, size_t oldSpan, size_t newSpan); michael@0: michael@0: public: michael@0: /* michael@0: * Trigger the write barrier on a range of slots that will no longer be michael@0: * reachable. michael@0: */ michael@0: void prepareSlotRangeForOverwrite(size_t start, size_t end) { michael@0: for (size_t i = start; i < end; i++) michael@0: getSlotAddressUnchecked(i)->js::HeapSlot::~HeapSlot(); michael@0: } michael@0: michael@0: void prepareElementRangeForOverwrite(size_t start, size_t end) { michael@0: JS_ASSERT(end <= getDenseInitializedLength()); michael@0: for (size_t i = start; i < end; i++) michael@0: elements[i].js::HeapSlot::~HeapSlot(); michael@0: } michael@0: michael@0: static bool rollbackProperties(js::ExclusiveContext *cx, js::HandleObject obj, michael@0: uint32_t slotSpan); michael@0: michael@0: void nativeSetSlot(uint32_t slot, const js::Value &value) { michael@0: JS_ASSERT(isNative()); michael@0: JS_ASSERT(slot < slotSpan()); michael@0: return setSlot(slot, value); michael@0: } michael@0: michael@0: inline bool nativeSetSlotIfHasType(js::Shape *shape, const js::Value &value); michael@0: inline void nativeSetSlotWithType(js::ExclusiveContext *cx, js::Shape *shape, michael@0: const js::Value &value); michael@0: michael@0: inline const js::Value &getReservedSlot(uint32_t index) const { michael@0: JS_ASSERT(index < JSSLOT_FREE(getClass())); michael@0: return getSlot(index); michael@0: } michael@0: michael@0: const js::HeapSlot &getReservedSlotRef(uint32_t index) const { michael@0: JS_ASSERT(index < JSSLOT_FREE(getClass())); michael@0: return getSlotRef(index); michael@0: } michael@0: michael@0: js::HeapSlot &getReservedSlotRef(uint32_t index) { michael@0: JS_ASSERT(index < JSSLOT_FREE(getClass())); michael@0: return getSlotRef(index); michael@0: } michael@0: michael@0: void initReservedSlot(uint32_t index, const js::Value &v) { michael@0: JS_ASSERT(index < JSSLOT_FREE(getClass())); michael@0: initSlot(index, v); michael@0: } michael@0: michael@0: void setReservedSlot(uint32_t index, const js::Value &v) { michael@0: JS_ASSERT(index < JSSLOT_FREE(getClass())); michael@0: setSlot(index, v); michael@0: } michael@0: michael@0: /* michael@0: * Marks this object as having a singleton type, and leave the type lazy. michael@0: * Constructs a new, unique shape for the object. michael@0: */ michael@0: static inline bool setSingletonType(js::ExclusiveContext *cx, js::HandleObject obj); michael@0: michael@0: // uninlinedGetType() is the same as getType(), but not inlined. michael@0: inline js::types::TypeObject* getType(JSContext *cx); michael@0: js::types::TypeObject* uninlinedGetType(JSContext *cx); michael@0: michael@0: const js::HeapPtr &typeFromGC() const { michael@0: /* Direct field access for use by GC. */ michael@0: return type_; michael@0: } michael@0: michael@0: /* michael@0: * We allow the prototype of an object to be lazily computed if the object michael@0: * is a proxy. In the lazy case, we store (JSObject *)0x1 in the proto field michael@0: * of the object's TypeObject. We offer three ways of getting the prototype: michael@0: * michael@0: * 1. obj->getProto() returns the prototype, but asserts if obj is a proxy. michael@0: * 2. obj->getTaggedProto() returns a TaggedProto, which can be tested to michael@0: * check if the proto is an object, nullptr, or lazily computed. michael@0: * 3. JSObject::getProto(cx, obj, &proto) computes the proto of an object. michael@0: * If obj is a proxy and the proto is lazy, this code may allocate or michael@0: * GC in order to compute the proto. Currently, it will not run JS code. michael@0: */ michael@0: bool uninlinedIsProxy() const; michael@0: JSObject *getProto() const { michael@0: JS_ASSERT(!uninlinedIsProxy()); michael@0: return getTaggedProto().toObjectOrNull(); michael@0: } michael@0: static inline bool getProto(JSContext *cx, js::HandleObject obj, michael@0: js::MutableHandleObject protop); michael@0: // Returns false on error, success of operation in outparam. michael@0: static inline bool setProto(JSContext *cx, JS::HandleObject obj, michael@0: JS::HandleObject proto, bool *succeeded); michael@0: michael@0: // uninlinedSetType() is the same as setType(), but not inlined. michael@0: inline void setType(js::types::TypeObject *newType); michael@0: void uninlinedSetType(js::types::TypeObject *newType); michael@0: michael@0: #ifdef DEBUG michael@0: bool hasNewType(const js::Class *clasp, js::types::TypeObject *newType); michael@0: #endif michael@0: michael@0: /* michael@0: * Mark an object that has been iterated over and is a singleton. We need michael@0: * to recover this information in the object's type information after it michael@0: * is purged on GC. michael@0: */ michael@0: bool isIteratedSingleton() const { michael@0: return lastProperty()->hasObjectFlag(js::BaseShape::ITERATED_SINGLETON); michael@0: } michael@0: bool setIteratedSingleton(js::ExclusiveContext *cx) { michael@0: return setFlag(cx, js::BaseShape::ITERATED_SINGLETON); michael@0: } michael@0: michael@0: /* michael@0: * Mark an object as requiring its default 'new' type to have unknown michael@0: * properties. michael@0: */ michael@0: bool isNewTypeUnknown() const { michael@0: return lastProperty()->hasObjectFlag(js::BaseShape::NEW_TYPE_UNKNOWN); michael@0: } michael@0: static bool setNewTypeUnknown(JSContext *cx, const js::Class *clasp, JS::HandleObject obj); michael@0: michael@0: /* Set a new prototype for an object with a singleton type. */ michael@0: bool splicePrototype(JSContext *cx, const js::Class *clasp, js::Handle proto); michael@0: michael@0: /* michael@0: * For bootstrapping, whether to splice a prototype for Function.prototype michael@0: * or the global object. michael@0: */ michael@0: bool shouldSplicePrototype(JSContext *cx); michael@0: michael@0: /* michael@0: * Parents and scope chains. michael@0: * michael@0: * All script-accessible objects with a nullptr parent are global objects, michael@0: * and all global objects have a nullptr parent. Some builtin objects michael@0: * which are not script-accessible also have a nullptr parent, such as michael@0: * parser created functions for non-compileAndGo scripts. michael@0: * michael@0: * Except for the non-script-accessible builtins, the global with which an michael@0: * object is associated can be reached by following parent links to that michael@0: * global (see global()). michael@0: * michael@0: * The scope chain of an object is the link in the search path when a michael@0: * script does a name lookup on a scope object. For JS internal scope michael@0: * objects --- Call, DeclEnv and Block --- the chain is stored in michael@0: * the first fixed slot of the object, and the object's parent is the michael@0: * associated global. For other scope objects, the chain is stored in the michael@0: * object's parent. michael@0: * michael@0: * In compileAndGo code, scope chains can contain only internal scope michael@0: * objects with a global object at the root as the scope of the outermost michael@0: * non-function script. In non-compileAndGo code, the scope of the michael@0: * outermost non-function script might not be a global object, and can have michael@0: * a mix of other objects above it before the global object is reached. michael@0: */ michael@0: michael@0: /* Access the parent link of an object. */ michael@0: JSObject *getParent() const { michael@0: return lastProperty()->getObjectParent(); michael@0: } michael@0: static bool setParent(JSContext *cx, js::HandleObject obj, js::HandleObject newParent); michael@0: michael@0: /* michael@0: * Get the enclosing scope of an object. When called on non-scope object, michael@0: * this will just be the global (the name "enclosing scope" still applies michael@0: * in this situation because non-scope objects can be on the scope chain). michael@0: */ michael@0: inline JSObject *enclosingScope(); michael@0: michael@0: /* Access the metadata on an object. */ michael@0: inline JSObject *getMetadata() const { michael@0: return lastProperty()->getObjectMetadata(); michael@0: } michael@0: static bool setMetadata(JSContext *cx, js::HandleObject obj, js::HandleObject newMetadata); michael@0: michael@0: inline js::GlobalObject &global() const; michael@0: inline bool isOwnGlobal() const; michael@0: michael@0: /* Remove the type (and prototype) or parent from a new object. */ michael@0: static inline bool clearType(JSContext *cx, js::HandleObject obj); michael@0: static bool clearParent(JSContext *cx, js::HandleObject obj); michael@0: michael@0: /* michael@0: * ES5 meta-object properties and operations. michael@0: */ michael@0: michael@0: private: michael@0: enum ImmutabilityType { SEAL, FREEZE }; michael@0: michael@0: /* michael@0: * The guts of Object.seal (ES5 15.2.3.8) and Object.freeze (ES5 15.2.3.9): mark the michael@0: * object as non-extensible, and adjust each property's attributes appropriately: each michael@0: * property becomes non-configurable, and if |freeze|, data properties become michael@0: * read-only as well. michael@0: */ michael@0: static bool sealOrFreeze(JSContext *cx, js::HandleObject obj, ImmutabilityType it); michael@0: michael@0: static bool isSealedOrFrozen(JSContext *cx, js::HandleObject obj, ImmutabilityType it, bool *resultp); michael@0: michael@0: static inline unsigned getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it); michael@0: michael@0: public: michael@0: /* ES5 15.2.3.8: non-extensible, all props non-configurable */ michael@0: static inline bool seal(JSContext *cx, js::HandleObject obj) { return sealOrFreeze(cx, obj, SEAL); } michael@0: /* ES5 15.2.3.9: non-extensible, all properties non-configurable, all data props read-only */ michael@0: static inline bool freeze(JSContext *cx, js::HandleObject obj) { return sealOrFreeze(cx, obj, FREEZE); } michael@0: michael@0: static inline bool isSealed(JSContext *cx, js::HandleObject obj, bool *resultp) { michael@0: return isSealedOrFrozen(cx, obj, SEAL, resultp); michael@0: } michael@0: static inline bool isFrozen(JSContext *cx, js::HandleObject obj, bool *resultp) { michael@0: return isSealedOrFrozen(cx, obj, FREEZE, resultp); michael@0: } michael@0: michael@0: /* toString support. */ michael@0: static const char *className(JSContext *cx, js::HandleObject obj); michael@0: michael@0: /* Accessors for elements. */ michael@0: bool ensureElements(js::ThreadSafeContext *cx, uint32_t capacity) { michael@0: if (capacity > getDenseCapacity()) michael@0: return growElements(cx, capacity); michael@0: return true; michael@0: } michael@0: michael@0: bool growElements(js::ThreadSafeContext *cx, uint32_t newcap); michael@0: void shrinkElements(js::ThreadSafeContext *cx, uint32_t cap); michael@0: void setDynamicElements(js::ObjectElements *header) { michael@0: JS_ASSERT(!hasDynamicElements()); michael@0: elements = header->elements(); michael@0: JS_ASSERT(hasDynamicElements()); michael@0: } michael@0: michael@0: uint32_t getDenseCapacity() { michael@0: JS_ASSERT(isNative()); michael@0: JS_ASSERT(getElementsHeader()->capacity >= getElementsHeader()->initializedLength); michael@0: return getElementsHeader()->capacity; michael@0: } michael@0: michael@0: private: michael@0: inline void ensureDenseInitializedLengthNoPackedCheck(js::ThreadSafeContext *cx, michael@0: uint32_t index, uint32_t extra); michael@0: michael@0: public: michael@0: void setDenseInitializedLength(uint32_t length) { michael@0: JS_ASSERT(isNative()); michael@0: JS_ASSERT(length <= getDenseCapacity()); michael@0: prepareElementRangeForOverwrite(length, getElementsHeader()->initializedLength); michael@0: getElementsHeader()->initializedLength = length; michael@0: } michael@0: michael@0: inline void ensureDenseInitializedLength(js::ExclusiveContext *cx, michael@0: uint32_t index, uint32_t extra); michael@0: inline void ensureDenseInitializedLengthPreservePackedFlag(js::ThreadSafeContext *cx, michael@0: uint32_t index, uint32_t extra); michael@0: void setDenseElement(uint32_t index, const js::Value &val) { michael@0: JS_ASSERT(isNative() && index < getDenseInitializedLength()); michael@0: elements[index].set(this, js::HeapSlot::Element, index, val); michael@0: } michael@0: michael@0: void initDenseElement(uint32_t index, const js::Value &val) { michael@0: JS_ASSERT(isNative() && index < getDenseInitializedLength()); michael@0: elements[index].init(this, js::HeapSlot::Element, index, val); michael@0: } michael@0: michael@0: void setDenseElementMaybeConvertDouble(uint32_t index, const js::Value &val) { michael@0: if (val.isInt32() && shouldConvertDoubleElements()) michael@0: setDenseElement(index, js::DoubleValue(val.toInt32())); michael@0: else michael@0: setDenseElement(index, val); michael@0: } michael@0: michael@0: inline bool setDenseElementIfHasType(uint32_t index, const js::Value &val); michael@0: inline void setDenseElementWithType(js::ExclusiveContext *cx, uint32_t index, michael@0: const js::Value &val); michael@0: inline void initDenseElementWithType(js::ExclusiveContext *cx, uint32_t index, michael@0: const js::Value &val); michael@0: inline void setDenseElementHole(js::ExclusiveContext *cx, uint32_t index); michael@0: static inline void removeDenseElementForSparseIndex(js::ExclusiveContext *cx, michael@0: js::HandleObject obj, uint32_t index); michael@0: michael@0: inline js::Value getDenseOrTypedArrayElement(uint32_t idx); michael@0: michael@0: void copyDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count) { michael@0: JS_ASSERT(dstStart + count <= getDenseCapacity()); michael@0: JSRuntime *rt = runtimeFromMainThread(); michael@0: if (JS::IsIncrementalBarrierNeeded(rt)) { michael@0: JS::Zone *zone = this->zone(); michael@0: for (uint32_t i = 0; i < count; ++i) michael@0: elements[dstStart + i].set(zone, this, js::HeapSlot::Element, dstStart + i, src[i]); michael@0: } else { michael@0: memcpy(&elements[dstStart], src, count * sizeof(js::HeapSlot)); michael@0: DenseRangeWriteBarrierPost(rt, this, dstStart, count); michael@0: } michael@0: } michael@0: michael@0: void initDenseElements(uint32_t dstStart, const js::Value *src, uint32_t count) { michael@0: JS_ASSERT(dstStart + count <= getDenseCapacity()); michael@0: memcpy(&elements[dstStart], src, count * sizeof(js::HeapSlot)); michael@0: DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count); michael@0: } michael@0: michael@0: void initDenseElementsUnbarriered(uint32_t dstStart, const js::Value *src, uint32_t count) { michael@0: /* michael@0: * For use by parallel threads, which since they cannot see nursery michael@0: * things do not require a barrier. michael@0: */ michael@0: JS_ASSERT(dstStart + count <= getDenseCapacity()); michael@0: #if defined(DEBUG) && defined(JSGC_GENERATIONAL) michael@0: JS::shadow::Runtime *rt = JS::shadow::Runtime::asShadowRuntime(runtimeFromAnyThread()); michael@0: JS_ASSERT(!js::gc::IsInsideNursery(rt, this)); michael@0: for (uint32_t index = 0; index < count; ++index) { michael@0: const JS::Value& value = src[index]; michael@0: if (value.isMarkable()) michael@0: JS_ASSERT(!js::gc::IsInsideNursery(rt, value.toGCThing())); michael@0: } michael@0: #endif michael@0: memcpy(&elements[dstStart], src, count * sizeof(js::HeapSlot)); michael@0: } michael@0: michael@0: void moveDenseElements(uint32_t dstStart, uint32_t srcStart, uint32_t count) { michael@0: JS_ASSERT(dstStart + count <= getDenseCapacity()); michael@0: JS_ASSERT(srcStart + count <= getDenseInitializedLength()); michael@0: michael@0: /* michael@0: * Using memmove here would skip write barriers. Also, we need to consider michael@0: * an array containing [A, B, C], in the following situation: michael@0: * michael@0: * 1. Incremental GC marks slot 0 of array (i.e., A), then returns to JS code. michael@0: * 2. JS code moves slots 1..2 into slots 0..1, so it contains [B, C, C]. michael@0: * 3. Incremental GC finishes by marking slots 1 and 2 (i.e., C). michael@0: * michael@0: * Since normal marking never happens on B, it is very important that the michael@0: * write barrier is invoked here on B, despite the fact that it exists in michael@0: * the array before and after the move. michael@0: */ michael@0: JS::Zone *zone = this->zone(); michael@0: JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone); michael@0: if (shadowZone->needsBarrier()) { michael@0: if (dstStart < srcStart) { michael@0: js::HeapSlot *dst = elements + dstStart; michael@0: js::HeapSlot *src = elements + srcStart; michael@0: for (uint32_t i = 0; i < count; i++, dst++, src++) michael@0: dst->set(zone, this, js::HeapSlot::Element, dst - elements, *src); michael@0: } else { michael@0: js::HeapSlot *dst = elements + dstStart + count - 1; michael@0: js::HeapSlot *src = elements + srcStart + count - 1; michael@0: for (uint32_t i = 0; i < count; i++, dst--, src--) michael@0: dst->set(zone, this, js::HeapSlot::Element, dst - elements, *src); michael@0: } michael@0: } else { michael@0: memmove(elements + dstStart, elements + srcStart, count * sizeof(js::HeapSlot)); michael@0: DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count); michael@0: } michael@0: } michael@0: michael@0: void moveDenseElementsNoPreBarrier(uint32_t dstStart, uint32_t srcStart, uint32_t count) { michael@0: JS_ASSERT(!shadowZone()->needsBarrier()); michael@0: michael@0: JS_ASSERT(dstStart + count <= getDenseCapacity()); michael@0: JS_ASSERT(srcStart + count <= getDenseCapacity()); michael@0: michael@0: memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value)); michael@0: DenseRangeWriteBarrierPost(runtimeFromMainThread(), this, dstStart, count); michael@0: } michael@0: michael@0: bool shouldConvertDoubleElements() { michael@0: JS_ASSERT(getClass()->isNative()); michael@0: return getElementsHeader()->shouldConvertDoubleElements(); michael@0: } michael@0: michael@0: inline void setShouldConvertDoubleElements(); michael@0: inline void clearShouldConvertDoubleElements(); michael@0: michael@0: /* Packed information for this object's elements. */ michael@0: inline bool writeToIndexWouldMarkNotPacked(uint32_t index); michael@0: inline void markDenseElementsNotPacked(js::ExclusiveContext *cx); michael@0: michael@0: /* michael@0: * ensureDenseElements ensures that the object can hold at least michael@0: * index + extra elements. It returns ED_OK on success, ED_FAILED on michael@0: * failure to grow the array, ED_SPARSE when the object is too sparse to michael@0: * grow (this includes the case of index + extra overflow). In the last michael@0: * two cases the object is kept intact. michael@0: */ michael@0: enum EnsureDenseResult { ED_OK, ED_FAILED, ED_SPARSE }; michael@0: michael@0: private: michael@0: inline EnsureDenseResult ensureDenseElementsNoPackedCheck(js::ThreadSafeContext *cx, michael@0: uint32_t index, uint32_t extra); michael@0: michael@0: public: michael@0: inline EnsureDenseResult ensureDenseElements(js::ExclusiveContext *cx, michael@0: uint32_t index, uint32_t extra); michael@0: inline EnsureDenseResult ensureDenseElementsPreservePackedFlag(js::ThreadSafeContext *cx, michael@0: uint32_t index, uint32_t extra); michael@0: michael@0: inline EnsureDenseResult extendDenseElements(js::ThreadSafeContext *cx, michael@0: uint32_t requiredCapacity, uint32_t extra); michael@0: michael@0: /* Convert a single dense element to a sparse property. */ michael@0: static bool sparsifyDenseElement(js::ExclusiveContext *cx, michael@0: js::HandleObject obj, uint32_t index); michael@0: michael@0: /* Convert all dense elements to sparse properties. */ michael@0: static bool sparsifyDenseElements(js::ExclusiveContext *cx, js::HandleObject obj); michael@0: michael@0: /* Small objects are dense, no matter what. */ michael@0: static const uint32_t MIN_SPARSE_INDEX = 1000; michael@0: michael@0: /* michael@0: * Element storage for an object will be sparse if fewer than 1/8 indexes michael@0: * are filled in. michael@0: */ michael@0: static const unsigned SPARSE_DENSITY_RATIO = 8; michael@0: michael@0: /* michael@0: * Check if after growing the object's elements will be too sparse. michael@0: * newElementsHint is an estimated number of elements to be added. michael@0: */ michael@0: bool willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint); michael@0: michael@0: /* michael@0: * After adding a sparse index to obj, see if it should be converted to use michael@0: * dense elements. michael@0: */ michael@0: static EnsureDenseResult maybeDensifySparseElements(js::ExclusiveContext *cx, js::HandleObject obj); michael@0: michael@0: public: michael@0: /* michael@0: * Iterator-specific getters and setters. michael@0: */ michael@0: michael@0: static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1; michael@0: michael@0: /* michael@0: * Back to generic stuff. michael@0: */ michael@0: bool isCallable() { michael@0: return getClass()->isCallable(); michael@0: } michael@0: michael@0: inline void finish(js::FreeOp *fop); michael@0: MOZ_ALWAYS_INLINE void finalize(js::FreeOp *fop); michael@0: michael@0: static inline bool hasProperty(JSContext *cx, js::HandleObject obj, michael@0: js::HandleId id, bool *foundp); michael@0: michael@0: /* michael@0: * Allocate and free an object slot. michael@0: * michael@0: * FIXME: bug 593129 -- slot allocation should be done by object methods michael@0: * after calling object-parameter-free shape methods, avoiding coupling michael@0: * logic across the object vs. shape module wall. michael@0: */ michael@0: static bool allocSlot(js::ThreadSafeContext *cx, JS::HandleObject obj, uint32_t *slotp); michael@0: void freeSlot(uint32_t slot); michael@0: michael@0: public: michael@0: static bool reportReadOnly(js::ThreadSafeContext *cx, jsid id, unsigned report = JSREPORT_ERROR); michael@0: bool reportNotConfigurable(js::ThreadSafeContext *cx, jsid id, unsigned report = JSREPORT_ERROR); michael@0: bool reportNotExtensible(js::ThreadSafeContext *cx, unsigned report = JSREPORT_ERROR); michael@0: michael@0: /* michael@0: * Get the property with the given id, then call it as a function with the michael@0: * given arguments, providing this object as |this|. If the property isn't michael@0: * callable a TypeError will be thrown. On success the value returned by michael@0: * the call is stored in *vp. michael@0: */ michael@0: bool callMethod(JSContext *cx, js::HandleId id, unsigned argc, js::Value *argv, michael@0: js::MutableHandleValue vp); michael@0: michael@0: private: michael@0: static js::Shape *getChildPropertyOnDictionary(js::ThreadSafeContext *cx, JS::HandleObject obj, michael@0: js::HandleShape parent, js::StackShape &child); michael@0: static js::Shape *getChildProperty(js::ExclusiveContext *cx, JS::HandleObject obj, michael@0: js::HandleShape parent, js::StackShape &child); michael@0: template michael@0: static inline js::Shape * michael@0: getOrLookupChildProperty(typename js::ExecutionModeTraits::ExclusiveContextType cx, michael@0: JS::HandleObject obj, js::HandleShape parent, js::StackShape &child) michael@0: { michael@0: if (mode == js::ParallelExecution) michael@0: return lookupChildProperty(cx, obj, parent, child); michael@0: return getChildProperty(cx->asExclusiveContext(), obj, parent, child); michael@0: } michael@0: michael@0: public: michael@0: /* michael@0: * XXX: This should be private, but is public because it needs to be a michael@0: * friend of ThreadSafeContext to get to the propertyTree on cx->compartment_. michael@0: */ michael@0: static js::Shape *lookupChildProperty(js::ThreadSafeContext *cx, JS::HandleObject obj, michael@0: js::HandleShape parent, js::StackShape &child); michael@0: michael@0: michael@0: protected: michael@0: /* michael@0: * Internal helper that adds a shape not yet mapped by this object. michael@0: * michael@0: * Notes: michael@0: * 1. getter and setter must be normalized based on flags (see jsscope.cpp). michael@0: * 2. Checks for non-extensibility must be done by callers. michael@0: */ michael@0: template michael@0: static js::Shape * michael@0: addPropertyInternal(typename js::ExecutionModeTraits::ExclusiveContextType cx, michael@0: JS::HandleObject obj, JS::HandleId id, michael@0: JSPropertyOp getter, JSStrictPropertyOp setter, michael@0: uint32_t slot, unsigned attrs, unsigned flags, js::Shape **spp, michael@0: bool allowDictionary); michael@0: michael@0: private: michael@0: struct TradeGutsReserved; michael@0: static bool ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b, michael@0: TradeGutsReserved &reserved); michael@0: michael@0: static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b, michael@0: TradeGutsReserved &reserved); michael@0: michael@0: public: michael@0: /* Add a property whose id is not yet in this scope. */ michael@0: static js::Shape *addProperty(js::ExclusiveContext *cx, JS::HandleObject, JS::HandleId id, michael@0: JSPropertyOp getter, JSStrictPropertyOp setter, michael@0: uint32_t slot, unsigned attrs, unsigned flags, michael@0: bool allowDictionary = true); michael@0: michael@0: /* Add a data property whose id is not yet in this scope. */ michael@0: js::Shape *addDataProperty(js::ExclusiveContext *cx, michael@0: jsid id_, uint32_t slot, unsigned attrs); michael@0: js::Shape *addDataProperty(js::ExclusiveContext *cx, js::HandlePropertyName name, michael@0: uint32_t slot, unsigned attrs); michael@0: michael@0: /* Add or overwrite a property for id in this scope. */ michael@0: template michael@0: static js::Shape * michael@0: putProperty(typename js::ExecutionModeTraits::ExclusiveContextType cx, michael@0: JS::HandleObject obj, JS::HandleId id, michael@0: JSPropertyOp getter, JSStrictPropertyOp setter, michael@0: uint32_t slot, unsigned attrs, michael@0: unsigned flags); michael@0: template michael@0: static inline js::Shape * michael@0: putProperty(typename js::ExecutionModeTraits::ExclusiveContextType cx, michael@0: JS::HandleObject obj, js::PropertyName *name, michael@0: JSPropertyOp getter, JSStrictPropertyOp setter, michael@0: uint32_t slot, unsigned attrs, michael@0: unsigned flags); michael@0: michael@0: /* Change the given property into a sibling with the same id in this scope. */ michael@0: template michael@0: static js::Shape * michael@0: changeProperty(typename js::ExecutionModeTraits::ExclusiveContextType cx, michael@0: js::HandleObject obj, js::HandleShape shape, unsigned attrs, unsigned mask, michael@0: JSPropertyOp getter, JSStrictPropertyOp setter); michael@0: michael@0: static inline bool changePropertyAttributes(JSContext *cx, js::HandleObject obj, michael@0: js::HandleShape shape, unsigned attrs); michael@0: michael@0: /* Remove the property named by id from this object. */ michael@0: bool removeProperty(js::ExclusiveContext *cx, jsid id); michael@0: michael@0: /* Clear the scope, making it empty. */ michael@0: static void clear(JSContext *cx, js::HandleObject obj); michael@0: michael@0: static bool lookupGeneric(JSContext *cx, js::HandleObject obj, js::HandleId id, michael@0: js::MutableHandleObject objp, js::MutableHandleShape propp); michael@0: michael@0: static bool lookupProperty(JSContext *cx, js::HandleObject obj, js::PropertyName *name, michael@0: js::MutableHandleObject objp, js::MutableHandleShape propp) michael@0: { michael@0: JS::RootedId id(cx, js::NameToId(name)); michael@0: return lookupGeneric(cx, obj, id, objp, propp); michael@0: } michael@0: michael@0: static bool lookupElement(JSContext *cx, js::HandleObject obj, uint32_t index, michael@0: js::MutableHandleObject objp, js::MutableHandleShape propp) michael@0: { michael@0: js::LookupElementOp op = obj->getOps()->lookupElement; michael@0: return (op ? op : js::baseops::LookupElement)(cx, obj, index, objp, propp); michael@0: } michael@0: michael@0: static bool defineGeneric(js::ExclusiveContext *cx, js::HandleObject obj, michael@0: js::HandleId id, js::HandleValue value, michael@0: JSPropertyOp getter = JS_PropertyStub, michael@0: JSStrictPropertyOp setter = JS_StrictPropertyStub, michael@0: unsigned attrs = JSPROP_ENUMERATE); michael@0: michael@0: static bool defineProperty(js::ExclusiveContext *cx, js::HandleObject obj, michael@0: js::PropertyName *name, js::HandleValue value, michael@0: JSPropertyOp getter = JS_PropertyStub, michael@0: JSStrictPropertyOp setter = JS_StrictPropertyStub, michael@0: unsigned attrs = JSPROP_ENUMERATE); michael@0: michael@0: static bool defineElement(js::ExclusiveContext *cx, js::HandleObject obj, michael@0: uint32_t index, js::HandleValue value, michael@0: JSPropertyOp getter = JS_PropertyStub, michael@0: JSStrictPropertyOp setter = JS_StrictPropertyStub, michael@0: unsigned attrs = JSPROP_ENUMERATE); michael@0: michael@0: static bool getGeneric(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, michael@0: js::HandleId id, js::MutableHandleValue vp) michael@0: { michael@0: JS_ASSERT(!!obj->getOps()->getGeneric == !!obj->getOps()->getProperty); michael@0: js::GenericIdOp op = obj->getOps()->getGeneric; michael@0: if (op) { michael@0: if (!op(cx, obj, receiver, id, vp)) michael@0: return false; michael@0: } else { michael@0: if (!js::baseops::GetProperty(cx, obj, receiver, id, vp)) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool getGenericNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, michael@0: jsid id, js::Value *vp) michael@0: { michael@0: js::GenericIdOp op = obj->getOps()->getGeneric; michael@0: if (op) michael@0: return false; michael@0: return js::baseops::GetPropertyNoGC(cx, obj, receiver, id, vp); michael@0: } michael@0: michael@0: static bool getProperty(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, michael@0: js::PropertyName *name, js::MutableHandleValue vp) michael@0: { michael@0: JS::RootedId id(cx, js::NameToId(name)); michael@0: return getGeneric(cx, obj, receiver, id, vp); michael@0: } michael@0: michael@0: static bool getPropertyNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, michael@0: js::PropertyName *name, js::Value *vp) michael@0: { michael@0: return getGenericNoGC(cx, obj, receiver, js::NameToId(name), vp); michael@0: } michael@0: michael@0: static inline bool getElement(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, michael@0: uint32_t index, js::MutableHandleValue vp); michael@0: static inline bool getElementNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, michael@0: uint32_t index, js::Value *vp); michael@0: michael@0: static bool setGeneric(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, michael@0: js::HandleId id, js::MutableHandleValue vp, bool strict) michael@0: { michael@0: if (obj->getOps()->setGeneric) michael@0: return nonNativeSetProperty(cx, obj, id, vp, strict); michael@0: return js::baseops::SetPropertyHelper( michael@0: cx, obj, receiver, id, js::baseops::Qualified, vp, strict); michael@0: } michael@0: michael@0: static bool setProperty(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, michael@0: js::PropertyName *name, michael@0: js::MutableHandleValue vp, bool strict) michael@0: { michael@0: JS::RootedId id(cx, js::NameToId(name)); michael@0: return setGeneric(cx, obj, receiver, id, vp, strict); michael@0: } michael@0: michael@0: static bool setElement(JSContext *cx, js::HandleObject obj, js::HandleObject receiver, michael@0: uint32_t index, js::MutableHandleValue vp, bool strict) michael@0: { michael@0: if (obj->getOps()->setElement) michael@0: return nonNativeSetElement(cx, obj, index, vp, strict); michael@0: return js::baseops::SetElementHelper(cx, obj, receiver, index, vp, strict); michael@0: } michael@0: michael@0: static bool nonNativeSetProperty(JSContext *cx, js::HandleObject obj, michael@0: js::HandleId id, js::MutableHandleValue vp, bool strict); michael@0: static bool nonNativeSetElement(JSContext *cx, js::HandleObject obj, michael@0: uint32_t index, js::MutableHandleValue vp, bool strict); michael@0: michael@0: static bool getGenericAttributes(JSContext *cx, js::HandleObject obj, michael@0: js::HandleId id, unsigned *attrsp) michael@0: { michael@0: js::GenericAttributesOp op = obj->getOps()->getGenericAttributes; michael@0: return (op ? op : js::baseops::GetAttributes)(cx, obj, id, attrsp); michael@0: } michael@0: michael@0: static inline bool setGenericAttributes(JSContext *cx, js::HandleObject obj, michael@0: js::HandleId id, unsigned *attrsp); michael@0: michael@0: static inline bool deleteProperty(JSContext *cx, js::HandleObject obj, michael@0: js::HandlePropertyName name, michael@0: bool *succeeded); michael@0: static inline bool deleteElement(JSContext *cx, js::HandleObject obj, michael@0: uint32_t index, bool *succeeded); michael@0: static bool deleteByValue(JSContext *cx, js::HandleObject obj, michael@0: const js::Value &property, bool *succeeded); michael@0: michael@0: static inline bool watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, michael@0: JS::HandleObject callable); michael@0: static inline bool unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id); michael@0: michael@0: static bool enumerate(JSContext *cx, JS::HandleObject obj, JSIterateOp iterop, michael@0: JS::MutableHandleValue statep, JS::MutableHandleId idp) michael@0: { michael@0: JSNewEnumerateOp op = obj->getOps()->enumerate; michael@0: return (op ? op : JS_EnumerateState)(cx, obj, iterop, statep, idp); michael@0: } michael@0: michael@0: static bool defaultValue(JSContext *cx, js::HandleObject obj, JSType hint, michael@0: js::MutableHandleValue vp) michael@0: { michael@0: JSConvertOp op = obj->getClass()->convert; michael@0: bool ok; michael@0: if (op == JS_ConvertStub) michael@0: ok = js::DefaultValue(cx, obj, hint, vp); michael@0: else michael@0: ok = op(cx, obj, hint, vp); michael@0: JS_ASSERT_IF(ok, vp.isPrimitive()); michael@0: return ok; michael@0: } michael@0: michael@0: static JSObject *thisObject(JSContext *cx, js::HandleObject obj) michael@0: { michael@0: JSObjectOp op = obj->getOps()->thisObject; michael@0: return op ? op(cx, obj) : obj; michael@0: } michael@0: michael@0: static bool thisObject(JSContext *cx, const js::Value &v, js::Value *vp); michael@0: michael@0: static bool swap(JSContext *cx, JS::HandleObject a, JS::HandleObject b); michael@0: michael@0: inline void initArrayClass(); michael@0: michael@0: /* michael@0: * In addition to the generic object interface provided by JSObject, michael@0: * specific types of objects may provide additional operations. To access, michael@0: * these addition operations, callers should use the pattern: michael@0: * michael@0: * if (obj.is()) { michael@0: * XObject &x = obj.as(); michael@0: * x.foo(); michael@0: * } michael@0: * michael@0: * These XObject classes form a hierarchy. For example, for a cloned block michael@0: * object, the following predicates are true: is, michael@0: * is, is and is. Each of michael@0: * these has a respective class that derives and adds operations. michael@0: * michael@0: * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file michael@0: * triplet (along with any class YObject that derives XObject). michael@0: * michael@0: * Note that X represents a low-level representation and does not query the michael@0: * [[Class]] property of object defined by the spec (for this, see michael@0: * js::ObjectClassIs). michael@0: */ michael@0: michael@0: template michael@0: inline bool is() const { return getClass() == &T::class_; } michael@0: michael@0: template michael@0: T &as() { michael@0: JS_ASSERT(is()); michael@0: return *static_cast(this); michael@0: } michael@0: michael@0: template michael@0: const T &as() const { michael@0: JS_ASSERT(is()); michael@0: return *static_cast(this); michael@0: } michael@0: michael@0: static inline js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; } michael@0: michael@0: #ifdef DEBUG michael@0: void dump(); michael@0: #endif michael@0: michael@0: private: michael@0: static void staticAsserts() { michael@0: static_assert(sizeof(JSObject) == sizeof(js::shadow::Object), michael@0: "shadow interface must match actual interface"); michael@0: static_assert(sizeof(JSObject) == sizeof(js::ObjectImpl), michael@0: "JSObject itself must not have any fields"); michael@0: static_assert(sizeof(JSObject) % sizeof(js::Value) == 0, michael@0: "fixed slots after an object must be aligned"); michael@0: static_assert(js::shadow::Object::MAX_FIXED_SLOTS == MAX_FIXED_SLOTS, michael@0: "We shouldn't be confused about our actual maximum " michael@0: "number of fixed slots"); michael@0: } michael@0: michael@0: JSObject() MOZ_DELETE; michael@0: JSObject(const JSObject &other) MOZ_DELETE; michael@0: void operator=(const JSObject &other) MOZ_DELETE; michael@0: }; michael@0: michael@0: template michael@0: MOZ_ALWAYS_INLINE JS::Handle michael@0: js::RootedBase::as() const michael@0: { michael@0: const JS::Rooted &self = *static_cast*>(this); michael@0: JS_ASSERT(self->is()); michael@0: return Handle::fromMarkedLocation(reinterpret_cast(self.address())); michael@0: } michael@0: michael@0: /* michael@0: * The only sensible way to compare JSObject with == is by identity. We use michael@0: * const& instead of * as a syntactic way to assert non-null. This leads to an michael@0: * abundance of address-of operators to identity. Hence this overload. michael@0: */ michael@0: static MOZ_ALWAYS_INLINE bool michael@0: operator==(const JSObject &lhs, const JSObject &rhs) michael@0: { michael@0: return &lhs == &rhs; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool michael@0: operator!=(const JSObject &lhs, const JSObject &rhs) michael@0: { michael@0: return &lhs != &rhs; michael@0: } michael@0: michael@0: struct JSObject_Slots2 : JSObject { js::Value fslots[2]; }; michael@0: struct JSObject_Slots4 : JSObject { js::Value fslots[4]; }; michael@0: struct JSObject_Slots8 : JSObject { js::Value fslots[8]; }; michael@0: struct JSObject_Slots12 : JSObject { js::Value fslots[12]; }; michael@0: struct JSObject_Slots16 : JSObject { js::Value fslots[16]; }; michael@0: michael@0: static inline bool michael@0: js_IsCallable(const js::Value &v) michael@0: { michael@0: return v.isObject() && v.toObject().isCallable(); michael@0: } michael@0: michael@0: inline JSObject * michael@0: GetInnerObject(JSContext *cx, js::HandleObject obj) michael@0: { michael@0: if (JSObjectOp op = obj->getClass()->ext.innerObject) michael@0: return op(cx, obj); michael@0: return obj; michael@0: } michael@0: michael@0: inline JSObject * michael@0: GetOuterObject(JSContext *cx, js::HandleObject obj) michael@0: { michael@0: if (JSObjectOp op = obj->getClass()->ext.outerObject) michael@0: return op(cx, obj); michael@0: return obj; michael@0: } michael@0: michael@0: class JSValueArray { michael@0: public: michael@0: const jsval *array; michael@0: size_t length; michael@0: michael@0: JSValueArray(const jsval *v, size_t c) : array(v), length(c) {} michael@0: }; michael@0: michael@0: class ValueArray { michael@0: public: michael@0: js::Value *array; michael@0: size_t length; michael@0: michael@0: ValueArray(js::Value *v, size_t c) : array(v), length(c) {} michael@0: }; michael@0: michael@0: namespace js { michael@0: michael@0: /* Set *resultp to tell whether obj has an own property with the given id. */ michael@0: bool michael@0: HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *resultp); michael@0: michael@0: template michael@0: extern bool michael@0: HasOwnProperty(JSContext *cx, LookupGenericOp lookup, michael@0: typename MaybeRooted::HandleType obj, michael@0: typename MaybeRooted::HandleType id, michael@0: typename MaybeRooted::MutableHandleType objp, michael@0: typename MaybeRooted::MutableHandleType propp); michael@0: michael@0: typedef JSObject *(*ClassInitializerOp)(JSContext *cx, JS::HandleObject obj); michael@0: michael@0: /* Fast access to builtin constructors and prototypes. */ michael@0: bool michael@0: GetBuiltinConstructor(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp); michael@0: michael@0: bool michael@0: GetBuiltinPrototype(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp); michael@0: michael@0: const Class * michael@0: ProtoKeyToClass(JSProtoKey key); michael@0: michael@0: JSObject * michael@0: GetBuiltinPrototypePure(GlobalObject *global, JSProtoKey protoKey); michael@0: michael@0: extern bool michael@0: SetClassAndProto(JSContext *cx, HandleObject obj, michael@0: const Class *clasp, Handle proto, bool *succeeded); michael@0: michael@0: /* michael@0: * Property-lookup-based access to interface and prototype objects for classes. michael@0: * If the class is built-in (hhas a non-null JSProtoKey), these forward to michael@0: * GetClass{Object,Prototype}. michael@0: */ michael@0: michael@0: bool michael@0: FindClassObject(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp); michael@0: michael@0: extern bool michael@0: FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp); michael@0: michael@0: } /* namespace js */ michael@0: michael@0: /* michael@0: * Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp. michael@0: */ michael@0: extern const char js_watch_str[]; michael@0: extern const char js_unwatch_str[]; michael@0: extern const char js_hasOwnProperty_str[]; michael@0: extern const char js_isPrototypeOf_str[]; michael@0: extern const char js_propertyIsEnumerable_str[]; michael@0: michael@0: #ifdef JS_OLD_GETTER_SETTER_METHODS michael@0: extern const char js_defineGetter_str[]; michael@0: extern const char js_defineSetter_str[]; michael@0: extern const char js_lookupGetter_str[]; michael@0: extern const char js_lookupSetter_str[]; michael@0: #endif michael@0: michael@0: extern bool michael@0: js_PopulateObject(JSContext *cx, js::HandleObject newborn, js::HandleObject props); michael@0: michael@0: michael@0: namespace js { michael@0: michael@0: extern bool michael@0: DefineOwnProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, michael@0: JS::HandleValue descriptor, bool *bp); michael@0: michael@0: extern bool michael@0: DefineOwnProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, michael@0: JS::Handle descriptor, bool *bp); michael@0: michael@0: /* michael@0: * The NewObjectKind allows an allocation site to specify the type properties michael@0: * and lifetime requirements that must be fixed at allocation time. michael@0: */ michael@0: enum NewObjectKind { michael@0: /* This is the default. Most objects are generic. */ michael@0: GenericObject, michael@0: michael@0: /* michael@0: * Singleton objects are treated specially by the type system. This flag michael@0: * ensures that the new object is automatically set up correctly as a michael@0: * singleton and is allocated in the correct heap. michael@0: */ michael@0: SingletonObject, michael@0: michael@0: /* michael@0: * Objects which may be marked as a singleton after allocation must still michael@0: * be allocated on the correct heap, but are not automatically setup as a michael@0: * singleton after allocation. michael@0: */ michael@0: MaybeSingletonObject, michael@0: michael@0: /* michael@0: * Objects which will not benefit from being allocated in the nursery michael@0: * (e.g. because they are known to have a long lifetime) may be allocated michael@0: * with this kind to place them immediately into the tenured generation. michael@0: */ michael@0: TenuredObject michael@0: }; michael@0: michael@0: inline gc::InitialHeap michael@0: GetInitialHeap(NewObjectKind newKind, const Class *clasp) michael@0: { michael@0: if (clasp->finalize || newKind != GenericObject) michael@0: return gc::TenuredHeap; michael@0: return gc::DefaultHeap; michael@0: } michael@0: michael@0: // Specialized call for constructing |this| with a known function callee, michael@0: // and a known prototype. michael@0: extern JSObject * michael@0: CreateThisForFunctionWithProto(JSContext *cx, js::HandleObject callee, JSObject *proto, michael@0: NewObjectKind newKind = GenericObject); michael@0: michael@0: // Specialized call for constructing |this| with a known function callee. michael@0: extern JSObject * michael@0: CreateThisForFunction(JSContext *cx, js::HandleObject callee, NewObjectKind newKind); michael@0: michael@0: // Generic call for constructing |this|. michael@0: extern JSObject * michael@0: CreateThis(JSContext *cx, const js::Class *clasp, js::HandleObject callee); michael@0: michael@0: extern JSObject * michael@0: CloneObject(JSContext *cx, HandleObject obj, Handle proto, HandleObject parent); michael@0: michael@0: extern JSObject * michael@0: DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind = GenericObject); michael@0: michael@0: /* michael@0: * Return successfully added or changed shape or nullptr on error. michael@0: */ michael@0: extern bool michael@0: DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, michael@0: PropertyOp getter, StrictPropertyOp setter, unsigned attrs); michael@0: michael@0: extern bool michael@0: LookupNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, michael@0: js::MutableHandleObject objp, js::MutableHandleShape propp); michael@0: michael@0: /* michael@0: * Call the [[DefineOwnProperty]] internal method of obj. michael@0: * michael@0: * If obj is an array, this follows ES5 15.4.5.1. michael@0: * If obj is any other native object, this follows ES5 8.12.9. michael@0: * If obj is a proxy, this calls the proxy handler's defineProperty method. michael@0: * Otherwise, this reports an error and returns false. michael@0: */ michael@0: extern bool michael@0: DefineProperty(JSContext *cx, js::HandleObject obj, michael@0: js::HandleId id, const PropDesc &desc, bool throwError, michael@0: bool *rval); michael@0: michael@0: bool michael@0: DefineProperties(JSContext *cx, HandleObject obj, HandleObject props); michael@0: michael@0: /* michael@0: * Read property descriptors from props, as for Object.defineProperties. See michael@0: * ES5 15.2.3.7 steps 3-5. michael@0: */ michael@0: extern bool michael@0: ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors, michael@0: AutoIdVector *ids, AutoPropDescArrayRooter *descs); michael@0: michael@0: /* Read the name using a dynamic lookup on the scopeChain. */ michael@0: extern bool michael@0: LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, michael@0: MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp); michael@0: michael@0: extern bool michael@0: LookupNameNoGC(JSContext *cx, PropertyName *name, JSObject *scopeChain, michael@0: JSObject **objp, JSObject **pobjp, Shape **propp); michael@0: michael@0: /* michael@0: * Like LookupName except returns the global object if 'name' is not found in michael@0: * any preceding non-global scope. michael@0: * michael@0: * Additionally, pobjp and propp are not needed by callers so they are not michael@0: * returned. michael@0: */ michael@0: extern bool michael@0: LookupNameWithGlobalDefault(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, michael@0: MutableHandleObject objp); michael@0: michael@0: } michael@0: michael@0: extern JSObject * michael@0: js_FindVariableScope(JSContext *cx, JSFunction **funp); michael@0: michael@0: michael@0: namespace js { michael@0: michael@0: bool michael@0: NativeGet(JSContext *cx, js::Handle obj, js::Handle pobj, michael@0: js::Handle shape, js::MutableHandle vp); michael@0: michael@0: template michael@0: bool michael@0: NativeSet(typename js::ExecutionModeTraits::ContextType cx, michael@0: js::Handle obj, js::Handle receiver, michael@0: js::Handle shape, bool strict, js::MutableHandleValue vp); michael@0: michael@0: bool michael@0: LookupPropertyPure(JSObject *obj, jsid id, JSObject **objp, Shape **propp); michael@0: michael@0: bool michael@0: GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp); michael@0: michael@0: inline bool michael@0: GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, PropertyName *name, Value *vp) michael@0: { michael@0: return GetPropertyPure(cx, obj, NameToId(name), vp); michael@0: } michael@0: michael@0: bool michael@0: GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, michael@0: MutableHandle desc); michael@0: michael@0: bool michael@0: GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp); michael@0: michael@0: bool michael@0: NewPropertyDescriptorObject(JSContext *cx, Handle desc, MutableHandleValue vp); michael@0: michael@0: /* michael@0: * If obj has an already-resolved data property for id, return true and michael@0: * store the property value in *vp. michael@0: */ michael@0: extern bool michael@0: HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp); michael@0: michael@0: inline bool michael@0: HasDataProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp) michael@0: { michael@0: return HasDataProperty(cx, obj, NameToId(name), vp); michael@0: } michael@0: michael@0: extern bool michael@0: IsDelegate(JSContext *cx, HandleObject obj, const Value &v, bool *result); michael@0: michael@0: // obj is a JSObject*, but we root it immediately up front. We do it michael@0: // that way because we need a Rooted temporary in this method anyway. michael@0: extern bool michael@0: IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result); michael@0: michael@0: bool michael@0: GetObjectElementOperationPure(ThreadSafeContext *cx, JSObject *obj, const Value &prop, Value *vp); michael@0: michael@0: /* Wrap boolean, number or string as Boolean, Number or String object. */ michael@0: extern JSObject * michael@0: PrimitiveToObject(JSContext *cx, const Value &v); michael@0: michael@0: } /* namespace js */ michael@0: michael@0: namespace js { michael@0: michael@0: /* michael@0: * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might michael@0: * already be an object, use ToObject. reportCantConvert controls how null and michael@0: * undefined errors are reported. michael@0: */ michael@0: extern JSObject * michael@0: ToObjectSlow(JSContext *cx, HandleValue vp, bool reportScanStack); michael@0: michael@0: /* For object conversion in e.g. native functions. */ michael@0: MOZ_ALWAYS_INLINE JSObject * michael@0: ToObject(JSContext *cx, HandleValue vp) michael@0: { michael@0: if (vp.isObject()) michael@0: return &vp.toObject(); michael@0: return ToObjectSlow(cx, vp, false); michael@0: } michael@0: michael@0: /* For converting stack values to objects. */ michael@0: MOZ_ALWAYS_INLINE JSObject * michael@0: ToObjectFromStack(JSContext *cx, HandleValue vp) michael@0: { michael@0: if (vp.isObject()) michael@0: return &vp.toObject(); michael@0: return ToObjectSlow(cx, vp, true); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: XDRObjectLiteral(XDRState *xdr, MutableHandleObject obj); michael@0: michael@0: extern JSObject * michael@0: CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj); michael@0: michael@0: } /* namespace js */ michael@0: michael@0: extern void michael@0: js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize); michael@0: michael@0: extern bool michael@0: js_ReportGetterOnlyAssignment(JSContext *cx, bool strict); michael@0: michael@0: michael@0: namespace js { michael@0: michael@0: extern JSObject * michael@0: NonNullObject(JSContext *cx, const Value &v); michael@0: michael@0: extern const char * michael@0: InformalValueTypeName(const Value &v); michael@0: michael@0: extern bool michael@0: GetFirstArgumentAsObject(JSContext *cx, const CallArgs &args, const char *method, michael@0: MutableHandleObject objp); michael@0: michael@0: /* Helpers for throwing. These always return false. */ michael@0: extern bool michael@0: Throw(JSContext *cx, jsid id, unsigned errorNumber); michael@0: michael@0: extern bool michael@0: Throw(JSContext *cx, JSObject *obj, unsigned errorNumber); michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #endif /* jsobj_h */