Bug 1516796 - Move property-descriptor APIs into a minimal js/public/PropertyDescriptor.h header, that (at least for now) jsapi.h #includes because it still contains some property-definition stuff. r=arai
authorJeff Walden <jwalden@mit.edu>
Sat, 29 Dec 2018 13:47:29 -0600
changeset 509936 dc3c004e74d841bba1d53f22c07aeed809c448c1
parent 509935 bf02f2ce30a2be60d43a76aee69e9c9a4c15f41d
child 509937 77a8b17163210f7d4bba7e800c2fcb55b0690a24
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1516796
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1516796 - Move property-descriptor APIs into a minimal js/public/PropertyDescriptor.h header, that (at least for now) jsapi.h #includes because it still contains some property-definition stuff. r=arai
js/public/PropertyDescriptor.h
js/src/jsapi-tests/testGetPropertyDescriptor.cpp
js/src/jsapi.h
js/src/moz.build
js/src/proxy/ScriptedProxyHandler.cpp
js/src/vm/Debugger.cpp
js/src/vm/JSObject.cpp
new file mode 100644
--- /dev/null
+++ b/js/public/PropertyDescriptor.h
@@ -0,0 +1,396 @@
+/* -*- 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_BROKEN_GCC_ATTRIBUTE_WARNING, 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 JSContext;
+class JSObject;
+
+#ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
+#endif  // JS_BROKEN_GCC_ATTRIBUTE_WARNING
+
+class JS_PUBLIC_API JSTracer;
+
+#ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING
+#pragma GCC diagnostic pop
+#endif  // JS_BROKEN_GCC_ATTRIBUTE_WARNING
+
+/* 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;
+
+  static void trace(PropertyDescriptor* self, JSTracer* trc) {
+    self->trace(trc);
+  }
+  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 */
--- a/js/src/jsapi-tests/testGetPropertyDescriptor.cpp
+++ b/js/src/jsapi-tests/testGetPropertyDescriptor.cpp
@@ -1,12 +1,13 @@
 /* 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/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(test_GetPropertyDescriptor) {
   JS::RootedValue v(cx);
   EVAL("({ somename : 123 })", &v);
   CHECK(v.isObject());
 
   JS::RootedObject obj(cx, &v.toObject());
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -31,16 +31,17 @@
 #include "js/Class.h"
 #include "js/CompileOptions.h"
 #include "js/ErrorReport.h"
 #include "js/GCVector.h"
 #include "js/HashTable.h"
 #include "js/Id.h"
 #include "js/OffThreadScriptCompilation.h"
 #include "js/Principals.h"
+#include "js/PropertyDescriptor.h"
 #include "js/Realm.h"
 #include "js/RefCounted.h"
 #include "js/RootingAPI.h"
 #include "js/TracingAPI.h"
 #include "js/Transcoding.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "js/Value.h"
@@ -238,80 +239,16 @@ static MOZ_ALWAYS_INLINE JS::Value JS_Nu
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API bool JS_StringHasBeenPinned(JSContext* cx, JSString* str);
 
 /************************************************************************/
 
