1000 lines
38 KiB
C++
1000 lines
38 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
|
* 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/. */
|
|
|
|
#include "js/PropertyAndElement.h"
|
|
|
|
#include "mozilla/Assertions.h" // MOZ_ASSERT
|
|
|
|
#include <stddef.h> // size_t
|
|
#include <stdint.h> // uint32_t
|
|
|
|
#include "jsfriendapi.h" // js::GetPropertyKeys, JSITER_OWNONLY
|
|
#include "jstypes.h" // JS_PUBLIC_API
|
|
|
|
#include "js/CallArgs.h" // JSNative
|
|
#include "js/Class.h" // JS::ObjectOpResult
|
|
#include "js/Context.h" // AssertHeapIsIdle
|
|
#include "js/GCVector.h" // JS::GCVector, JS::RootedVector
|
|
#include "js/Id.h" // JS::PropertyKey, jsid
|
|
#include "js/PropertyDescriptor.h" // JS::PropertyDescriptor, JSPROP_READONLY
|
|
#include "js/PropertySpec.h" // JSNativeWrapper
|
|
#include "js/RootingAPI.h" // JS::Rooted, JS::Handle, JS::MutableHandle
|
|
#include "js/Value.h" // JS::Value, JS::*Value
|
|
#include "vm/FunctionPrefixKind.h" // js::FunctionPrefixKind
|
|
#include "vm/GlobalObject.h" // js::GlobalObject
|
|
#include "vm/JSAtomUtils.h" // js::Atomize, js::AtomizeChars
|
|
#include "vm/JSContext.h" // JSContext, CHECK_THREAD
|
|
#include "vm/JSFunction.h" // js::IdToFunctionName, js::DefineFunction
|
|
#include "vm/JSObject.h" // JSObject, js::DefineFunctions
|
|
#include "vm/ObjectOperations.h" // js::DefineProperty, js::DefineDataProperty, js::HasOwnProperty
|
|
#include "vm/PropertyResult.h" // js::PropertyResult
|
|
#include "vm/StringType.h" // JSAtom, js::PropertyName
|
|
|
|
#include "vm/JSAtomUtils-inl.h" // js::AtomToId, js::IndexToId
|
|
#include "vm/JSContext-inl.h" // JSContext::check
|
|
#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance
|
|
#include "vm/NativeObject-inl.h" // js::NativeLookupOwnPropertyNoResolve
|
|
#include "vm/ObjectOperations-inl.h" // js::GetProperty, js::GetElement, js::SetProperty, js::HasProperty, js::DeleteProperty, js::DeleteElement
|
|
|
|
using namespace js;
|
|
|
|
static bool DefinePropertyByDescriptor(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JS::PropertyDescriptor> desc,
|
|
JS::ObjectOpResult& result) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id, desc);
|
|
return js::DefineProperty(cx, obj, id, desc, result);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(
|
|
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
|
JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result) {
|
|
return ::DefinePropertyByDescriptor(cx, obj, id, desc, result);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(
|
|
JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
|
JS::Handle<JS::PropertyDescriptor> desc) {
|
|
JS::ObjectOpResult result;
|
|
return ::DefinePropertyByDescriptor(cx, obj, id, desc, result) &&
|
|
result.checkStrict(cx, obj, id);
|
|
}
|
|
|
|
static bool DefineDataPropertyById(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JS::Value> value,
|
|
unsigned attrs) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id, value);
|
|
|
|
return js::DefineDataProperty(cx, obj, id, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JS::Value> value,
|
|
unsigned attrs) {
|
|
return ::DefineDataPropertyById(cx, obj, id, value, attrs);
|
|
}
|
|
|
|
static bool DefineAccessorPropertyById(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JSObject*> getter,
|
|
JS::Handle<JSObject*> setter,
|
|
unsigned attrs) {
|
|
// JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd
|
|
// throw if this happens, but we've accepted it for long enough that it's
|
|
// not worth trying to make callers change their ways. Just flip it off on
|
|
// its way through the API layer so that we can enforce this internally.
|
|
attrs &= ~JSPROP_READONLY;
|
|
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id, getter, setter);
|
|
|
|
return js::DefineAccessorProperty(cx, obj, id, getter, setter, attrs);
|
|
}
|
|
|
|
static bool DefineAccessorPropertyById(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
const JSNativeWrapper& get,
|
|
const JSNativeWrapper& set,
|
|
unsigned attrs) {
|
|
// Getter/setter are both possibly-null JSNatives. Wrap them in JSFunctions.
|
|
|
|
// Use unprefixed name with LAZY_ACCESSOR_NAME flag, to avoid calculating
|
|
// the accessor name, which is less likely to be used.
|
|
JS::Rooted<JSAtom*> atom(cx, IdToFunctionName(cx, id));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JSFunction*> getter(cx);
|
|
if (get.op) {
|
|
getter = NewNativeFunction(cx, get.op, 0, atom, gc::AllocKind::FUNCTION,
|
|
TenuredObject,
|
|
FunctionFlags::NATIVE_GETTER_WITH_LAZY_NAME);
|
|
if (!getter) {
|
|
return false;
|
|
}
|
|
|
|
if (get.info) {
|
|
getter->setJitInfo(get.info);
|
|
}
|
|
}
|
|
|
|
JS::Rooted<JSFunction*> setter(cx);
|
|
if (set.op) {
|
|
setter = NewNativeFunction(cx, set.op, 1, atom, gc::AllocKind::FUNCTION,
|
|
TenuredObject,
|
|
FunctionFlags::NATIVE_SETTER_WITH_LAZY_NAME);
|
|
if (!setter) {
|
|
return false;
|
|
}
|
|
|
|
if (set.info) {
|
|
setter->setJitInfo(set.info);
|
|
}
|
|
}
|
|
|
|
return ::DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
|
|
}
|
|
|
|
/*
|
|
* Wrapper functions to create wrappers with no corresponding JSJitInfo from API
|
|
* function arguments.
|
|
*/
|
|
static JSNativeWrapper NativeOpWrapper(Native native) {
|
|
JSNativeWrapper ret;
|
|
ret.op = native;
|
|
ret.info = nullptr;
|
|
return ret;
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id, JSNative getter,
|
|
JSNative setter, unsigned attrs) {
|
|
return ::DefineAccessorPropertyById(cx, obj, id, ::NativeOpWrapper(getter),
|
|
::NativeOpWrapper(setter), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JSObject*> getter,
|
|
JS::Handle<JSObject*> setter,
|
|
unsigned attrs) {
|
|
return ::DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JSObject*> valueArg,
|
|
unsigned attrs) {
|
|
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*valueArg));
|
|
return ::DefineDataPropertyById(cx, obj, id, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
HandleString valueArg,
|
|
unsigned attrs) {
|
|
JS::Rooted<JS::Value> value(cx, JS::StringValue(valueArg));
|
|
return ::DefineDataPropertyById(cx, obj, id, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id, int32_t valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::Int32Value(valueArg);
|
|
return ::DefineDataPropertyById(
|
|
cx, obj, id, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id, uint32_t valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::NumberValue(valueArg);
|
|
return ::DefineDataPropertyById(
|
|
cx, obj, id, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefinePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id, double valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::NumberValue(valueArg);
|
|
return ::DefineDataPropertyById(
|
|
cx, obj, id, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
static bool DefineDataProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, JS::Handle<JS::Value> value,
|
|
unsigned attrs) {
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
|
|
return ::DefineDataPropertyById(cx, obj, id, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name,
|
|
JS::Handle<JS::Value> value,
|
|
unsigned attrs) {
|
|
return ::DefineDataProperty(cx, obj, name, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, JSNative getter,
|
|
JSNative setter, unsigned attrs) {
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return ::DefineAccessorPropertyById(cx, obj, id, ::NativeOpWrapper(getter),
|
|
::NativeOpWrapper(setter), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name,
|
|
JS::Handle<JSObject*> getter,
|
|
JS::Handle<JSObject*> setter,
|
|
unsigned attrs) {
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
|
|
return ::DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name,
|
|
JS::Handle<JSObject*> valueArg,
|
|
unsigned attrs) {
|
|
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*valueArg));
|
|
return ::DefineDataProperty(cx, obj, name, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, HandleString valueArg,
|
|
unsigned attrs) {
|
|
JS::Rooted<JS::Value> value(cx, JS::StringValue(valueArg));
|
|
return ::DefineDataProperty(cx, obj, name, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, int32_t valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::Int32Value(valueArg);
|
|
return ::DefineDataProperty(
|
|
cx, obj, name, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, uint32_t valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::NumberValue(valueArg);
|
|
return ::DefineDataProperty(
|
|
cx, obj, name, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, double valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::NumberValue(valueArg);
|
|
return ::DefineDataProperty(
|
|
cx, obj, name, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
#define AUTO_NAMELEN(s, n) (((n) == (size_t)-1) ? js_strlen(s) : (n))
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
JS::Handle<JS::PropertyDescriptor> desc,
|
|
JS::ObjectOpResult& result) {
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return ::DefinePropertyByDescriptor(cx, obj, id, desc, result);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(
|
|
JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
|
|
size_t namelen, JS::Handle<JS::PropertyDescriptor> desc) {
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
JS::ObjectOpResult result;
|
|
return ::DefinePropertyByDescriptor(cx, obj, id, desc, result) &&
|
|
result.checkStrict(cx, obj, id);
|
|
}
|
|
|
|
static bool DefineUCDataProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
JS::Handle<JS::Value> value, unsigned attrs) {
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return ::DefineDataPropertyById(cx, obj, id, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
JS::Handle<JS::Value> value,
|
|
unsigned attrs) {
|
|
return ::DefineUCDataProperty(cx, obj, name, namelen, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
JS::Handle<JSObject*> getter,
|
|
JS::Handle<JSObject*> setter,
|
|
unsigned attrs) {
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return ::DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
JS::Handle<JSObject*> valueArg,
|
|
unsigned attrs) {
|
|
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*valueArg));
|
|
return ::DefineUCDataProperty(cx, obj, name, namelen, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
HandleString valueArg, unsigned attrs) {
|
|
JS::Rooted<JS::Value> value(cx, JS::StringValue(valueArg));
|
|
return ::DefineUCDataProperty(cx, obj, name, namelen, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
int32_t valueArg, unsigned attrs) {
|
|
JS::Value value = JS::Int32Value(valueArg);
|
|
return ::DefineUCDataProperty(
|
|
cx, obj, name, namelen, JS::Handle<JS::Value>::fromMarkedLocation(&value),
|
|
attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
uint32_t valueArg, unsigned attrs) {
|
|
JS::Value value = JS::NumberValue(valueArg);
|
|
return ::DefineUCDataProperty(
|
|
cx, obj, name, namelen, JS::Handle<JS::Value>::fromMarkedLocation(&value),
|
|
attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
double valueArg, unsigned attrs) {
|
|
JS::Value value = JS::NumberValue(valueArg);
|
|
return ::DefineUCDataProperty(
|
|
cx, obj, name, namelen, JS::Handle<JS::Value>::fromMarkedLocation(&value),
|
|
attrs);
|
|
}
|
|
|
|
extern bool PropertySpecNameToId(JSContext* cx, JSPropertySpec::Name name,
|
|
MutableHandleId id);
|
|
|
|
static bool DefineSelfHostedProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
const char* getterName,
|
|
const char* setterName, unsigned attrs) {
|
|
JSAtom* getterNameAtom = Atomize(cx, getterName, strlen(getterName));
|
|
if (!getterNameAtom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<PropertyName*> getterNameName(cx,
|
|
getterNameAtom->asPropertyName());
|
|
|
|
JS::Rooted<JSAtom*> name(cx, IdToFunctionName(cx, id));
|
|
if (!name) {
|
|
return false;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> getterValue(cx);
|
|
if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), getterNameName,
|
|
name, 0, &getterValue)) {
|
|
return false;
|
|
}
|
|
MOZ_ASSERT(getterValue.isObject() && getterValue.toObject().is<JSFunction>());
|
|
JS::Rooted<JSFunction*> getterFunc(cx,
|
|
&getterValue.toObject().as<JSFunction>());
|
|
|
|
JS::Rooted<JSFunction*> setterFunc(cx);
|
|
if (setterName) {
|
|
JSAtom* setterNameAtom = Atomize(cx, setterName, strlen(setterName));
|
|
if (!setterNameAtom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<PropertyName*> setterNameName(cx,
|
|
setterNameAtom->asPropertyName());
|
|
|
|
JS::Rooted<JS::Value> setterValue(cx);
|
|
if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), setterNameName,
|
|
name, 1, &setterValue)) {
|
|
return false;
|
|
}
|
|
MOZ_ASSERT(setterValue.isObject() &&
|
|
setterValue.toObject().is<JSFunction>());
|
|
setterFunc = &setterValue.toObject().as<JSFunction>();
|
|
}
|
|
|
|
return ::DefineAccessorPropertyById(cx, obj, id, getterFunc, setterFunc,
|
|
attrs);
|
|
}
|
|
|
|
static bool DefineDataElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, JS::Handle<JS::Value> value,
|
|
unsigned attrs) {
|
|
cx->check(obj, value);
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
JS::Rooted<jsid> id(cx);
|
|
if (!IndexToId(cx, index, &id)) {
|
|
return false;
|
|
}
|
|
return ::DefineDataPropertyById(cx, obj, id, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, JS::Handle<JS::Value> value,
|
|
unsigned attrs) {
|
|
return ::DefineDataElement(cx, obj, index, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index,
|
|
JS::Handle<JSObject*> getter,
|
|
JS::Handle<JSObject*> setter,
|
|
unsigned attrs) {
|
|
JS::Rooted<jsid> id(cx);
|
|
if (!IndexToId(cx, index, &id)) {
|
|
return false;
|
|
}
|
|
return ::DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index,
|
|
JS::Handle<JSObject*> valueArg,
|
|
unsigned attrs) {
|
|
JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*valueArg));
|
|
return ::DefineDataElement(cx, obj, index, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, HandleString valueArg,
|
|
unsigned attrs) {
|
|
JS::Rooted<JS::Value> value(cx, JS::StringValue(valueArg));
|
|
return ::DefineDataElement(cx, obj, index, value, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, int32_t valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::Int32Value(valueArg);
|
|
return ::DefineDataElement(
|
|
cx, obj, index, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, uint32_t valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::NumberValue(valueArg);
|
|
return ::DefineDataElement(
|
|
cx, obj, index, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, double valueArg,
|
|
unsigned attrs) {
|
|
JS::Value value = JS::NumberValue(valueArg);
|
|
return ::DefineDataElement(
|
|
cx, obj, index, JS::Handle<JS::Value>::fromMarkedLocation(&value), attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_HasPropertyById(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id, bool* foundp) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id);
|
|
|
|
return js::HasProperty(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_HasProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, bool* foundp) {
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_HasPropertyById(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_HasUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
bool* foundp) {
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_HasPropertyById(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_HasElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, bool* foundp) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
JS::Rooted<jsid> id(cx);
|
|
if (!IndexToId(cx, index, &id)) {
|
|
return false;
|
|
}
|
|
return JS_HasPropertyById(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_HasOwnPropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id, bool* foundp) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id);
|
|
|
|
return js::HasOwnProperty(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_HasOwnProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, bool* foundp) {
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_HasOwnPropertyById(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_ForwardGetPropertyTo(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JS::Value> receiver,
|
|
JS::MutableHandle<JS::Value> vp) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id, receiver);
|
|
|
|
return js::GetProperty(cx, obj, receiver, id, vp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_ForwardGetElementTo(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
uint32_t index,
|
|
JS::Handle<JSObject*> receiver,
|
|
JS::MutableHandle<JS::Value> vp) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj);
|
|
|
|
return js::GetElement(cx, obj, receiver, index, vp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_GetPropertyById(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::MutableHandle<JS::Value> vp) {
|
|
JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
|
|
return JS_ForwardGetPropertyTo(cx, obj, id, receiver, vp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name,
|
|
JS::MutableHandle<JS::Value> vp) {
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_GetPropertyById(cx, obj, id, vp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_GetUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
JS::MutableHandle<JS::Value> vp) {
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_GetPropertyById(cx, obj, id, vp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_GetElement(JSContext* cx, JS::Handle<JSObject*> objArg,
|
|
uint32_t index,
|
|
JS::MutableHandle<JS::Value> vp) {
|
|
return JS_ForwardGetElementTo(cx, objArg, index, objArg, vp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_ForwardSetPropertyTo(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JS::Value> v,
|
|
JS::Handle<JS::Value> receiver,
|
|
JS::ObjectOpResult& result) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id, v, receiver);
|
|
|
|
return js::SetProperty(cx, obj, id, v, receiver, result);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetPropertyById(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::Handle<JS::Value> v) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id, v);
|
|
|
|
JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
|
|
JS::ObjectOpResult ignored;
|
|
return js::SetProperty(cx, obj, id, v, receiver, ignored);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name, JS::Handle<JS::Value> v) {
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_SetPropertyById(cx, obj, id, v);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
JS::Handle<JS::Value> v) {
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_SetPropertyById(cx, obj, id, v);
|
|
}
|
|
|
|
static bool SetElement(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t index,
|
|
JS::Handle<JS::Value> v) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, v);
|
|
|
|
JS::Rooted<JS::Value> receiver(cx, JS::ObjectValue(*obj));
|
|
JS::ObjectOpResult ignored;
|
|
return js::SetElement(cx, obj, index, v, receiver, ignored);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, JS::Handle<JS::Value> v) {
|
|
return ::SetElement(cx, obj, index, v);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, JS::Handle<JSObject*> v) {
|
|
JS::Rooted<JS::Value> value(cx, JS::ObjectOrNullValue(v));
|
|
return ::SetElement(cx, obj, index, value);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, HandleString v) {
|
|
JS::Rooted<JS::Value> value(cx, JS::StringValue(v));
|
|
return ::SetElement(cx, obj, index, value);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, int32_t v) {
|
|
JS::Rooted<JS::Value> value(cx, JS::NumberValue(v));
|
|
return ::SetElement(cx, obj, index, value);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, uint32_t v) {
|
|
JS::Rooted<JS::Value> value(cx, JS::NumberValue(v));
|
|
return ::SetElement(cx, obj, index, value);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_SetElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index, double v) {
|
|
JS::Rooted<JS::Value> value(cx, JS::NumberValue(v));
|
|
return ::SetElement(cx, obj, index, value);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DeletePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JS::ObjectOpResult& result) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id);
|
|
|
|
return js::DeleteProperty(cx, obj, id, result);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DeleteProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name,
|
|
JS::ObjectOpResult& result) {
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj);
|
|
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return js::DeleteProperty(cx, obj, id, result);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DeleteUCProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char16_t* name, size_t namelen,
|
|
JS::ObjectOpResult& result) {
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj);
|
|
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return js::DeleteProperty(cx, obj, id, result);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DeleteElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index,
|
|
JS::ObjectOpResult& result) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj);
|
|
|
|
return js::DeleteElement(cx, obj, index, result);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DeletePropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id) {
|
|
JS::ObjectOpResult ignored;
|
|
return JS_DeletePropertyById(cx, obj, id, ignored);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DeleteProperty(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const char* name) {
|
|
JS::ObjectOpResult ignored;
|
|
return JS_DeleteProperty(cx, obj, name, ignored);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DeleteElement(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
uint32_t index) {
|
|
JS::ObjectOpResult ignored;
|
|
return JS_DeleteElement(cx, obj, index, ignored);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_Enumerate(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
JS::MutableHandle<IdVector> props) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, props);
|
|
MOZ_ASSERT(props.empty());
|
|
|
|
JS::RootedVector<JS::PropertyKey> ids(cx);
|
|
if (!js::GetPropertyKeys(cx, obj, JSITER_OWNONLY, &ids)) {
|
|
return false;
|
|
}
|
|
|
|
return props.append(ids.begin(), ids.end());
|
|
}
|
|
|
|
JS_PUBLIC_API JSObject* JS_DefineObject(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
const char* name, const JSClass* clasp,
|
|
unsigned attrs) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj);
|
|
|
|
JS::Rooted<JSObject*> nobj(cx);
|
|
if (!clasp) {
|
|
// Default class is Object.
|
|
nobj = NewPlainObject(cx);
|
|
} else {
|
|
nobj = NewBuiltinClassInstance(cx, clasp);
|
|
}
|
|
if (!nobj) {
|
|
return nullptr;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> nobjValue(cx, JS::ObjectValue(*nobj));
|
|
if (!::DefineDataProperty(cx, obj, name, nobjValue, attrs)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return nobj;
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const JSPropertySpec* ps) {
|
|
JS::Rooted<jsid> id(cx);
|
|
|
|
for (; ps->name; ps++) {
|
|
if (!PropertySpecNameToId(cx, ps->name, &id)) {
|
|
return false;
|
|
}
|
|
|
|
if (ShouldIgnorePropertyDefinition(cx, StandardProtoKeyOrNull(obj), id)) {
|
|
continue;
|
|
}
|
|
|
|
if (ps->isAccessor()) {
|
|
if (ps->isSelfHosted()) {
|
|
if (!::DefineSelfHostedProperty(
|
|
cx, obj, id, ps->u.accessors.getter.selfHosted.funname,
|
|
ps->u.accessors.setter.selfHosted.funname, ps->attributes())) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!::DefineAccessorPropertyById(
|
|
cx, obj, id, ps->u.accessors.getter.native,
|
|
ps->u.accessors.setter.native, ps->attributes())) {
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
JS::Rooted<JS::Value> v(cx);
|
|
if (!ps->getValue(cx, &v)) {
|
|
return false;
|
|
}
|
|
|
|
if (!::DefineDataPropertyById(cx, obj, id, v, ps->attributes())) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_AlreadyHasOwnPropertyById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
bool* foundp) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id);
|
|
|
|
if (!obj->is<NativeObject>()) {
|
|
return js::HasOwnProperty(cx, obj, id, foundp);
|
|
}
|
|
|
|
PropertyResult prop;
|
|
if (!NativeLookupOwnPropertyNoResolve(cx, &obj->as<NativeObject>(), id,
|
|
&prop)) {
|
|
return false;
|
|
}
|
|
*foundp = prop.isFound();
|
|
return true;
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_AlreadyHasOwnProperty(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
const char* name, bool* foundp) {
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_AlreadyHasOwnUCProperty(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
const char16_t* name,
|
|
size_t namelen, bool* foundp) {
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return false;
|
|
}
|
|
JS::Rooted<jsid> id(cx, AtomToId(atom));
|
|
return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_AlreadyHasOwnElement(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
uint32_t index, bool* foundp) {
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
JS::Rooted<jsid> id(cx);
|
|
if (!IndexToId(cx, index, &id)) {
|
|
return false;
|
|
}
|
|
return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
|
|
}
|
|
|
|
JS_PUBLIC_API bool JS_DefineFunctions(JSContext* cx, JS::Handle<JSObject*> obj,
|
|
const JSFunctionSpec* fs) {
|
|
MOZ_ASSERT(!cx->zone()->isAtomsZone());
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj);
|
|
|
|
return js::DefineFunctions(cx, obj, fs);
|
|
}
|
|
|
|
JS_PUBLIC_API JSFunction* JS_DefineFunction(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
const char* name, JSNative call,
|
|
unsigned nargs, unsigned attrs) {
|
|
MOZ_ASSERT(!cx->zone()->isAtomsZone());
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj);
|
|
JSAtom* atom = Atomize(cx, name, strlen(name));
|
|
if (!atom) {
|
|
return nullptr;
|
|
}
|
|
Rooted<jsid> id(cx, AtomToId(atom));
|
|
return js::DefineFunction(cx, obj, id, call, nargs, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API JSFunction* JS_DefineUCFunction(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
const char16_t* name,
|
|
size_t namelen, JSNative call,
|
|
unsigned nargs, unsigned attrs) {
|
|
MOZ_ASSERT(!cx->zone()->isAtomsZone());
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj);
|
|
JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
|
|
if (!atom) {
|
|
return nullptr;
|
|
}
|
|
Rooted<jsid> id(cx, AtomToId(atom));
|
|
return js::DefineFunction(cx, obj, id, call, nargs, attrs);
|
|
}
|
|
|
|
JS_PUBLIC_API JSFunction* JS_DefineFunctionById(JSContext* cx,
|
|
JS::Handle<JSObject*> obj,
|
|
JS::Handle<jsid> id,
|
|
JSNative call, unsigned nargs,
|
|
unsigned attrs) {
|
|
MOZ_ASSERT(!cx->zone()->isAtomsZone());
|
|
AssertHeapIsIdle();
|
|
CHECK_THREAD(cx);
|
|
cx->check(obj, id);
|
|
return js::DefineFunction(cx, obj, id, call, nargs, attrs);
|
|
}
|