diff options
Diffstat (limited to 'js/public/PropertyDescriptor.h')
-rw-r--r-- | js/public/PropertyDescriptor.h | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/js/public/PropertyDescriptor.h b/js/public/PropertyDescriptor.h new file mode 100644 index 0000000000..de70838f61 --- /dev/null +++ b/js/public/PropertyDescriptor.h @@ -0,0 +1,383 @@ +/* -*- 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 */ |