#ifndef vm_JSObject_inl_h
#define vm_JSObject_inl_h
#include "vm/JSObject.h"
#include "mozilla/DebugOnly.h"
#include "jsfriendapi.h"
#include "builtin/MapObject.h"
#include "builtin/TypedObject.h"
#include "gc/Allocator.h"
#include "gc/FreeOp.h"
#include "vm/ArrayObject.h"
#include "vm/DateObject.h"
#include "vm/EnvironmentObject.h"
#include "vm/JSFunction.h"
#include "vm/NumberObject.h"
#include "vm/Probes.h"
#include "vm/StringObject.h"
#include "vm/TypedArrayObject.h"
#include "gc/Marking-inl.h"
#include "gc/ObjectKind-inl.h"
#include "vm/JSAtom-inl.h"
#include "vm/ObjectOperations-inl.h"
#include "vm/Realm-inl.h"
#include "vm/ShapedObject-inl.h"
#include "vm/TypeInference-inl.h"
namespace js {
inline bool MaybeConvertUnboxedObjectToNative(JSContext* cx, JSObject* obj) {
if (obj->is<UnboxedPlainObject>()) {
return UnboxedPlainObject::convertToNative(cx, obj);
}
return true;
}
}
inline js::Shape* JSObject::maybeShape() const {
if (!is<js::ShapedObject>()) {
return nullptr;
}
return as<js::ShapedObject>().shape();
}
inline js::Shape* JSObject::ensureShape(JSContext* cx) {
if (!js::MaybeConvertUnboxedObjectToNative(cx, this)) {
return nullptr;
}
js::Shape* shape = maybeShape();
MOZ_ASSERT(shape);
return shape;
}
inline void JSObject::finalize(js::FreeOp* fop) {
js::probes::FinalizeObject(this);
#ifdef DEBUG
MOZ_ASSERT(isTenured());
if (!IsBackgroundFinalized(asTenured().getAllocKind())) {
MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
}
#endif
const js::Class* clasp = getClass();
js::NativeObject* nobj = nullptr;
if (clasp->isNative()) {
nobj = &as<js::NativeObject>();
}
if (clasp->hasFinalize()) {
clasp->doFinalize(fop, this);
}
if (!nobj) {
return;
}
if (nobj->hasDynamicSlots()) {
fop->free_(nobj->slots_);
}
if (nobj->hasDynamicElements()) {
js::ObjectElements* elements = nobj->getElementsHeader();
if (elements->isCopyOnWrite()) {
if (elements->ownerObject() == this) {
MOZ_ASSERT(elements->numShiftedElements() == 0);
fop->freeLater(elements);
}
} else {
fop->free_(nobj->getUnshiftedElementsHeader());
}
}
nobj->sweepDictionaryListPointer();
}
MOZ_ALWAYS_INLINE void js::NativeObject::sweepDictionaryListPointer() {
if (shape()->listp == shapePtr()) {
shape()->listp = nullptr;
}
}
MOZ_ALWAYS_INLINE void
js::NativeObject::updateDictionaryListPointerAfterMinorGC(NativeObject* old) {
MOZ_ASSERT(this == Forwarded(old));
if (shape()->listp == old->shapePtr()) {
shape()->listp = shapePtr();
}
}
inline bool JSObject::setSingleton(JSContext* cx,
js::HandleObject obj) {
MOZ_ASSERT(!IsInsideNursery(obj));
MOZ_ASSERT(!obj->isSingleton());
js::ObjectGroup* group = js::ObjectGroup::lazySingletonGroup(
cx, obj->group_, obj->getClass(), obj->taggedProto());
if (!group) {
return false;
}
obj->group_ = group;
return true;
}
inline js::ObjectGroup* JSObject::getGroup(JSContext* cx,
js::HandleObject obj) {
MOZ_ASSERT(cx->compartment() == obj->compartment());
if (obj->hasLazyGroup()) {
if (cx->compartment() != obj->compartment()) {
MOZ_CRASH();
}
return makeLazyGroup(cx, obj);
}
return obj->group_;
}
inline void JSObject::setGroup(js::ObjectGroup* group) {
MOZ_RELEASE_ASSERT(group);
MOZ_ASSERT(!isSingleton());
MOZ_ASSERT(maybeCCWRealm() == group->realm());
group_ = group;
}
inline bool JSObject::isQualifiedVarObj() const {
if (is<js::DebugEnvironmentProxy>()) {
return as<js::DebugEnvironmentProxy>().environment().isQualifiedVarObj();
}
bool rv = hasAllFlags(js::BaseShape::QUALIFIED_VAROBJ);
MOZ_ASSERT_IF(rv, is<js::GlobalObject>() || is<js::CallObject>() ||
is<js::VarEnvironmentObject>() ||
is<js::ModuleEnvironmentObject>() ||
is<js::NonSyntacticVariablesObject>() ||
(is<js::WithEnvironmentObject>() &&
!as<js::WithEnvironmentObject>().isSyntactic()));
return rv;
}
inline bool JSObject::isUnqualifiedVarObj() const {
if (is<js::DebugEnvironmentProxy>()) {
return as<js::DebugEnvironmentProxy>().environment().isUnqualifiedVarObj();
}
return is<js::GlobalObject>() || is<js::NonSyntacticVariablesObject>();
}
namespace js {
inline bool ClassCanHaveFixedData(const Class* clasp) {
return !clasp->isNative() || clasp == &js::ArrayBufferObject::class_ ||
js::IsTypedArrayClass(clasp);
}
template <typename T>
static MOZ_ALWAYS_INLINE MOZ_MUST_USE T* SetNewObjectMetadata(JSContext* cx,
T* obj) {
MOZ_ASSERT(!cx->realm()->hasObjectPendingMetadata());
if (!cx->helperThread()) {
if (MOZ_UNLIKELY(cx->realm()->hasAllocationMetadataBuilder()) &&
!cx->zone()->suppressAllocationMetadataBuilder) {
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
Rooted<T*> rooted(cx, obj);
cx->realm()->setNewObjectMetadata(cx, rooted);
return rooted;
}
}
return obj;
}
}
inline js::GlobalObject& JSObject::nonCCWGlobal() const {
return *nonCCWRealm()->unsafeUnbarrieredMaybeGlobal();
}
inline bool JSObject::hasAllFlags(js::BaseShape::Flag flags) const {
MOZ_ASSERT(flags);
if (js::Shape* shape = maybeShape()) {
return shape->hasAllObjectFlags(flags);
}
return false;
}
inline bool JSObject::nonProxyIsExtensible() const {
MOZ_ASSERT(!uninlinedIsProxy());
return !hasAllFlags(js::BaseShape::NOT_EXTENSIBLE);
}
inline bool JSObject::isBoundFunction() const {
return is<JSFunction>() && as<JSFunction>().isBoundFunction();
}
inline bool JSObject::isDelegate() const {
return hasAllFlags(js::BaseShape::DELEGATE);
}
inline bool JSObject::hasUncacheableProto() const {
return hasAllFlags(js::BaseShape::UNCACHEABLE_PROTO);
}
MOZ_ALWAYS_INLINE bool JSObject::maybeHasInterestingSymbolProperty() const {
const js::NativeObject* nobj;
if (isNative()) {
nobj = &as<js::NativeObject>();
} else if (is<js::UnboxedPlainObject>()) {
nobj = as<js::UnboxedPlainObject>().maybeExpando();
if (!nobj) {
return false;
}
} else {
return true;
}
return nobj->hasInterestingSymbol();
}
inline bool JSObject::staticPrototypeIsImmutable() const {
MOZ_ASSERT(hasStaticPrototype());
return hasAllFlags(js::BaseShape::IMMUTABLE_PROTOTYPE);
}
inline bool JSObject::isIteratedSingleton() const {
return hasAllFlags(js::BaseShape::ITERATED_SINGLETON);
}
inline bool JSObject::isNewGroupUnknown() const {
return hasAllFlags(js::BaseShape::NEW_GROUP_UNKNOWN);
}
namespace js {
static MOZ_ALWAYS_INLINE bool IsFunctionObject(const js::Value& v) {
return v.isObject() && v.toObject().is<JSFunction>();
}
static MOZ_ALWAYS_INLINE bool IsFunctionObject(const js::Value& v,
JSFunction** fun) {
if (v.isObject() && v.toObject().is<JSFunction>()) {
*fun = &v.toObject().as<JSFunction>();
return true;
}
return false;
}
static MOZ_ALWAYS_INLINE bool IsNativeFunction(const js::Value& v) {
JSFunction* fun;
return IsFunctionObject(v, &fun) && fun->isNative();
}
static MOZ_ALWAYS_INLINE bool IsNativeFunction(const js::Value& v,
JSFunction** fun) {
return IsFunctionObject(v, fun) && (*fun)->isNative();
}
static MOZ_ALWAYS_INLINE bool IsNativeFunction(const js::Value& v,
JSNative native) {
JSFunction* fun;
return IsFunctionObject(v, &fun) && fun->maybeNative() == native;
}
static MOZ_ALWAYS_INLINE bool IsNativeFunction(const JSObject* obj,
JSNative native) {
return obj->is<JSFunction>() && obj->as<JSFunction>().maybeNative() == native;
}
static MOZ_ALWAYS_INLINE bool HasNativeMethodPure(JSObject* obj,
PropertyName* name,
JSNative native,
JSContext* cx) {
Value v;
if (!GetPropertyPure(cx, obj, NameToId(name), &v)) {
return false;
}
return IsNativeFunction(v, native);
}
static MOZ_ALWAYS_INLINE bool HasNoToPrimitiveMethodPure(JSObject* obj,
JSContext* cx) {
Symbol* toPrimitive = cx->wellKnownSymbols().toPrimitive;
JSObject* holder;
if (!MaybeHasInterestingSymbolProperty(cx, obj, toPrimitive, &holder)) {
#ifdef DEBUG
JSObject* pobj;
PropertyResult prop;
MOZ_ASSERT(
LookupPropertyPure(cx, obj, SYMBOL_TO_JSID(toPrimitive), &pobj, &prop));
MOZ_ASSERT(!prop);
#endif
return true;
}
JSObject* pobj;
PropertyResult prop;
if (!LookupPropertyPure(cx, holder, SYMBOL_TO_JSID(toPrimitive), &pobj,
&prop)) {
return false;
}
return !prop;
}
extern bool ToPropertyKeySlow(JSContext* cx, HandleValue argument,
MutableHandleId result);
MOZ_ALWAYS_INLINE bool ToPropertyKey(JSContext* cx, HandleValue argument,
MutableHandleId result) {
if (MOZ_LIKELY(argument.isPrimitive())) {
return ValueToId<CanGC>(cx, argument, result);
}
return ToPropertyKeySlow(cx, argument, result);
}
inline bool IsInternalFunctionObject(JSObject& funobj) {
JSFunction& fun = funobj.as<JSFunction>();
return fun.isInterpreted() && !fun.environment();
}
JSObject* NewObjectWithGivenTaggedProto(JSContext* cx, const Class* clasp,
Handle<TaggedProto> proto,
gc::AllocKind allocKind,
NewObjectKind newKind,
uint32_t initialShapeFlags = 0);
inline JSObject* NewObjectWithGivenTaggedProto(
JSContext* cx, const Class* clasp, Handle<TaggedProto> proto,
NewObjectKind newKind = GenericObject, uint32_t initialShapeFlags = 0) {
gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
return NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind, newKind,
initialShapeFlags);
}
template <typename T>
inline T* NewObjectWithGivenTaggedProto(JSContext* cx,
Handle<TaggedProto> proto,
NewObjectKind newKind = GenericObject,
uint32_t initialShapeFlags = 0) {
JSObject* obj = NewObjectWithGivenTaggedProto(cx, &T::class_, proto, newKind,
initialShapeFlags);
return obj ? &obj->as<T>() : nullptr;
}
template <typename T>
inline T* NewObjectWithGivenTaggedProto(JSContext* cx,
Handle<TaggedProto> proto,
gc::AllocKind allocKind,
NewObjectKind newKind = GenericObject,
uint32_t initialShapeFlags = 0) {
JSObject* obj = NewObjectWithGivenTaggedProto(
cx, &T::class_, proto, allocKind, newKind, initialShapeFlags);
return obj ? &obj->as<T>() : nullptr;
}
template <typename T>
inline T* NewObjectWithNullTaggedProto(JSContext* cx,
NewObjectKind newKind = GenericObject,
uint32_t initialShapeFlags = 0) {
Handle<TaggedProto> nullProto = AsTaggedProto(nullptr);
return NewObjectWithGivenTaggedProto<T>(cx, nullProto, newKind,
initialShapeFlags);
}
inline JSObject* NewObjectWithGivenProto(
JSContext* cx, const Class* clasp, HandleObject proto,
gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) {
return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(proto),
allocKind, newKind);
}
inline JSObject* NewObjectWithGivenProto(
JSContext* cx, const Class* clasp, HandleObject proto,
NewObjectKind newKind = GenericObject) {
return NewObjectWithGivenTaggedProto(cx, clasp, AsTaggedProto(proto),
newKind);
}
template <typename T>
inline T* NewObjectWithGivenProto(JSContext* cx, HandleObject proto,
NewObjectKind newKind = GenericObject) {
return NewObjectWithGivenTaggedProto<T>(cx, AsTaggedProto(proto), newKind);
}
template <typename T>
inline T* NewObjectWithGivenProto(JSContext* cx, HandleObject proto,
gc::AllocKind allocKind,
NewObjectKind newKind = GenericObject) {
JSObject* obj = NewObjectWithGivenTaggedProto(
cx, &T::class_, AsTaggedProto(proto), allocKind, newKind);
return obj ? &obj->as<T>() : nullptr;
}
JSObject* NewObjectWithClassProtoCommon(JSContext* cx, const Class* clasp,
HandleObject proto,
gc::AllocKind allocKind,
NewObjectKind newKind);
inline JSObject* NewObjectWithClassProto(
JSContext* cx, const Class* clasp, HandleObject proto,
gc::AllocKind allocKind, NewObjectKind newKind = GenericObject) {
return NewObjectWithClassProtoCommon(cx, clasp, proto, allocKind, newKind);
}
inline JSObject* NewObjectWithClassProto(
JSContext* cx, const Class* clasp, HandleObject proto,
NewObjectKind newKind = GenericObject) {
gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
return NewObjectWithClassProto(cx, clasp, proto, allocKind, newKind);
}
template <class T>
inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto,
NewObjectKind newKind = GenericObject) {
JSObject* obj = NewObjectWithClassProto(cx, &T::class_, proto, newKind);
return obj ? &obj->as<T>() : nullptr;
}
template <class T>
inline T* NewObjectWithClassProto(JSContext* cx, HandleObject proto,
gc::AllocKind allocKind,
NewObjectKind newKind = GenericObject) {
JSObject* obj =
NewObjectWithClassProto(cx, &T::class_, proto, allocKind, newKind);
return obj ? &obj->as<T>() : nullptr;
}
inline JSObject* NewBuiltinClassInstance(
JSContext* cx, const Class* clasp, gc::AllocKind allocKind,
NewObjectKind newKind = GenericObject) {
return NewObjectWithClassProto(cx, clasp, nullptr, allocKind, newKind);
}
inline JSObject* NewBuiltinClassInstance(
JSContext* cx, const Class* clasp, NewObjectKind newKind = GenericObject) {
gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
return NewBuiltinClassInstance(cx, clasp, allocKind, newKind);
}
template <typename T>
inline T* NewBuiltinClassInstance(JSContext* cx,
NewObjectKind newKind = GenericObject) {
JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, newKind);
return obj ? &obj->as<T>() : nullptr;
}
template <typename T>
inline T* NewBuiltinClassInstance(JSContext* cx, gc::AllocKind allocKind,
NewObjectKind newKind = GenericObject) {
JSObject* obj = NewBuiltinClassInstance(cx, &T::class_, allocKind, newKind);
return obj ? &obj->as<T>() : nullptr;
}
bool NewObjectScriptedCall(JSContext* cx, MutableHandleObject obj);
JSObject* NewObjectWithGroupCommon(JSContext* cx, HandleObjectGroup group,
gc::AllocKind allocKind,
NewObjectKind newKind);
template <typename T>
inline T* NewObjectWithGroup(JSContext* cx, HandleObjectGroup group,
gc::AllocKind allocKind,
NewObjectKind newKind = GenericObject) {
JSObject* obj = NewObjectWithGroupCommon(cx, group, allocKind, newKind);
return obj ? &obj->as<T>() : nullptr;
}
template <typename T>
inline T* NewObjectWithGroup(JSContext* cx, HandleObjectGroup group,
NewObjectKind newKind = GenericObject) {
gc::AllocKind allocKind = gc::GetGCObjectKind(group->clasp());
return NewObjectWithGroup<T>(cx, group, allocKind, newKind);
}
static inline gc::AllocKind GuessObjectGCKind(size_t numElements) {
if (numElements) {
return gc::GetGCObjectKind(numElements);
}
return gc::AllocKind::OBJECT4;
}
static inline gc::AllocKind GuessArrayGCKind(size_t numElements) {
if (numElements) {
return gc::GetGCArrayKind(numElements);
}
return gc::AllocKind::OBJECT8;
}
inline bool GetClassOfValue(JSContext* cx, HandleValue v, ESClass* cls) {
if (!v.isObject()) {
*cls = ESClass::Other;
return true;
}
RootedObject obj(cx, &v.toObject());
return GetBuiltinClass(cx, obj, cls);
}
extern NativeObject* InitClass(JSContext* cx, HandleObject obj,
HandleObject parent_proto, const Class* clasp,
JSNative constructor, unsigned nargs,
const JSPropertySpec* ps,
const JSFunctionSpec* fs,
const JSPropertySpec* static_ps,
const JSFunctionSpec* static_fs,
NativeObject** ctorp = nullptr);
MOZ_ALWAYS_INLINE const char* GetObjectClassName(JSContext* cx,
HandleObject obj) {
if (obj->is<ProxyObject>()) {
return Proxy::className(cx, obj);
}
return obj->getClass()->name;
}
inline bool IsCallable(const Value& v) {
return v.isObject() && v.toObject().isCallable();
}
inline bool IsConstructor(const Value& v) {
return v.isObject() && v.toObject().isConstructor();
}
MOZ_ALWAYS_INLINE bool CreateThis(JSContext* cx, HandleFunction callee,
JSScript* calleeScript,
HandleObject newTarget, NewObjectKind newKind,
MutableHandleValue thisv) {
if (callee->isBoundFunction()) {
thisv.setMagic(JS_UNINITIALIZED_LEXICAL);
return true;
}
if (calleeScript->isDerivedClassConstructor()) {
MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor());
thisv.setMagic(JS_UNINITIALIZED_LEXICAL);
return true;
}
MOZ_ASSERT(thisv.isMagic(JS_IS_CONSTRUCTING));
JSObject* obj = CreateThisForFunction(cx, callee, newTarget, newKind);
if (!obj) {
return false;
}
MOZ_ASSERT(obj->nonCCWRealm() == callee->realm());
thisv.setObject(*obj);
return true;
}
}
MOZ_ALWAYS_INLINE bool JSObject::isCallable() const {
if (is<JSFunction>()) {
return true;
}
if (is<js::ProxyObject>()) {
const js::ProxyObject& p = as<js::ProxyObject>();
return p.handler()->isCallable(const_cast<JSObject*>(this));
}
return callHook() != nullptr;
}
MOZ_ALWAYS_INLINE bool JSObject::isConstructor() const {
if (is<JSFunction>()) {
const JSFunction& fun = as<JSFunction>();
return fun.isConstructor();
}
if (is<js::ProxyObject>()) {
const js::ProxyObject& p = as<js::ProxyObject>();
return p.handler()->isConstructor(const_cast<JSObject*>(this));
}
return constructHook() != nullptr;
}
MOZ_ALWAYS_INLINE JSNative JSObject::callHook() const {
return getClass()->getCall();
}
MOZ_ALWAYS_INLINE JSNative JSObject::constructHook() const {
return getClass()->getConstruct();
}
#endif