-/* Property attributes, set in JSPropertySpec and passed to API functions.
- *
- * NB: 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.
- */
-
-/* property is visible to for/in loop */
-static const uint8_t JSPROP_ENUMERATE = 0x01;
-
-/* not settable: assignment is no-op.  This flag is only valid when neither
-   JSPROP_GETTER nor JSPROP_SETTER is set. */
-static const uint8_t JSPROP_READONLY = 0x02;
-
-/* property cannot be deleted */
-static const uint8_t JSPROP_PERMANENT = 0x04;
-
-/* (0x08 is unused) */
-
-/* property holds getter function */
-static const uint8_t JSPROP_GETTER = 0x10;
-
-/* property holds setter function */
-static const uint8_t JSPROP_SETTER = 0x20;
-
-/* internal JS engine use only */
-static const uint8_t JSPROP_INTERNAL_USE_BIT = 0x80;
-
-/* native that can be called as a ctor */
-static const unsigned JSFUN_CONSTRUCTOR = 0x400;
-
-/* | of all the JSFUN_* flags */
-static const unsigned JSFUN_FLAGS_MASK = 0x400;
-
-/*
- * 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 const unsigned JSPROP_RESOLVING = 0x2000;
-
-/* ignore the value in JSPROP_ENUMERATE.  This flag only valid when defining
-   over an existing property. */
-static const unsigned JSPROP_IGNORE_ENUMERATE = 0x4000;
-
-/* ignore the value in JSPROP_READONLY.  This flag only valid when defining over
-   an existing property. */
-static const unsigned JSPROP_IGNORE_READONLY = 0x8000;
-
-/* ignore the value in JSPROP_PERMANENT.  This flag only valid when defining
-   over an existing property. */
-static const unsigned JSPROP_IGNORE_PERMANENT = 0x10000;
-
-/* ignore the Value in the descriptor. Nothing was specified when passed to
-   Object.defineProperty from script. */
-static const unsigned JSPROP_IGNORE_VALUE = 0x20000;
-
 /** Microseconds since the epoch, midnight, January 1, 1970 UTC. */
 extern JS_PUBLIC_API int64_t JS_Now(void);
 
 /** Don't want to export data, so provide accessors for non-inline Values. */
 extern JS_PUBLIC_API JS::Value JS_GetNaNValue(JSContext* cx);
 
 extern JS_PUBLIC_API JS::Value JS_GetNegativeInfinityValue(JSContext* cx);
 
@@ -1795,296 +1732,16 @@ extern JS_PUBLIC_API bool JS_DeepFreezeO
                                               JS::Handle<JSObject*> obj);
 
 /**
  * Freezes an object; see ES5's Object.freeze(obj) method.
  */
 extern JS_PUBLIC_API bool JS_FreezeObject(JSContext* cx,
                                           JS::Handle<JSObject*> obj);
 
