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