Mypal/js/src/jsobj.h

1365 lines
47 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/Conversions.h"
#include "js/GCAPI.h"
#include "js/GCVector.h"
#include "js/HeapAPI.h"
#include "vm/Shape.h"
#include "vm/String.h"
#include "vm/Xdr.h"
namespace JS {
struct ClassInfo;
} // namespace JS
namespace js {
using PropertyDescriptorVector = JS::GCVector<JS::PropertyDescriptor>;
class GCMarker;
class Nursery;
namespace gc {
class RelocationOverlay;
} // namespace gc
inline JSObject*
CastAsObject(GetterOp op)
{
return JS_FUNC_TO_DATA_PTR(JSObject*, op);
}
inline JSObject*
CastAsObject(SetterOp op)
{
return JS_FUNC_TO_DATA_PTR(JSObject*, op);
}
inline Value
CastAsObjectJsval(GetterOp op)
{
return ObjectOrNullValue(CastAsObject(op));
}
inline Value
CastAsObjectJsval(SetterOp op)
{
return ObjectOrNullValue(CastAsObject(op));
}
/******************************************************************************/
extern const Class IntlClass;
extern const Class JSONClass;
extern const Class MathClass;
class GlobalObject;
class NewObjectCache;
enum class IntegrityLevel {
Sealed,
Frozen
};
// Forward declarations, required for later friend declarations.
bool PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result, IntegrityLevel level = IntegrityLevel::Sealed);
bool SetImmutablePrototype(js::ExclusiveContext* cx, JS::HandleObject obj, bool* succeeded);
} /* namespace js */
/*
* A JavaScript object. The members common to all objects are as follows:
*
* - The |group_| member stores the group of the object, which contains its
* prototype object, its class and the possible types of its properties.
*
* Subclasses of JSObject --- mainly NativeObject and JSFunction --- add more
* members. Notable among these is the object's shape, which stores flags and
* some other state, and, for native objects, the layout of all its properties.
* The second word of a JSObject generally stores its shape; if the second word
* stores anything else, the value stored cannot be a valid Shape* pointer, so
* that shape guards can be performed on objects without regard to the specific
* layout in use.
*/
class JSObject : public js::gc::Cell
{
protected:
js::GCPtrObjectGroup group_;
private:
friend class js::Shape;
friend class js::GCMarker;
friend class js::NewObjectCache;
friend class js::Nursery;
friend class js::gc::RelocationOverlay;
friend bool js::PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result, js::IntegrityLevel level);
friend bool js::SetImmutablePrototype(js::ExclusiveContext* cx, JS::HandleObject obj,
bool* succeeded);
// Make a new group to use for a singleton object.
static js::ObjectGroup* makeLazyGroup(JSContext* cx, js::HandleObject obj);
public:
bool isNative() const {
return getClass()->isNative();
}
const js::Class* getClass() const {
return group_->clasp();
}
const JSClass* getJSClass() const {
return Jsvalify(getClass());
}
bool hasClass(const js::Class* c) const {
return getClass() == c;
}
js::LookupPropertyOp getOpsLookupProperty() const { return getClass()->getOpsLookupProperty(); }
js::DefinePropertyOp getOpsDefineProperty() const { return getClass()->getOpsDefineProperty(); }
js::HasPropertyOp getOpsHasProperty() const { return getClass()->getOpsHasProperty(); }
js::GetPropertyOp getOpsGetProperty() const { return getClass()->getOpsGetProperty(); }
js::SetPropertyOp getOpsSetProperty() const { return getClass()->getOpsSetProperty(); }
js::GetOwnPropertyOp getOpsGetOwnPropertyDescriptor()
const { return getClass()->getOpsGetOwnPropertyDescriptor(); }
js::DeletePropertyOp getOpsDeleteProperty() const { return getClass()->getOpsDeleteProperty(); }
js::GetElementsOp getOpsGetElements() const { return getClass()->getOpsGetElements(); }
JSNewEnumerateOp getOpsEnumerate() const { return getClass()->getOpsEnumerate(); }
JSFunToStringOp getOpsFunToString() const { return getClass()->getOpsFunToString(); }
js::ObjectGroup* group() const {
MOZ_ASSERT(!hasLazyGroup());
return groupRaw();
}
js::ObjectGroup* groupRaw() const {
return group_;
}
/*
* Whether this is the only object which has its specified group. This
* object will have its group constructed lazily as needed by analysis.
*/
bool isSingleton() const {
return group_->singleton();
}
/*
* Whether the object's group has not been constructed yet. If an object
* might have a lazy group, use getGroup() below, otherwise group().
*/
bool hasLazyGroup() const {
return group_->lazy();
}
JSCompartment* compartment() const { return group_->compartment(); }
JSCompartment* maybeCompartment() const { return compartment(); }
inline js::Shape* maybeShape() const;
inline js::Shape* ensureShape(js::ExclusiveContext* cx);
/*
* 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::HandleObjectGroup group);
// Set the initial slots and elements of an object. These pointers are only
// valid for native objects, but during initialization are set for all
// objects. For non-native objects, these must not be dynamically allocated
// pointers which leak when the non-native object finishes initialization.
inline void setInitialSlotsMaybeNonNative(js::HeapSlot* slots);
inline void setInitialElementsMaybeNonNative(js::HeapSlot* elements);
enum GenerateShape {
GENERATE_NONE,
GENERATE_SHAPE
};
static bool setFlags(js::ExclusiveContext* cx, JS::HandleObject obj, js::BaseShape::Flag flags,
GenerateShape generateShape = GENERATE_NONE);
inline bool hasAllFlags(js::BaseShape::Flag flags) const;
/*
* An object is a delegate if it is on another object's prototype or scope
* chain, and therefore the delegate might be asked implicitly to get or
* set a property on behalf of another object. Delegates may be accessed
* directly too, as may any object, but only those objects linked after the
* head of any prototype or scope chain are flagged as delegates. This
* definition helps to optimize shape-based property cache invalidation
* (see Purge{Scope,Proto}Chain in jsobj.cpp).
*/
inline bool isDelegate() const;
static bool setDelegate(js::ExclusiveContext* cx, JS::HandleObject obj) {
return setFlags(cx, obj, js::BaseShape::DELEGATE, GENERATE_SHAPE);
}
inline bool isBoundFunction() const;
inline bool hasSpecialEquality() const;
// A "qualified" varobj is the object on which "qualified" variable
// declarations (i.e., those defined with "var") are kept.
//
// Conceptually, when a var binding is defined, it is defined on the
// innermost qualified varobj on the scope chain.
//
// Function scopes (CallObjects) are qualified varobjs, and there can be
// no other qualified varobj that is more inner for var bindings in that
// function. As such, all references to local var bindings in a function
// may be statically bound to the function scope. This is subject to
// further optimization. Unaliased bindings inside functions reside
// entirely on the frame, not in CallObjects.
//
// Global scopes are also qualified varobjs. It is possible to statically
// know, for a given script, that are no more inner qualified varobjs, so
// free variable references can be statically bound to the global.
//
// Finally, there are non-syntactic qualified varobjs used by embedders
// (e.g., Gecko and XPConnect), as they often wish to run scripts under a
// scope that captures var bindings.
inline bool isQualifiedVarObj() const;
static bool setQualifiedVarObj(js::ExclusiveContext* cx, JS::HandleObject obj) {
return setFlags(cx, obj, js::BaseShape::QUALIFIED_VAROBJ);
}
// An "unqualified" varobj is the object on which "unqualified"
// assignments (i.e., bareword assignments for which the LHS does not
// exist on the scope chain) are kept.
inline bool isUnqualifiedVarObj() const;
// Objects with an uncacheable proto can have their prototype mutated
// without inducing a shape change on the object. JIT inline caches should
// do an explicit group guard to guard against this. Singletons always
// generate a new shape when their prototype changes, regardless of this
// hasUncacheableProto flag.
inline bool hasUncacheableProto() const;
static bool setUncacheableProto(js::ExclusiveContext* cx, JS::HandleObject obj) {
MOZ_ASSERT(obj->hasStaticPrototype(),
"uncacheability as a concept is only applicable to static "
"(not dynamically-computed) prototypes");
return setFlags(cx, obj, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
}
/*
* Whether SETLELEM was used to access this object. See also the comment near
* PropertyTree::MAX_HEIGHT.
*/
inline bool hadElementsAccess() const;
static bool setHadElementsAccess(js::ExclusiveContext* cx, JS::HandleObject obj) {
return setFlags(cx, obj, js::BaseShape::HAD_ELEMENTS_ACCESS);
}
/*
* Whether there may be indexed properties on this object, excluding any in
* the object's elements.
*/
inline bool isIndexed() const;
/*
* If this object was instantiated with `new Ctor`, return the constructor's
* display atom. Otherwise, return nullptr.
*/
static bool constructorDisplayAtom(JSContext* cx, js::HandleObject obj,
js::MutableHandleAtom name);
/*
* The same as constructorDisplayAtom above, however if this object has a
* lazy group, nullptr is returned. This allows for use in situations that
* cannot GC and where having some information, even if it is inconsistently
* available, is better than no information.
*/
JSAtom* maybeConstructorDisplayAtom() const;
/* GC support. */
void traceChildren(JSTracer* trc);
void fixupAfterMovingGC();
static const JS::TraceKind TraceKind = JS::TraceKind::Object;
static const size_t MaxTagBits = 3;
static bool isNullLike(const JSObject* obj) { return uintptr_t(obj) < (1 << MaxTagBits); }
MOZ_ALWAYS_INLINE JS::Zone* zone() const {
return group_->zone();
}
MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZone() const {
return JS::shadow::Zone::asShadowZone(zone());
}
MOZ_ALWAYS_INLINE JS::Zone* zoneFromAnyThread() const {
return group_->zoneFromAnyThread();
}
MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZoneFromAnyThread() const {
return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
}
static MOZ_ALWAYS_INLINE void readBarrier(JSObject* obj);
static MOZ_ALWAYS_INLINE void writeBarrierPre(JSObject* obj);
static MOZ_ALWAYS_INLINE void writeBarrierPost(void* cellp, JSObject* prev, JSObject* next);
/* Return the allocKind we would use if we were to tenure this object. */
js::gc::AllocKind allocKindForTenure(const js::Nursery& nursery) const;
size_t tenuredSizeOfThis() const {
MOZ_ASSERT(isTenured());
return js::gc::Arena::thingSize(asTenured().getAllocKind());
}
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info);
// We can only use addSizeOfExcludingThis on tenured objects: it assumes it
// can apply mallocSizeOf to bits and pieces of the object, whereas objects
// in the nursery may have those bits and pieces allocated in the nursery
// along with them, and are not each their own malloc blocks.
size_t sizeOfIncludingThisInNursery() const;
// Marks this object as having a singleton group, and leave the group lazy.
// Constructs a new, unique shape for the object. This should only be
// called for an object that was just created.
static inline bool setSingleton(js::ExclusiveContext* cx, js::HandleObject obj);
// Change an existing object to have a singleton group.
static bool changeToSingleton(JSContext* cx, js::HandleObject obj);
static inline js::ObjectGroup* getGroup(JSContext* cx, js::HandleObject obj);
const js::GCPtrObjectGroup& groupFromGC() const {
/* Direct field access for use by GC. */
return group_;
}
/*
* We permit proxies to dynamically compute their prototype if desired.
* (Not all proxies will so desire: in particular, most DOM proxies can
* track their prototype with a single, nullable JSObject*.) If a proxy
* so desires, we store (JSObject*)0x1 in the proto field of the object's
* group.
*
* We offer three ways to get an object's prototype:
*
* 1. obj->staticPrototype() returns the prototype, but it asserts if obj
* is a proxy, and the proxy has opted to dynamically compute its
* prototype using a getPrototype() handler.
* 2. obj->taggedProto() returns a TaggedProto, which can be tested to
* check if the proto is an object, nullptr, or lazily computed.
* 3. js::GetPrototype(cx, obj, &proto) computes the proto of an object.
* If obj is a proxy with dynamically-computed prototype, this code may
* perform arbitrary behavior (allocation, GC, run JS) while computing
* the proto.
*/
js::TaggedProto taggedProto() const {
return group_->proto();
}
bool hasTenuredProto() const;
bool uninlinedIsProxy() const;
JSObject* staticPrototype() const {
MOZ_ASSERT(hasStaticPrototype());
return taggedProto().toObjectOrNull();
}
// Normal objects and a subset of proxies have an uninteresting, static
// (albeit perhaps mutable) [[Prototype]]. For such objects the
// [[Prototype]] is just a value returned when needed for accesses, or
// modified in response to requests. These objects store the
// [[Prototype]] directly within |obj->group_|.
bool hasStaticPrototype() const {
return !hasDynamicPrototype();
}
// The remaining proxies have a [[Prototype]] requiring dynamic computation
// for every access, going through the proxy handler {get,set}Prototype and
// setImmutablePrototype methods. (Wrappers particularly use this to keep
// the wrapper/wrappee [[Prototype]]s consistent.)
bool hasDynamicPrototype() const {
bool dynamic = taggedProto().isDynamic();
MOZ_ASSERT_IF(dynamic, uninlinedIsProxy());
MOZ_ASSERT_IF(dynamic, !isNative());
return dynamic;
}
// True iff this object's [[Prototype]] is immutable. Must be called only
// on objects with a static [[Prototype]]!
inline bool staticPrototypeIsImmutable() const;
inline void setGroup(js::ObjectGroup* group);
/*
* 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.
*/
inline bool isIteratedSingleton() const;
static bool setIteratedSingleton(js::ExclusiveContext* cx, JS::HandleObject obj) {
return setFlags(cx, obj, js::BaseShape::ITERATED_SINGLETON);
}
/*
* Mark an object as requiring its default 'new' type to have unknown
* properties.
*/
inline bool isNewGroupUnknown() const;
static bool setNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleObject obj);
// Mark an object as having its 'new' script information cleared.
inline bool wasNewScriptCleared() const;
static bool setNewScriptCleared(js::ExclusiveContext* cx, JS::HandleObject obj) {
return setFlags(cx, obj, js::BaseShape::NEW_SCRIPT_CLEARED);
}
/* Set a new prototype for an object with a singleton type. */
static bool splicePrototype(JSContext* cx, js::HandleObject obj, 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();
/*
* Environment chains.
*
* The environment chain of an object is the link in the search path when
* a script does a name lookup on an environment object. For JS internal
* environment objects --- Call, LexicalEnvironment, and WithEnvironment
* --- the chain is stored in the first fixed slot of the object. For
* other environment objects, the chain goes directly to the global.
*
* In code which is not marked hasNonSyntacticScope, environment chains
* can contain only syntactic environment objects (see
* IsSyntacticEnvironment) with a global object at the root as the
* environment of the outermost non-function script. In
* hasNonSyntacticScope code, the environment 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.
*/
/*
* Get the enclosing environment of an object. When called on a
* non-EnvironmentObject, this will just be the global (the name
* "enclosing environment" still applies in this situation because
* non-EnvironmentObjects can be on the environment chain).
*/
inline JSObject* enclosingEnvironment() const;
inline js::GlobalObject& global() const;
// In some rare cases the global object's compartment's global may not be
// the same global object. For this reason, we need to take extra care when
// tracing.
//
// These cases are:
// 1) The off-thread parsing task uses a dummy global since it cannot
// share with the actual global being used concurrently on the main
// thread.
// 2) A GC may occur when creating the GlobalObject, in which case the
// compartment global pointer may not yet be set. In this case there is
// nothing interesting to trace in the compartment.
inline bool isOwnGlobal(JSTracer*) const;
inline js::GlobalObject* globalForTracing(JSTracer*) const;
/*
* ES5 meta-object properties and operations.
*/
public:
// Indicates whether a non-proxy is extensible. Don't call on proxies!
// This method really shouldn't exist -- but there are a few internal
// places that want it (JITs and the like), and it'd be a pain to mark them
// all as friends.
inline bool nonProxyIsExtensible() const;
public:
/*
* Iterator-specific getters and setters.
*/
static const uint32_t ITER_CLASS_NFIXED_SLOTS = 1;
/*
* Back to generic stuff.
*/
bool isCallable() const;
bool isConstructor() const;
JSNative callHook() const;
JSNative constructHook() const;
MOZ_ALWAYS_INLINE void finalize(js::FreeOp* fop);
public:
static bool reportReadOnly(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR);
static bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR);
static bool reportNotExtensible(JSContext* cx, js::HandleObject obj,
unsigned report = JSREPORT_ERROR);
static bool nonNativeSetProperty(JSContext* cx, js::HandleObject obj, js::HandleId id,
js::HandleValue v, js::HandleValue receiver,
JS::ObjectOpResult& result);
static bool nonNativeSetElement(JSContext* cx, js::HandleObject obj, uint32_t index,
js::HandleValue v, js::HandleValue receiver,
JS::ObjectOpResult& result);
static bool swap(JSContext* cx, JS::HandleObject a, JS::HandleObject b);
private:
void fixDictionaryShapeAfterSwap();
public:
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<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::GetBuiltinClass).
*/
template <class T>
inline bool is() const { return getClass() == &T::class_; }
template <class T>
T& as() {
MOZ_ASSERT(this->is<T>());
return *static_cast<T*>(this);
}
template <class T>
const T& as() const {
MOZ_ASSERT(this->is<T>());
return *static_cast<const T*>(this);
}
#ifdef DEBUG
void dump(FILE* fp) const;
void dump() const;
#endif
/* JIT Accessors */
static size_t offsetOfGroup() { return offsetof(JSObject, group_); }
// Maximum size in bytes of a JSObject.
static const size_t MAX_BYTE_SIZE = 4 * sizeof(void*) + 16 * sizeof(JS::Value);
private:
JSObject() = delete;
JSObject(const JSObject& other) = delete;
void operator=(const JSObject& other) = 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);
MOZ_ASSERT(self->is<U>());
return Handle<U*>::fromMarkedLocation(reinterpret_cast<U* const*>(self.address()));
}
template <class U>
MOZ_ALWAYS_INLINE JS::Handle<U*>
js::HandleBase<JSObject*>::as() const
{
const JS::Handle<JSObject*>& self = *static_cast<const JS::Handle<JSObject*>*>(this);
MOZ_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;
}
// Size of the various GC thing allocation sizes used for objects.
struct JSObject_Slots0 : JSObject { void* data[3]; };
struct JSObject_Slots2 : JSObject { void* data[3]; js::Value fslots[2]; };
struct JSObject_Slots4 : JSObject { void* data[3]; js::Value fslots[4]; };
struct JSObject_Slots8 : JSObject { void* data[3]; js::Value fslots[8]; };
struct JSObject_Slots12 : JSObject { void* data[3]; js::Value fslots[12]; };
struct JSObject_Slots16 : JSObject { void* data[3]; js::Value fslots[16]; };
/* static */ MOZ_ALWAYS_INLINE void
JSObject::readBarrier(JSObject* obj)
{
MOZ_ASSERT_IF(obj, !isNullLike(obj));
if (obj && obj->isTenured())
obj->asTenured().readBarrier(&obj->asTenured());
}
/* static */ MOZ_ALWAYS_INLINE void
JSObject::writeBarrierPre(JSObject* obj)
{
MOZ_ASSERT_IF(obj, !isNullLike(obj));
if (obj && obj->isTenured())
obj->asTenured().writeBarrierPre(&obj->asTenured());
}
/* static */ MOZ_ALWAYS_INLINE void
JSObject::writeBarrierPost(void* cellp, JSObject* prev, JSObject* next)
{
MOZ_ASSERT(cellp);
MOZ_ASSERT_IF(next, !IsNullTaggedPointer(next));
MOZ_ASSERT_IF(prev, !IsNullTaggedPointer(prev));
// If the target needs an entry, add it.
js::gc::StoreBuffer* buffer;
if (next && (buffer = next->storeBuffer())) {
// If we know that the prev has already inserted an entry, we can skip
// doing the lookup to add the new entry. Note that we cannot safely
// assert the presence of the entry because it may have been added
// via a different store buffer.
if (prev && prev->storeBuffer())
return;
buffer->putCell(static_cast<js::gc::Cell**>(cellp));
return;
}
// Remove the prev entry if the new value does not need it.
if (prev && (buffer = prev->storeBuffer()))
buffer->unputCell(static_cast<js::gc::Cell**>(cellp));
}
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();
}
} /* namespace js */
class JSValueArray {
public:
const js::Value* array;
size_t length;
JSValueArray(const js::Value* 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 {
/*** Standard internal methods ********************************************************************
*
* The functions below are the fundamental operations on objects. See the
* comment about "Standard internal methods" in jsapi.h.
*/
/*
* ES6 [[GetPrototypeOf]]. Get obj's prototype, storing it in protop.
*
* If obj is definitely not a proxy, the infallible obj->getProto() can be used
* instead. See the comment on JSObject::getTaggedProto().
*/
inline bool
GetPrototype(JSContext* cx, HandleObject obj, MutableHandleObject protop);
/*
* ES6 [[SetPrototypeOf]]. Change obj's prototype to proto.
*
* Returns false on error, success of operation in *result. For example, if
* obj is not extensible, its prototype is fixed. js::SetPrototype will return
* true, because no exception is thrown for this; but *result will be false.
*/
extern bool
SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto,
ObjectOpResult& result);
/* Convenience function: like the above, but throw on failure. */
extern bool
SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto);
/*
* ES6 [[IsExtensible]]. Extensible objects can have new properties defined on
* them. Inextensible objects can't, and their [[Prototype]] slot is fixed as
* well.
*/
inline bool
IsExtensible(ExclusiveContext* cx, HandleObject obj, bool* extensible);
/*
* ES6 [[PreventExtensions]]. Attempt to change the [[Extensible]] bit on |obj|
* to false. Indicate success or failure through the |result| outparam, or
* actual error through the return value.
*
* The `level` argument is SM-specific. `obj` should have an integrity level of
* at least `level`.
*/
extern bool
PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result, IntegrityLevel level);
/* Convenience function. As above, but throw on failure. */
extern bool
PreventExtensions(JSContext* cx, HandleObject obj, IntegrityLevel level = IntegrityLevel::Sealed);
/*
* ES6 [[GetOwnProperty]]. Get a description of one of obj's own properties.
*
* If no such property exists on obj, return true with desc.object() set to
* null.
*/
extern bool
GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<JS::PropertyDescriptor> desc);
/* ES6 [[DefineOwnProperty]]. Define a property on obj. */
extern bool
DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
Handle<JS::PropertyDescriptor> desc, ObjectOpResult& result);
extern bool
DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value,
JSGetterOp getter, JSSetterOp setter, unsigned attrs, ObjectOpResult& result);
extern bool
DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
JSGetterOp getter, JSSetterOp setter, unsigned attrs, ObjectOpResult& result);
extern bool
DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value,
JSGetterOp getter, JSSetterOp setter, unsigned attrs, ObjectOpResult& result);
/*
* When the 'result' out-param is omitted, the behavior is the same as above, except
* that any failure results in a TypeError.
*/
extern bool
DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<JS::PropertyDescriptor> desc);
extern bool
DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value,
JSGetterOp getter = nullptr,
JSSetterOp setter = nullptr,
unsigned attrs = JSPROP_ENUMERATE);
extern bool
DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
JSGetterOp getter = nullptr,
JSSetterOp setter = nullptr,
unsigned attrs = JSPROP_ENUMERATE);
extern bool
DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value,
JSGetterOp getter = nullptr,
JSSetterOp setter = nullptr,
unsigned attrs = JSPROP_ENUMERATE);
/*
* ES6 [[Has]]. Set *foundp to true if `id in obj` (that is, if obj has an own
* or inherited property obj[id]), false otherwise.
*/
inline bool
HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
inline bool
HasProperty(JSContext* cx, HandleObject obj, PropertyName* name, bool* foundp);
/*
* ES6 [[Get]]. Get the value of the property `obj[id]`, or undefined if no
* such property exists.
*
* Typically obj == receiver; if obj != receiver then the caller is most likely
* a proxy using GetProperty to finish a property get that started out as
* `receiver[id]`, and we've already searched the prototype chain up to `obj`.
*/
inline bool
GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
MutableHandleValue vp);
inline bool
GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, PropertyName* name,
MutableHandleValue vp)
{
RootedId id(cx, NameToId(name));
return GetProperty(cx, obj, receiver, id, vp);
}
inline bool
GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id,
MutableHandleValue vp)
{
RootedValue receiverValue(cx, ObjectValue(*receiver));
return GetProperty(cx, obj, receiverValue, id, vp);
}
inline bool
GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, PropertyName* name,
MutableHandleValue vp)
{
RootedValue receiverValue(cx, ObjectValue(*receiver));
return GetProperty(cx, obj, receiverValue, name, vp);
}
inline bool
GetElement(JSContext* cx, HandleObject obj, HandleValue receiver, uint32_t index,
MutableHandleValue vp);
inline bool
GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index,
MutableHandleValue vp);
inline bool
GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, jsid id, Value* vp);
inline bool
GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, jsid id, Value* vp)
{
return GetPropertyNoGC(cx, obj, ObjectValue(*receiver), id, vp);
}
inline bool
GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, PropertyName* name, Value* vp)
{
return GetPropertyNoGC(cx, obj, receiver, NameToId(name), vp);
}
inline bool
GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, PropertyName* name, Value* vp)
{
return GetPropertyNoGC(cx, obj, ObjectValue(*receiver), name, vp);
}
inline bool
GetElementNoGC(JSContext* cx, JSObject* obj, const Value& receiver, uint32_t index, Value* vp);
inline bool
GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index, Value* vp);
/*
* ES6 [[Set]]. Carry out the assignment `obj[id] = v`.
*
* The `receiver` argument has to do with how [[Set]] interacts with the
* prototype chain and proxies. It's hard to explain and ES6 doesn't really
* try. Long story short, if you just want bog-standard assignment, pass
* `ObjectValue(*obj)` as receiver. Or better, use one of the signatures that
* doesn't have a receiver parameter.
*
* Callers pass obj != receiver e.g. when a proxy is involved, obj is the
* proxy's target, and the proxy is using SetProperty to finish an assignment
* that started out as `receiver[id] = v`, by delegating it to obj.
*/
inline bool
SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
HandleValue receiver, ObjectOpResult& result);
inline bool
SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
{
RootedValue receiver(cx, ObjectValue(*obj));
ObjectOpResult result;
return SetProperty(cx, obj, id, v, receiver, result) &&
result.checkStrict(cx, obj, id);
}
inline bool
SetProperty(JSContext* cx, HandleObject obj, PropertyName* name, HandleValue v,
HandleValue receiver, ObjectOpResult& result)
{
RootedId id(cx, NameToId(name));
return SetProperty(cx, obj, id, v, receiver, result);
}
inline bool
SetProperty(JSContext* cx, HandleObject obj, PropertyName* name, HandleValue v)
{
RootedId id(cx, NameToId(name));
RootedValue receiver(cx, ObjectValue(*obj));
ObjectOpResult result;
return SetProperty(cx, obj, id, v, receiver, result) &&
result.checkStrict(cx, obj, id);
}
inline bool
SetElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue v,
HandleValue receiver, ObjectOpResult& result);
/*
* ES6 draft rev 31 (15 Jan 2015) 7.3.3 Put (O, P, V, Throw), except that on
* success, the spec says this is supposed to return a boolean value, which we
* don't bother doing.
*/
inline bool
PutProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, bool strict)
{
RootedValue receiver(cx, ObjectValue(*obj));
ObjectOpResult result;
return SetProperty(cx, obj, id, v, receiver, result) &&
result.checkStrictErrorOrWarning(cx, obj, id, strict);
}
/*
* ES6 [[Delete]]. Equivalent to the JS code `delete obj[id]`.
*/
inline bool
DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result);
inline bool
DeleteElement(JSContext* cx, HandleObject obj, uint32_t index, ObjectOpResult& result);
/*** SpiderMonkey nonstandard internal methods ***************************************************/
/**
* If |obj| (underneath any functionally-transparent wrapper proxies) has as
* its [[GetPrototypeOf]] trap the ordinary [[GetPrototypeOf]] behavior defined
* for ordinary objects, set |*isOrdinary = true| and store |obj|'s prototype
* in |result|. Otherwise set |*isOrdinary = false|. In case of error, both
* outparams have unspecified value.
*/
extern bool
GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary,
MutableHandleObject protop);
/*
* Attempt to make |obj|'s [[Prototype]] immutable, such that subsequently
* trying to change it will not work. If an internal error occurred,
* returns false. Otherwise, |*succeeded| is set to true iff |obj|'s
* [[Prototype]] is now immutable.
*/
extern bool
SetImmutablePrototype(js::ExclusiveContext* cx, JS::HandleObject obj, bool* succeeded);
extern bool
GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<JS::PropertyDescriptor> desc);
/*
* Deprecated. A version of HasProperty that also returns the object on which
* the property was found (but that information is unreliable for proxies), and
* the Shape of the property, if native.
*/
extern bool
LookupProperty(JSContext* cx, HandleObject obj, HandleId id,
MutableHandleObject objp, MutableHandleShape propp);
inline bool
LookupProperty(JSContext* cx, HandleObject obj, PropertyName* name,
MutableHandleObject objp, MutableHandleShape propp)
{
RootedId id(cx, NameToId(name));
return LookupProperty(cx, obj, id, objp, propp);
}
/* Set *result to tell whether obj has an own property with the given id. */
extern bool
HasOwnProperty(JSContext* cx, HandleObject obj, HandleId id, bool* result);
/**
* This enum is used to select whether the defined functions should be marked as
* builtin native instrinsics for self-hosted code.
*/
enum DefineAsIntrinsic {
NotIntrinsic,
AsIntrinsic
};
extern bool
DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
DefineAsIntrinsic intrinsic);
/* ES6 draft rev 36 (2015 March 17) 7.1.1 ToPrimitive(vp[, preferredType]) */
extern bool
ToPrimitiveSlow(JSContext* cx, JSType hint, MutableHandleValue vp);
inline bool
ToPrimitive(JSContext* cx, MutableHandleValue vp)
{
if (vp.isPrimitive())
return true;
return ToPrimitiveSlow(cx, JSTYPE_VOID, vp);
}
inline bool
ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
{
if (vp.isPrimitive())
return true;
return ToPrimitiveSlow(cx, preferredType, vp);
}
/*
* toString support. (This isn't called GetClassName because there's a macro in
* <windows.h> with that name.)
*/
extern const char*
GetObjectClassName(JSContext* cx, HandleObject obj);
/*
* Return an object that may be used as `this` in place of obj. For most
* objects this just returns obj.
*
* Some JSObjects shouldn't be exposed directly to script. This includes (at
* least) WithEnvironmentObjects and Window objects. However, since both of
* those can be on scope chains, we sometimes would expose those as `this` if
* we were not so vigilant about calling GetThisValue where appropriate.
*
* See comments at ComputeImplicitThis.
*/
Value
GetThisValue(JSObject* obj);
/* * */
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);
extern bool
IsStandardPrototype(JSObject* obj, JSProtoKey key);
} /* 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
namespace js {
inline gc::InitialHeap
GetInitialHeap(NewObjectKind newKind, const Class* clasp)
{
if (newKind == NurseryAllocatedProxy) {
MOZ_ASSERT(clasp->isProxy());
MOZ_ASSERT(clasp->hasFinalize());
MOZ_ASSERT(!CanNurseryAllocateFinalizedClass(clasp));
return gc::DefaultHeap;
}
if (newKind != GenericObject)
return gc::TenuredHeap;
if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp))
return gc::TenuredHeap;
return gc::DefaultHeap;
}
bool
NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto,
NewObjectKind newKind, const Class* clasp);
// ES6 9.1.15 GetPrototypeFromConstructor.
extern bool
GetPrototypeFromConstructor(JSContext* cx, js::HandleObject newTarget, js::MutableHandleObject proto);
extern bool
GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, js::MutableHandleObject proto);
// Specialized call for constructing |this| with a known function callee,
// and a known prototype.
extern JSObject*
CreateThisForFunctionWithProto(JSContext* cx, js::HandleObject callee, HandleObject newTarget,
HandleObject proto, NewObjectKind newKind = GenericObject);
// Specialized call for constructing |this| with a known function callee.
extern JSObject*
CreateThisForFunction(JSContext* cx, js::HandleObject callee, js::HandleObject newTarget,
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);
extern JSObject*
DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKind = GenericObject);
inline JSGetterOp
CastAsGetterOp(JSObject* object)
{
return JS_DATA_TO_FUNC_PTR(JSGetterOp, object);
}
inline JSSetterOp
CastAsSetterOp(JSObject* object)
{
return JS_DATA_TO_FUNC_PTR(JSSetterOp, object);
}
/* ES6 draft rev 32 (2015 Feb 2) 6.2.4.5 ToPropertyDescriptor(Obj) */
bool
ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
MutableHandle<JS::PropertyDescriptor> desc);
/*
* Throw a TypeError if desc.getterObject() or setterObject() is not
* callable. This performs exactly the checks omitted by ToPropertyDescriptor
* when checkAccessors is false.
*/
bool
CheckPropertyDescriptorAccessors(JSContext* cx, Handle<JS::PropertyDescriptor> desc);
void
CompletePropertyDescriptor(MutableHandle<JS::PropertyDescriptor> desc);
/*
* 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, MutableHandle<PropertyDescriptorVector> 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. If the value for the name in the looked-up scope is an
* uninitialized lexical, an UninitializedLexicalObject is returned.
*
* Additionally, pobjp is not needed by callers so it is not returned.
*/
extern bool
LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject scopeChain,
MutableHandleObject objp);
} // namespace js
namespace js {
extern JSObject*
FindVariableScope(JSContext* cx, JSFunction** funp);
bool
LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** objp,
Shape** propp);
bool
LookupOwnPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Shape** propp,
bool* isTypedArrayOutOfRange = nullptr);
bool
GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp);
bool
GetGetterPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSFunction** fp);
bool
GetOwnGetterPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSFunction** fp);
bool
GetOwnNativeGetterPure(JSContext* cx, JSObject* obj, jsid id, JSNative* native);
bool
HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result);
bool
GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<JS::PropertyDescriptor> desc);
bool
GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
/*
* Like JS::FromPropertyDescriptor, but ignore desc.object() and always set vp
* to an object on success.
*
* Use JS::FromPropertyDescriptor for getOwnPropertyDescriptor, since desc.object()
* is used to indicate whether a result was found or not. Use this instead for
* defineProperty: it would be senseless to define a "missing" property.
*/
extern bool
FromPropertyDescriptorToObject(JSContext* cx, Handle<JS::PropertyDescriptor> desc,
MutableHandleValue 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);
/* Wrap boolean, number or string as Boolean, Number or String object. */
extern JSObject*
PrimitiveToObject(JSContext* cx, const Value& v);
} /* namespace js */
namespace js {
/* For converting stack values to objects. */
MOZ_ALWAYS_INLINE JSObject*
ToObjectFromStack(JSContext* cx, HandleValue vp)
{
if (vp.isObject())
return &vp.toObject();
return js::ToObjectSlow(cx, vp, true);
}
template<XDRMode mode>
bool
XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj);
/*
* Report a TypeError: "so-and-so is not an object".
* Using NotNullObject is usually less code.
*/
extern void
ReportNotObject(JSContext* cx, const Value& v);
inline JSObject*
NonNullObject(JSContext* cx, const Value& v)
{
if (v.isObject())
return &v.toObject();
ReportNotObject(cx, v);
return nullptr;
}
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);
/*
* ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each
* of obj's own properties' attributes appropriately: each property becomes
* non-configurable, and if level == Frozen, data properties become
* non-writable as well.
*/
extern bool
SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level);
inline bool
FreezeObject(JSContext* cx, HandleObject obj)
{
return SetIntegrityLevel(cx, obj, IntegrityLevel::Frozen);
}
/*
* ES6 rev 29 (6 Dec 2014) 7.3.14. Code shared by Object.isSealed and
* Object.isFrozen.
*/
extern bool
TestIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level, bool* resultp);
extern bool
SpeciesConstructor(JSContext* cx, HandleObject obj, HandleValue defaultCtor, MutableHandleValue pctor);
extern bool
SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey, MutableHandleValue pctor);
extern bool
GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj);
} /* namespace js */
#endif /* jsobj_h */