#ifndef vm_ObjectOperations_inl_h
#define vm_ObjectOperations_inl_h
#include "vm/ObjectOperations.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Likely.h"
#include <stdint.h>
#include "jsapi.h"
#include "js/Class.h"
#include "js/GCAPI.h"
#include "js/Id.h"
#include "js/RootingAPI.h"
#include "js/Value.h"
#include "proxy/Proxy.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/NativeObject.h"
#include "vm/ProxyObject.h"
#include "vm/StringType.h"
#include "vm/SymbolType.h"
#include "vm/JSAtom-inl.h"
#include "vm/TypeInference-inl.h"
namespace js {
inline bool GetPrototype(JSContext* cx, JS::Handle<JSObject*> obj,
JS::MutableHandle<JSObject*> protop) {
if (obj->hasDynamicPrototype()) {
MOZ_ASSERT(obj->is<ProxyObject>());
return Proxy::getPrototype(cx, obj, protop);
}
protop.set(obj->taggedProto().toObjectOrNull());
return true;
}
inline bool IsExtensible(JSContext* cx, JS::Handle<JSObject*> obj,
bool* extensible) {
if (obj->is<ProxyObject>()) {
MOZ_ASSERT(!cx->helperThread());
return Proxy::isExtensible(cx, obj, extensible);
}
*extensible = obj->nonProxyIsExtensible();
MOZ_ASSERT_IF(obj->isNative() && !*extensible,
obj->as<NativeObject>().getDenseInitializedLength() ==
obj->as<NativeObject>().getDenseCapacity());
return true;
}
inline bool HasProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, bool* foundp) {
if (HasPropertyOp op = obj->getOpsHasProperty()) {
return op(cx, obj, id, foundp);
}
return NativeHasProperty(cx, obj.as<NativeObject>(), id, foundp);
}
inline bool HasProperty(JSContext* cx, JS::Handle<JSObject*> obj,
PropertyName* name, bool* foundp) {
JS::Rooted<jsid> id(cx, NameToId(name));
return HasProperty(cx, obj, id, foundp);
}
inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp) {
if (GetPropertyOp op = obj->getOpsGetProperty()) {
return op(cx, obj, receiver, id, vp);
}
return NativeGetProperty(cx, obj.as<NativeObject>(), receiver, id, vp);
}
inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<JS::Value> receiver, PropertyName* name,
JS::MutableHandle<JS::Value> vp) {
JS::Rooted<jsid> id(cx, NameToId(name));
return GetProperty(cx, obj, receiver, id, vp);
}
inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<JSObject*> receiver, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp) {
JS::Rooted<JS::Value> receiverValue(cx, JS::ObjectValue(*receiver));
return GetProperty(cx, obj, receiverValue, id, vp);
}
inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<JSObject*> receiver, PropertyName* name,
JS::MutableHandle<JS::Value> vp) {
JS::Rooted<JS::Value> receiverValue(cx, JS::ObjectValue(*receiver));
return GetProperty(cx, obj, receiverValue, name, vp);
}
inline bool GetElement(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<JS::Value> receiver, uint32_t index,
JS::MutableHandle<JS::Value> vp) {
JS::Rooted<jsid> id(cx);
if (!IndexToId(cx, index, &id)) {
return false;
}
return GetProperty(cx, obj, receiver, id, vp);
}
inline bool GetElement(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<JSObject*> receiver, uint32_t index,
JS::MutableHandle<JS::Value> vp) {
JS::Rooted<JS::Value> receiverValue(cx, JS::ObjectValue(*receiver));
return GetElement(cx, obj, receiverValue, index, vp);
}
inline bool GetPropertyNoGC(JSContext* cx, JSObject* obj,
const JS::Value& receiver, jsid id, JS::Value* vp) {
if (obj->getOpsGetProperty()) {
return false;
}
return NativeGetPropertyNoGC(cx, &obj->as<NativeObject>(), receiver, id, vp);
}
inline bool GetPropertyNoGC(JSContext* cx, JSObject* obj,
const JS::Value& receiver, PropertyName* name,
JS::Value* vp) {
return GetPropertyNoGC(cx, obj, receiver, NameToId(name), vp);
}
inline bool GetElementNoGC(JSContext* cx, JSObject* obj,
const JS::Value& receiver, uint32_t index,
JS::Value* vp) {
if (obj->getOpsGetProperty()) {
return false;
}
if (index > JSID_INT_MAX) {
return false;
}
return GetPropertyNoGC(cx, obj, receiver, INT_TO_JSID(index), vp);
}
static MOZ_ALWAYS_INLINE bool ClassMayResolveId(const JSAtomState& names,
const Class* clasp, jsid id,
JSObject* maybeObj) {
MOZ_ASSERT_IF(maybeObj, maybeObj->getClass() == clasp);
if (!clasp->getResolve()) {
MOZ_ASSERT(!clasp->getMayResolve(),
"Class with mayResolve hook but no resolve hook");
return false;
}
if (JSMayResolveOp mayResolve = clasp->getMayResolve()) {
JS::AutoSuppressGCAnalysis nogc;
if (!mayResolve(names, id, maybeObj)) {
return false;
}
}
return true;
}
MOZ_ALWAYS_INLINE bool MaybeHasInterestingSymbolProperty(
JSContext* cx, JSObject* obj, JS::Symbol* symbol,
JSObject** holder ) {
MOZ_ASSERT(symbol->isInterestingSymbol());
jsid id = SYMBOL_TO_JSID(symbol);
do {
if (obj->maybeHasInterestingSymbolProperty() ||
MOZ_UNLIKELY(
ClassMayResolveId(cx->names(), obj->getClass(), id, obj))) {
if (holder) {
*holder = obj;
}
return true;
}
obj = obj->staticPrototype();
} while (obj);
return false;
}
MOZ_ALWAYS_INLINE bool GetInterestingSymbolProperty(
JSContext* cx, JS::Handle<JSObject*> obj, JS::Symbol* sym,
JS::MutableHandle<JS::Value> vp) {
JSObject* holder;
if (!MaybeHasInterestingSymbolProperty(cx, obj, sym, &holder)) {
#ifdef DEBUG
JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
JS::Rooted<jsid> id(cx, SYMBOL_TO_JSID(sym));
if (!GetProperty(cx, obj, receiver, id, vp)) {
return false;
}
MOZ_ASSERT(vp.isUndefined());
#endif
vp.setUndefined();
return true;
}
JS::Rooted<JSObject*> holderRoot(cx, holder);
JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
JS::Rooted<jsid> id(cx, SYMBOL_TO_JSID(sym));
return GetProperty(cx, holderRoot, receiver, id, vp);
}
inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, JS::Handle<JS::Value> v,
JS::Handle<JS::Value> receiver,
JS::ObjectOpResult& result) {
if (obj->getOpsSetProperty()) {
return JSObject::nonNativeSetProperty(cx, obj, id, v, receiver, result);
}
return NativeSetProperty<Qualified>(cx, obj.as<NativeObject>(), id, v,
receiver, result);
}
inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, JS::Handle<JS::Value> v) {
JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
JS::ObjectOpResult result;
return SetProperty(cx, obj, id, v, receiver, result) &&
result.checkStrict(cx, obj, id);
}
inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
PropertyName* name, JS::Handle<JS::Value> v,
JS::Handle<JS::Value> receiver,
JS::ObjectOpResult& result) {
JS::Rooted<jsid> id(cx, NameToId(name));
return SetProperty(cx, obj, id, v, receiver, result);
}
inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
PropertyName* name, JS::Handle<JS::Value> v) {
JS::Rooted<jsid> id(cx, NameToId(name));
JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
JS::ObjectOpResult result;
return SetProperty(cx, obj, id, v, receiver, result) &&
result.checkStrict(cx, obj, id);
}
inline bool SetElement(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t index,
JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
JS::ObjectOpResult& result) {
if (obj->getOpsSetProperty()) {
return JSObject::nonNativeSetElement(cx, obj, index, v, receiver, result);
}
return NativeSetElement(cx, obj.as<NativeObject>(), index, v, receiver,
result);
}
inline bool PutProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, JS::Handle<JS::Value> v,
bool strict) {
JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
JS::ObjectOpResult result;
return SetProperty(cx, obj, id, v, receiver, result) &&
result.checkStrictErrorOrWarning(cx, obj, id, strict);
}
inline bool DeleteProperty(JSContext* cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, JS::ObjectOpResult& result) {
MarkTypePropertyNonData(cx, obj, id);
if (DeletePropertyOp op = obj->getOpsDeleteProperty()) {
return op(cx, obj, id, result);
}
return NativeDeleteProperty(cx, obj.as<NativeObject>(), id, result);
}
inline bool DeleteElement(JSContext* cx, JS::Handle<JSObject*> obj,
uint32_t index, JS::ObjectOpResult& result) {
JS::Rooted<jsid> id(cx);
if (!IndexToId(cx, index, &id)) {
return false;
}
return DeleteProperty(cx, obj, id, result);
}
}
#endif