jsobj.h
/* -*- 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<PropDesc, 1> 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 <AllowGC allowGC>
extern bool
LookupProperty(ExclusiveContext* cx,
typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
typename MaybeRooted<jsid, allowGC>::HandleType id,
typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
typename MaybeRooted<Shape*, allowGC>::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 <ExecutionMode mode>
extern bool
SetPropertyHelper(typename ExecutionModeTraits<mode>::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<js::TaggedProto> 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 <js::ExecutionMode mode>
static inline js::Shape*
getOrLookupChildProperty(typename js::ExecutionModeTraits<mode>::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 <js::ExecutionMode mode>
static js::Shape*
addPropertyInternal(typename js::ExecutionModeTraits<mode>::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 <js::ExecutionMode mode>
static js::Shape*
putProperty(typename js::ExecutionModeTraits<mode>::ExclusiveContextType cx,
JS::HandleObject obj, JS::HandleId id,
JSPropertyOp getter, JSStrictPropertyOp setter,
uint32_t slot, unsigned attrs,
unsigned flags);
template <js::ExecutionMode mode>
static inline js::Shape*
putProperty(typename js::ExecutionModeTraits<mode>::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 <js::ExecutionMode mode>
static js::Shape*
changeProperty(typename js::ExecutionModeTraits<mode>::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<js::SequentialExecution>(
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>()) {
* XObject& x = obj.as<XObject>();
* x.foo();
* }
*
* These XObject classes form a hierarchy. For example, for a cloned block
* object, the following predicates are true: is<ClonedBlockObject>,
* is<BlockObject>, is<NestedScopeObject> and is<ScopeObject>. 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 <class T>
inline bool is() const { return getClass() == &T::class_; }
template <class T>
T& as() {
JS_ASSERT(is<T>());
return *static_cast<T*>(this);
}
template <class T>
const T& as() const {
JS_ASSERT(is<T>());
return *static_cast<const T*>(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 <class U>
MOZ_ALWAYS_INLINE JS::Handle<U*>
js::RootedBase<JSObject*>::as() const
{
const JS::Rooted<JSObject*>& self = *static_cast<const JS::Rooted<JSObject*>*>(this);
JS_ASSERT(self->is<U>());
return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(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 <AllowGC allowGC>
extern bool
HasOwnProperty(JSContext* cx, LookupGenericOp lookup,
typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
typename MaybeRooted<jsid, allowGC>::HandleType id,
typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
typename MaybeRooted<Shape*, allowGC>::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<TaggedProto> 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<js::PropertyDescriptor> 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<js::TaggedProto> 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<JSObject*> obj, js::Handle<JSObject*> pobj,
js::Handle<js::Shape*> shape, js::MutableHandle<js::Value> vp);
template <js::ExecutionMode mode>
bool
NativeSet(typename js::ExecutionModeTraits<mode>::ContextType cx,
js::Handle<JSObject*> obj, js::Handle<JSObject*> receiver,
js::Handle<js::Shape*> 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<PropertyDescriptor> desc);
bool
GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
bool
NewPropertyDescriptorObject(JSContext* cx, Handle<PropertyDescriptor> 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<XDRMode mode>
bool
XDRObjectLiteral(XDRState<mode>* 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 */