/* -*- 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/Conversions.h" #include "js/GCAPI.h" #include "js/HeapAPI.h" #include "vm/Shape.h" #include "vm/String.h" #include "vm/Xdr.h" namespace JS { struct ClassInfo; } namespace js { typedef AutoVectorRooter AutoPropertyDescriptorVector; class GCMarker; class Nursery; namespace gc { class RelocationOverlay; } 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; // Forward declarations, required for later friend declarations. bool PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result); 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::HeapPtrObjectGroup 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); 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; } const js::ObjectOps* getOps() const { return &getClass()->ops; } 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 shape of an object. This pointer is valid for native objects and // some non-native objects. After creating an object, the objects for which // the shape pointer is invalid need to overwrite this pointer before a GC // can occur. inline void setInitialShapeMaybeNonNative(js::Shape* shape); inline void setShapeMaybeNonNative(js::Shape* shape); // 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 }; bool setFlags(js::ExclusiveContext* cx, 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; bool setDelegate(js::ExclusiveContext* cx) { return setFlags(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE); } inline bool isBoundFunction() const; inline bool hasSpecialEquality() const; inline bool watched() const; bool setWatched(js::ExclusiveContext* cx) { return setFlags(cx, js::BaseShape::WATCHED, GENERATE_SHAPE); } // 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; bool setQualifiedVarObj(js::ExclusiveContext* cx) { return setFlags(cx, 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. Property cache entries * and JIT inline caches should not be filled for lookups across prototype * lookups on the object. */ inline bool hasUncacheableProto() const; bool setUncacheableProto(js::ExclusiveContext* cx) { return setFlags(cx, 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; bool setHadElementsAccess(js::ExclusiveContext* cx) { return setFlags(cx, 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. */ bool constructorDisplayAtom(JSContext* cx, 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 js::ThingRootKind rootKind() { return js::THING_ROOT_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(JSObject* obj, void* cellp); static MOZ_ALWAYS_INLINE void writeBarrierPostRelocate(JSObject* obj, void* cellp); static MOZ_ALWAYS_INLINE void writeBarrierPostRemove(JSObject* obj, void* cellp); /* 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); inline js::ObjectGroup* getGroup(JSContext* cx); const js::HeapPtrObjectGroup& groupFromGC() const { /* Direct field access for use by GC. */ return group_; } /* * 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 group. 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. js::GetPrototype(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. */ js::TaggedProto getTaggedProto() const { return group_->proto(); } bool hasTenuredProto() const; bool uninlinedIsProxy() const; JSObject* getProto() const { MOZ_ASSERT(!uninlinedIsProxy()); return getTaggedProto().toObjectOrNull(); } // Normal objects and a subset of proxies have uninteresting [[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->type_|. // // Proxies that don't have such a simple [[Prototype]] instead have a // "lazy" [[Prototype]]. Accessing the [[Prototype]] of such an object // requires going through the proxy handler {get,set}Prototype and // setImmutablePrototype methods. This is most commonly useful for proxies // that are wrappers around other objects. If the [[Prototype]] of the // underlying object changes, the [[Prototype]] of the wrapper must also // simultaneously change. We implement this by having the handler methods // simply delegate to the wrapped object, forwarding its response to the // caller. // // This method returns true if this object has a non-simple [[Prototype]] // as described above, or false otherwise. bool hasLazyPrototype() const { bool lazy = getTaggedProto().isLazy(); MOZ_ASSERT_IF(lazy, uninlinedIsProxy()); return lazy; } // True iff this object's [[Prototype]] is immutable. Must not be called // on proxies with lazy [[Prototype]]! inline bool nonLazyPrototypeIsImmutable() 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; bool setIteratedSingleton(js::ExclusiveContext* cx) { return setFlags(cx, 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; bool setNewScriptCleared(js::ExclusiveContext* cx) { return setFlags(cx, js::BaseShape::NEW_SCRIPT_CLEARED); } /* Set a new prototype for an object with a singleton type. */ bool splicePrototype(JSContext* cx, const js::Class* clasp, js::Handle proto); /* * For bootstrapping, whether to splice a prototype for Function.prototype * or the global object. */ bool shouldSplicePrototype(JSContext* cx); /* * Scope chains. * * 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, Block, and With --- the chain is stored in the first fixed * slot of the object. For other scope objects, the chain goes directly to * the global. * * In code which is not marked hasNonSyntacticScope, scope chains can * contain only syntactic scope objects (see IsSyntacticScope) with a global * object at the root as the scope of the outermost non-function script. In * hasNonSyntacticScope 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. */ /* * 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(); inline js::GlobalObject& global() const; inline bool isOwnGlobal() 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); bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR); bool reportNotExtensible(JSContext* 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); 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& x = obj.as(); * x.foo(); * } * * These XObject classes form a hierarchy. For example, for a cloned block * object, the following predicates are true: is, * is, is and is. Each of * these has a respective class that derives and adds operations. * * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file * triplet (along with any class YObject that derives XObject). * * Note that X represents a low-level representation and does not query the * [[Class]] property of object defined by the spec (for this, see * js::ObjectClassIs). */ template inline bool is() const { return getClass() == &T::class_; } template T& as() { MOZ_ASSERT(this->is()); return *static_cast(this); } template const T& as() const { MOZ_ASSERT(this->is()); return *static_cast(this); } #ifdef DEBUG void dump(); #endif /* JIT Accessors */ static size_t offsetOfGroup() { return offsetof(JSObject, group_); } static size_t offsetOfShape() { return sizeof(JSObject); } // 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 MOZ_ALWAYS_INLINE JS::Handle js::RootedBase::as() const { const JS::Rooted& self = *static_cast*>(this); MOZ_ASSERT(self->is()); return Handle::fromMarkedLocation(reinterpret_cast(self.address())); } template MOZ_ALWAYS_INLINE JS::Handle js::HandleBase::as() const { const JS::Handle& self = *static_cast*>(this); MOZ_ASSERT(self->is()); return Handle::fromMarkedLocation(reinterpret_cast(self.address())); } /* * The only sensible way to compare JSObject with == is by identity. We use * const& instead of * as a syntactic way to assert non-null. This leads to an * abundance of address-of operators to identity. Hence this overload. */ static MOZ_ALWAYS_INLINE bool operator==(const JSObject& lhs, const JSObject& rhs) { return &lhs == &rhs; } static MOZ_ALWAYS_INLINE bool operator!=(const JSObject& lhs, const JSObject& rhs) { return &lhs != &rhs; } // 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) { if (!isNullLike(obj) && obj->isTenured()) obj->asTenured().readBarrier(&obj->asTenured()); } /* static */ MOZ_ALWAYS_INLINE void JSObject::writeBarrierPre(JSObject* obj) { if (!isNullLike(obj) && obj->isTenured()) obj->asTenured().writeBarrierPre(&obj->asTenured()); } /* static */ MOZ_ALWAYS_INLINE void JSObject::writeBarrierPost(JSObject* obj, void* cellp) { MOZ_ASSERT(cellp); if (IsNullTaggedPointer(obj)) return; MOZ_ASSERT(obj == *static_cast(cellp)); js::gc::StoreBuffer* storeBuffer = obj->storeBuffer(); if (storeBuffer) storeBuffer->putCellFromAnyThread(static_cast(cellp)); } /* static */ MOZ_ALWAYS_INLINE void JSObject::writeBarrierPostRelocate(JSObject* obj, void* cellp) { MOZ_ASSERT(cellp); MOZ_ASSERT(obj); MOZ_ASSERT(obj == *static_cast(cellp)); js::gc::StoreBuffer* storeBuffer = obj->storeBuffer(); if (storeBuffer) storeBuffer->putRelocatableCellFromAnyThread(static_cast(cellp)); } /* static */ MOZ_ALWAYS_INLINE void JSObject::writeBarrierPostRemove(JSObject* obj, void* cellp) { MOZ_ASSERT(cellp); MOZ_ASSERT(obj); MOZ_ASSERT(obj == *static_cast(cellp)); obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCellFromAnyThread( static_cast(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 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 { /*** Standard internal methods ******************************************************************** * * The functions below are the fundamental operations on objects. * * ES6 specifies 14 internal methods that define how objects behave. The spec * is actually quite good on this topic, though you may have to read it a few * times. See ES6 draft rev 29 (6 Dec 2014) 6.1.7.2 and 6.1.7.3. * * When 'obj' is an ordinary object, these functions have boring standard * behavior as specified by ES6 draft rev 29 section 9.1; see the section about * internal methods in vm/NativeObject.h. * * Proxies override the behavior of internal methods. So when 'obj' is a proxy, * any one of the functions below could do just about anything. See js/Proxy.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. */ extern bool PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result); /* Convenience function. As above, but throw on failure. */ extern bool PreventExtensions(JSContext* cx, HandleObject obj); /* * ES6 [[GetOwnPropertyDescriptor]]. 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 desc); /* ES6 [[DefineOwnProperty]]. Define a property on obj. */ extern bool DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle 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 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 [[HasProperty]]. 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, HandleObject receiver, HandleId id, MutableHandleValue vp); inline bool GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, PropertyName* name, MutableHandleValue vp) { RootedId id(cx, NameToId(name)); return GetProperty(cx, obj, receiver, id, vp); } inline bool GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index, MutableHandleValue vp); inline bool GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, jsid id, Value* vp); inline bool GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, PropertyName* name, Value* vp) { return GetPropertyNoGC(cx, obj, receiver, NameToId(name), 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 ***************************************************/ /* * 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 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); /* * Set a watchpoint: a synchronous callback when the given property of the * given object is set. * * Watchpoints are nonstandard and do not fit in well with the way ES6 * specifies [[Set]]. They are also insufficient for implementing * Object.observe. */ extern bool WatchProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable); /* Clear a watchpoint. */ extern bool UnwatchProperty(JSContext* cx, HandleObject obj, HandleId id); /* * ToPrimitive support, currently implemented like an internal method (JSClass::convert). * In ES6 this is just a method, @@toPrimitive. See bug 1054756. */ extern bool ToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp); MOZ_ALWAYS_INLINE bool ToPrimitive(JSContext* cx, MutableHandleValue vp); MOZ_ALWAYS_INLINE bool ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp); /* * toString support. (This isn't called GetClassName because there's a macro in * with that name.) */ extern const char* GetObjectClassName(JSContext* cx, HandleObject obj); /* * Inner and outer objects * * GetInnerObject and GetOuterObject (and also GetThisObject, somewhat) have to * do with Windows and WindowProxies. There's a screwy invariant that actual * Window objects (the global objects of web pages) are never directly exposed * to script. Instead we often substitute a WindowProxy. * * As a result, we have calls to these three "substitute-this-object-for-that- * object" functions sprinkled at apparently arbitrary (but actually *very* * carefully and nervously selected) places throughout the engine and indeed * the universe. */ /* * If obj a WindowProxy, return its current inner Window. Otherwise return obj. * * GetInnerObject is called when we need a scope chain; you never want a * WindowProxy on a scope chain. * * It's also called in a few places where an object comes in from script, and * the user probably intends to operate on the Window, not the * WindowProxy. Object.prototype.watch and various Debugger features do * this. (Users can't simply pass the Window, because the Window isn't exposed * to scripts.) */ inline JSObject* GetInnerObject(JSObject* obj) { if (InnerObjectOp op = obj->getClass()->ext.innerObject) { JS::AutoSuppressGCAnalysis nogc; return op(obj); } return obj; } /* * If obj is a Window object, return the WindowProxy. Otherwise return obj. * * This must be called before passing an object to script, if the object might * be a Window. (But usually those cases involve scope objects, and for those, * it is better to call GetThisObject instead.) */ inline JSObject* GetOuterObject(JSContext* cx, HandleObject obj) { if (ObjectOp op = obj->getClass()->ext.outerObject) return op(cx, obj); return 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) DynamicWithObjects 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 GetThisObject where appropriate. * * See comments at ComputeImplicitThis. */ inline JSObject* GetThisObject(JSContext* cx, HandleObject obj) { if (ObjectOp op = obj->getOps()->thisObject) return op(cx, obj); return 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 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 != GenericObject) return gc::TenuredHeap; if (clasp->finalize && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE)) 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, HandleObject proto, NewObjectKind newKind = GenericObject); // Specialized call for constructing |this| with a known function callee. extern JSObject* CreateThisForFunction(JSContext* cx, js::HandleObject callee, NewObjectKind newKind); // Generic call for constructing |this|. extern JSObject* CreateThis(JSContext* cx, const js::Class* clasp, js::HandleObject callee); extern JSObject* CloneObject(JSContext* cx, HandleObject obj, Handle proto); extern JSObject* DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKind = GenericObject); extern bool DefineProperties(JSContext* cx, HandleObject obj, HandleObject props); 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 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 desc); void CompletePropertyDescriptor(MutableHandle 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, AutoPropertyDescriptorVector* 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 { extern JSObject* FindVariableScope(JSContext* cx, JSFunction** funp); bool LookupPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, JSObject** objp, Shape** propp); bool GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp); bool GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandle desc); bool GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp); /* * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc). * * If desc.object() is null, then vp is set to undefined. */ extern bool FromPropertyDescriptor(JSContext* cx, Handle desc, MutableHandleValue vp); /* * Like FromPropertyDescriptor, but ignore desc.object() and always set vp * to an object on success. * * Use 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 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 bool XDRObjectLiteral(XDRState* xdr, MutableHandleObject obj); extern bool ReportGetterOnlyAssignment(JSContext* cx, bool strict); 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); enum class IntegrityLevel { Sealed, Frozen }; /* * 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); } /* namespace js */ #endif /* jsobj_h */