-/*** Property descriptors ***************************************************/
-
-namespace JS {
-
-struct JS_PUBLIC_API PropertyDescriptor {
-  JSObject* obj;
-  unsigned attrs;
-  JSGetterOp getter;
-  JSSetterOp setter;
-  JS::Value value;
-
-  PropertyDescriptor()
-      : obj(nullptr),
-        attrs(0),
-        getter(nullptr),
-        setter(nullptr),
-        value(JS::UndefinedValue()) {}
-
-  static void trace(PropertyDescriptor* self, JSTracer* trc) {
-    self->trace(trc);
-  }
-  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::HandleValue::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::HandleObject getterObject() const {
-    MOZ_ASSERT(hasGetterObject());
-    return JS::HandleObject::fromMarkedLocation(
-        reinterpret_cast<JSObject* const*>(&desc().getter));
-  }
-  bool hasSetterObject() const { return has(JSPROP_SETTER); }
-  JS::HandleObject setterObject() const {
-    MOZ_ASSERT(hasSetterObject());
-    return JS::HandleObject::fromMarkedLocation(
-        reinterpret_cast<JSObject* const*>(&desc().setter));
-  }
-
-  bool hasGetterOrSetter() const { return desc().getter || desc().setter; }
-
-  JS::HandleObject object() const {
-    return JS::HandleObject::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::HandleObject obj, JS::HandleValue 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::HandleValue 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::MutableHandleObject object() {
-    return JS::MutableHandleObject::fromMarkedLocation(&desc().obj);
-  }
-  unsigned& attributesRef() { return desc().attrs; }
-  JSGetterOp& getter() { return desc().getter; }
-  JSSetterOp& setter() { return desc().setter; }
-  JS::MutableHandleValue value() {
-    return JS::MutableHandleValue::fromMarkedLocation(&desc().value);
-  }
-  void setValue(JS::HandleValue 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::MutableHandleObject getterObject() {
-    MOZ_ASSERT(this->hasGetterObject());
-    return JS::MutableHandleObject::fromMarkedLocation(
-        reinterpret_cast<JSObject**>(&desc().getter));
-  }
-  JS::MutableHandleObject setterObject() {
-    MOZ_ASSERT(this->hasSetterObject());
-    return JS::MutableHandleObject::fromMarkedLocation(
-        reinterpret_cast<JSObject**>(&desc().setter));
-  }
-};
-
-}  // namespace js
-
-namespace JS {
-
-extern JS_PUBLIC_API bool ObjectToCompletePropertyDescriptor(
-    JSContext* cx, JS::HandleObject obj, JS::HandleValue descriptor,
-    JS::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, JS::Handle<JS::PropertyDescriptor> desc,
-    JS::MutableHandleValue vp);
-
-}  // namespace JS
-
 /*** Standard internal methods **********************************************
  *
  * The functions below are the fundamental operations on objects.
  *
  * ES6 specifies 14 internal methods that define how objects behave.  The
  * standard is actually quite good on this topic, though you may have to read
  * it a few times. See ES6 sections 6.1.7.2 and 6.1.7.3.
  *
@@ -2924,16 +2581,26 @@ extern JS_PUBLIC_API void JS_ReleaseMapp
 extern JS_PUBLIC_API JS::Value JS_GetReservedSlot(JSObject* obj,
                                                   uint32_t index);
 
 extern JS_PUBLIC_API void JS_SetReservedSlot(JSObject* obj, uint32_t index,
                                              const JS::Value& v);
 
 /************************************************************************/
 
+/* native that can be called as a ctor */
+static constexpr unsigned JSFUN_CONSTRUCTOR = 0x400;
+
+/* | of all the JSFUN_* flags */
+static constexpr unsigned JSFUN_FLAGS_MASK = 0x400;
+
+static_assert((JSPROP_FLAGS_MASK & JSFUN_FLAGS_MASK) == 0,
+              "JSFUN_* flags do not overlap JSPROP_* flags, because bits from "
+              "the two flag-sets appear in the same flag in some APIs");
+
 /*
  * Functions and scripts.
  */
 extern JS_PUBLIC_API JSFunction* JS_NewFunction(JSContext* cx, JSNative call,
                                                 unsigned nargs, unsigned flags,
                                                 const char* name);
 
 namespace JS {
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -140,16 +140,17 @@ EXPORTS.js += [
     '../public/MemoryFunctions.h',
     '../public/MemoryMetrics.h',
     '../public/OffThreadScriptCompilation.h',
     '../public/Principals.h',
     '../public/Printf.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
     '../public/Promise.h',
+    '../public/PropertyDescriptor.h',
     '../public/ProtoKey.h',
     '../public/Proxy.h',
     '../public/Realm.h',
     '../public/RefCounted.h',
     '../public/RequiredDefines.h',
     '../public/Result.h',
     '../public/RootingAPI.h',
     '../public/SavedFrameAPI.h',
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -4,16 +4,17 @@
  * 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 "proxy/ScriptedProxyHandler.h"
 
 #include "jsapi.h"
 
 #include "js/CharacterEncoding.h"
+#include "js/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
 #include "vm/EqualityOperations.h"  // js::SameValue
 
 #include "vm/JSObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using JS::IsArrayAnswer;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -22,16 +22,17 @@
 #include "gc/HashUtil.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
 #include "js/CharacterEncoding.h"
 #include "js/Date.h"
+#include "js/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
 #include "js/SourceText.h"
 #include "js/StableStringChars.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/Vector.h"
 #include "js/Wrapper.h"
 #include "proxy/ScriptedProxyHandler.h"
 #include "util/Text.h"
 #include "vm/ArgumentsObject.h"
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -32,16 +32,17 @@
 #include "builtin/Object.h"
 #include "builtin/String.h"
 #include "builtin/Symbol.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Policy.h"
 #include "jit/BaselineJIT.h"
 #include "js/CharacterEncoding.h"
 #include "js/MemoryMetrics.h"
+#include "js/PropertyDescriptor.h"  // JS::FromPropertyDescriptor
 #include "js/Proxy.h"
 #include "js/UbiNode.h"
 #include "js/UniquePtr.h"
 #include "js/Wrapper.h"
 #include "util/Text.h"
 #include "util/Windows.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/BytecodeUtil.h"