summaryrefslogtreecommitdiffstats
path: root/js/public/PropertyDescriptor.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/public/PropertyDescriptor.h')
-rw-r--r--js/public/PropertyDescriptor.h383
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 */