зеркало из https://github.com/mozilla/gecko-dev.git
384 строки
13 KiB
C++
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 */
|