https://github.com/mozilla/gecko-dev
Raw File
Tip revision: 9398ac3b5cf68d0c389f98538eaedfcf3e662809 authored by ffxbld on 06 January 2016, 00:37:12 UTC
Added FIREFOX_43_0_4_RELEASE FIREFOX_43_0_4_BUILD3 tag(s) for changeset 146f494b6a79. DONTBUILD CLOSED TREE a=release
Tip revision: 9398ac3
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/Conversions.h"
#include "js/GCAPI.h"
#include "js/HeapAPI.h"
#include "js/TraceableVector.h"
#include "vm/Shape.h"
#include "vm/String.h"
#include "vm/Xdr.h"

namespace JS {
struct ClassInfo;
} // namespace JS

namespace js {

using PropertyDescriptorVector = TraceableVector<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;

// 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(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);

    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
     *    with a relevant getPrototype() handler.
     * 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(!hasLazyPrototype());
        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<js::TaggedProto> 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>()) {
     *     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() {
        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();
#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 <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.
        if (prev && prev->storeBuffer()) {
            buffer->assertHasCellEdge(static_cast<js::gc::Cell**>(cellp));
            return;
        }
        buffer->putCellFromAnyThread(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->unputCellFromAnyThread(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.
 *
 * 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<PropertyDescriptor> desc);

/* ES6 [[DefineOwnProperty]]. Define a property on obj. */
extern bool
DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
               Handle<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<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 [[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, 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 ***************************************************/

/*
 * 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<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);

/*
 * 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
 * <windows.h> 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 is a WindowProxy, return its current inner Window. Otherwise return
 * obj. This function can't fail and never returns nullptr.
 *
 * 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 function can't fail; it never sets an exception or returns nullptr.
 *
 * 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<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 != 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<js::TaggedProto> 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<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<PropertyDescriptor> desc);

void
CompletePropertyDescriptor(MutableHandle<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
GetPropertyPure(ExclusiveContext* cx, JSObject* obj, jsid id, Value* vp);

bool
GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                         MutableHandle<PropertyDescriptor> 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<PropertyDescriptor> 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<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);

extern bool
ReportGetterOnlyAssignment(JSContext* cx, bool strict);

/*
 * 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 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 */
back to top