gecko-dev/js/public/PropertyDescriptor.h

384 строки
13 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
/* Property descriptors and flags. */
#ifndef js_PropertyDescriptor_h
#define js_PropertyDescriptor_h
#include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF
#include <stdint.h> // uint8_t
#include "jstypes.h" // JS_PUBLIC_API
#include "js/Class.h" // JS{Getter,Setter}Op
#include "js/RootingAPI.h" // JS::Handle, js::{,Mutable}WrappedPtrOperations
#include "js/Value.h" // JS::Value
struct JS_PUBLIC_API JSContext;
class JS_PUBLIC_API JSObject;
class JS_PUBLIC_API JSTracer;
/* Property attributes, set in JSPropertySpec and passed to API functions.
*
* The data structure in which some of these values are stored only uses a
* uint8_t to store the relevant information. Proceed with caution if trying to
* reorder or change the the first byte worth of flags.
*/
/** The property is visible in for/in loops. */
static constexpr uint8_t JSPROP_ENUMERATE = 0x01;
/**
* The property is non-writable. This flag is only valid when neither
* JSPROP_GETTER nor JSPROP_SETTER is set.
*/
static constexpr uint8_t JSPROP_READONLY = 0x02;
/**
* The property is non-configurable: it can't be deleted, and if it's an
* accessor descriptor, its getter and setter can't be changed.
*/
static constexpr uint8_t JSPROP_PERMANENT = 0x04;
/* (0x08 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
/** The property has a getter function. */
static constexpr uint8_t JSPROP_GETTER = 0x10;
/** The property has a setter function. */
static constexpr uint8_t JSPROP_SETTER = 0x20;
/* (0x40 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
/** A bit for internal JS engine use only. */
static constexpr uint8_t JSPROP_INTERNAL_USE_BIT = 0x80;
/* (0x1000 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
/**
* Resolve hooks and enumerate hooks must pass this flag when calling
* JS_Define* APIs to reify lazily-defined properties.
*
* JSPROP_RESOLVING is used only with property-defining APIs. It tells the
* engine to skip the resolve hook when performing the lookup at the beginning
* of property definition. This keeps the resolve hook from accidentally
* triggering itself: unchecked recursion.
*
* For enumerate hooks, triggering the resolve hook would be merely silly, not
* fatal, except in some cases involving non-configurable properties.
*/
static constexpr unsigned JSPROP_RESOLVING = 0x2000;
/**
* When redefining an existing property, ignore the value of the
* JSPROP_ENUMERATE flag. This flag is ignored in other situations.
*/
static constexpr unsigned JSPROP_IGNORE_ENUMERATE = 0x4000;
/**
* When redefining an existing property, ignore the value of the JSPROP_READONLY
* flag. This flag is ignored in other situations.
*/
static constexpr unsigned JSPROP_IGNORE_READONLY = 0x8000;
/**
* When redefining an existing property, ignore the value of the
* JSPROP_PERMANENT flag. This flag is ignored in other situations.
*/
static constexpr unsigned JSPROP_IGNORE_PERMANENT = 0x10000;
/**
* When redefining an existing property, ignore the Value in the descriptor.
* This flag is ignored in other situations.
*/
static constexpr unsigned JSPROP_IGNORE_VALUE = 0x20000;
/* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
static constexpr unsigned JSPROP_FLAGS_MASK =
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_GETTER |
JSPROP_SETTER | JSPROP_INTERNAL_USE_BIT | JSPROP_RESOLVING |
JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT |
JSPROP_IGNORE_VALUE;
namespace JS {
/**
* A structure that represents a property on an object, or the absence of a
* property. Use {,Mutable}Handle<PropertyDescriptor> to interact with
* instances of this structure rather than interacting directly with member
* fields.
*/
struct JS_PUBLIC_API PropertyDescriptor {
JSObject* obj = nullptr;
unsigned attrs = 0;
JSGetterOp getter = nullptr;
JSSetterOp setter = nullptr;
Value value;
PropertyDescriptor() = default;
void trace(JSTracer* trc);
};
} // namespace JS
namespace js {
template <typename Wrapper>
class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
const JS::PropertyDescriptor& desc() const {
return static_cast<const Wrapper*>(this)->get();
}
bool has(unsigned bit) const {
MOZ_ASSERT(bit != 0);
MOZ_ASSERT((bit & (bit - 1)) == 0); // only a single bit
return (desc().attrs & bit) != 0;
}
bool hasAny(unsigned bits) const { return (desc().attrs & bits) != 0; }
bool hasAll(unsigned bits) const { return (desc().attrs & bits) == bits; }
public:
// Descriptors with JSGetterOp/JSSetterOp are considered data
// descriptors. It's complicated.
bool isAccessorDescriptor() const {
return hasAny(JSPROP_GETTER | JSPROP_SETTER);
}
bool isGenericDescriptor() const {
return (desc().attrs & (JSPROP_GETTER | JSPROP_SETTER |
JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
}
bool isDataDescriptor() const {
return !isAccessorDescriptor() && !isGenericDescriptor();
}
bool hasConfigurable() const { return !has(JSPROP_IGNORE_PERMANENT); }
bool configurable() const {
MOZ_ASSERT(hasConfigurable());
return !has(JSPROP_PERMANENT);
}
bool hasEnumerable() const { return !has(JSPROP_IGNORE_ENUMERATE); }
bool enumerable() const {
MOZ_ASSERT(hasEnumerable());
return has(JSPROP_ENUMERATE);
}
bool hasValue() const {
return !isAccessorDescriptor() && !has(JSPROP_IGNORE_VALUE);
}
JS::HandleValue value() const {
return JS::Handle<JS::Value>::fromMarkedLocation(&desc().value);
}
bool hasWritable() const {
return !isAccessorDescriptor() && !has(JSPROP_IGNORE_READONLY);
}
bool writable() const {
MOZ_ASSERT(hasWritable());
return !has(JSPROP_READONLY);
}
bool hasGetterObject() const { return has(JSPROP_GETTER); }
JS::Handle<JSObject*> getterObject() const {
MOZ_ASSERT(hasGetterObject());
return JS::Handle<JSObject*>::fromMarkedLocation(
reinterpret_cast<JSObject* const*>(&desc().getter));
}
bool hasSetterObject() const { return has(JSPROP_SETTER); }
JS::Handle<JSObject*> setterObject() const {
MOZ_ASSERT(hasSetterObject());
return JS::Handle<JSObject*>::fromMarkedLocation(
reinterpret_cast<JSObject* const*>(&desc().setter));
}
bool hasGetterOrSetter() const { return desc().getter || desc().setter; }
JS::Handle<JSObject*> object() const {
return JS::Handle<JSObject*>::fromMarkedLocation(&desc().obj);
}
unsigned attributes() const { return desc().attrs; }
JSGetterOp getter() const { return desc().getter; }
JSSetterOp setter() const { return desc().setter; }
void assertValid() const {
#ifdef DEBUG
MOZ_ASSERT(
(attributes() &
~(JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE | JSPROP_PERMANENT |
JSPROP_IGNORE_PERMANENT | JSPROP_READONLY | JSPROP_IGNORE_READONLY |
JSPROP_IGNORE_VALUE | JSPROP_GETTER | JSPROP_SETTER |
JSPROP_RESOLVING | JSPROP_INTERNAL_USE_BIT)) == 0);
MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE));
MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT));
if (isAccessorDescriptor()) {
MOZ_ASSERT(!has(JSPROP_READONLY));
MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY));
MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE));
MOZ_ASSERT(!has(JSPROP_INTERNAL_USE_BIT));
MOZ_ASSERT(value().isUndefined());
MOZ_ASSERT_IF(!has(JSPROP_GETTER), !getter());
MOZ_ASSERT_IF(!has(JSPROP_SETTER), !setter());
} else {
MOZ_ASSERT(!hasAll(JSPROP_IGNORE_READONLY | JSPROP_READONLY));
MOZ_ASSERT_IF(has(JSPROP_IGNORE_VALUE), value().isUndefined());
}
MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_ENUMERATE));
MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_PERMANENT));
MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_READONLY));
MOZ_ASSERT_IF(has(JSPROP_RESOLVING), !has(JSPROP_IGNORE_VALUE));
#endif
}
void assertComplete() const {
#ifdef DEBUG
assertValid();
MOZ_ASSERT(
(attributes() & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT |
JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER |
JSPROP_RESOLVING | JSPROP_INTERNAL_USE_BIT)) == 0);
MOZ_ASSERT_IF(isAccessorDescriptor(),
has(JSPROP_GETTER) && has(JSPROP_SETTER));
#endif
}
void assertCompleteIfFound() const {
#ifdef DEBUG
if (object()) {
assertComplete();
}
#endif
}
};
template <typename Wrapper>
class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
: public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
public:
void clear() {
object().set(nullptr);
setAttributes(0);
setGetter(nullptr);
setSetter(nullptr);
value().setUndefined();
}
void initFields(JS::Handle<JSObject*> obj, JS::Handle<JS::Value> v,
unsigned attrs, JSGetterOp getterOp, JSSetterOp setterOp) {
object().set(obj);
value().set(v);
setAttributes(attrs);
setGetter(getterOp);
setSetter(setterOp);
}
void assign(JS::PropertyDescriptor& other) {
object().set(other.obj);
setAttributes(other.attrs);
setGetter(other.getter);
setSetter(other.setter);
value().set(other.value);
}
void setDataDescriptor(JS::Handle<JS::Value> v, unsigned attrs) {
MOZ_ASSERT((attrs & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT |
JSPROP_READONLY | JSPROP_IGNORE_ENUMERATE |
JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_READONLY)) ==
0);
object().set(nullptr);
setAttributes(attrs);
setGetter(nullptr);
setSetter(nullptr);
value().set(v);
}
JS::MutableHandle<JSObject*> object() {
return JS::MutableHandle<JSObject*>::fromMarkedLocation(&desc().obj);
}
unsigned& attributesRef() { return desc().attrs; }
JSGetterOp& getter() { return desc().getter; }
JSSetterOp& setter() { return desc().setter; }
JS::MutableHandle<JS::Value> value() {
return JS::MutableHandle<JS::Value>::fromMarkedLocation(&desc().value);
}
void setValue(JS::Handle<JS::Value> v) {
MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
attributesRef() &= ~JSPROP_IGNORE_VALUE;
value().set(v);
}
void setConfigurable(bool configurable) {
setAttributes(
(desc().attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
(configurable ? 0 : JSPROP_PERMANENT));
}
void setEnumerable(bool enumerable) {
setAttributes(
(desc().attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
(enumerable ? JSPROP_ENUMERATE : 0));
}
void setWritable(bool writable) {
MOZ_ASSERT(!(desc().attrs & (JSPROP_GETTER | JSPROP_SETTER)));
setAttributes((desc().attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
(writable ? 0 : JSPROP_READONLY));
}
void setAttributes(unsigned attrs) { desc().attrs = attrs; }
void setGetter(JSGetterOp op) { desc().getter = op; }
void setSetter(JSSetterOp op) { desc().setter = op; }
void setGetterObject(JSObject* obj) {
desc().getter = reinterpret_cast<JSGetterOp>(obj);
desc().attrs &=
~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
desc().attrs |= JSPROP_GETTER;
}
void setSetterObject(JSObject* obj) {
desc().setter = reinterpret_cast<JSSetterOp>(obj);
desc().attrs &=
~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY | JSPROP_READONLY);
desc().attrs |= JSPROP_SETTER;
}
JS::MutableHandle<JSObject*> getterObject() {
MOZ_ASSERT(this->hasGetterObject());
return JS::MutableHandle<JSObject*>::fromMarkedLocation(
reinterpret_cast<JSObject**>(&desc().getter));
}
JS::MutableHandle<JSObject*> setterObject() {
MOZ_ASSERT(this->hasSetterObject());
return JS::MutableHandle<JSObject*>::fromMarkedLocation(
reinterpret_cast<JSObject**>(&desc().setter));
}
};
} // namespace js
namespace JS {
extern JS_PUBLIC_API bool ObjectToCompletePropertyDescriptor(
JSContext* cx, Handle<JSObject*> obj, Handle<Value> descriptor,
MutableHandle<PropertyDescriptor> desc);
/*
* 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 JS_PUBLIC_API bool FromPropertyDescriptor(
JSContext* cx, Handle<PropertyDescriptor> desc, MutableHandle<Value> vp);
} // namespace JS
#endif /* js_PropertyDescriptor_h */