tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PropertyDescriptor.h (16458B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /* Property descriptors and flags. */
      7 
      8 #ifndef js_PropertyDescriptor_h
      9 #define js_PropertyDescriptor_h
     10 
     11 #include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_ASSERT_IF
     12 #include "mozilla/EnumSet.h"     // mozilla::EnumSet
     13 #include "mozilla/Maybe.h"       // mozilla::Maybe
     14 
     15 #include <stddef.h>  // size_t
     16 #include <stdint.h>  // uint8_t
     17 
     18 #include "jstypes.h"  // JS_PUBLIC_API
     19 
     20 #include "js/Id.h"          // jsid
     21 #include "js/RootingAPI.h"  // JS::Handle, js::{,Mutable}WrappedPtrOperations
     22 #include "js/Value.h"       // JS::Value
     23 
     24 struct JS_PUBLIC_API JSContext;
     25 class JS_PUBLIC_API JSObject;
     26 class JS_PUBLIC_API JSTracer;
     27 
     28 /* Property attributes, set in JSPropertySpec and passed to API functions.
     29 *
     30 * The data structure in which some of these values are stored only uses a
     31 * uint8_t to store the relevant information.  Proceed with caution if trying to
     32 * reorder or change the the first byte worth of flags.
     33 */
     34 
     35 /** The property is visible in for/in loops. */
     36 static constexpr uint8_t JSPROP_ENUMERATE = 0x01;
     37 
     38 /**
     39 * The property is non-writable.  This flag is only valid for data properties.
     40 */
     41 static constexpr uint8_t JSPROP_READONLY = 0x02;
     42 
     43 /**
     44 * The property is non-configurable: it can't be deleted, and if it's an
     45 * accessor descriptor, its getter and setter can't be changed.
     46 */
     47 static constexpr uint8_t JSPROP_PERMANENT = 0x04;
     48 
     49 /**
     50 * Resolve hooks and enumerate hooks must pass this flag when calling
     51 * JS_Define* APIs to reify lazily-defined properties.
     52 *
     53 * JSPROP_RESOLVING is used only with property-defining APIs. It tells the
     54 * engine to skip the resolve hook when performing the lookup at the beginning
     55 * of property definition. This keeps the resolve hook from accidentally
     56 * triggering itself: unchecked recursion.
     57 *
     58 * For enumerate hooks, triggering the resolve hook would be merely silly, not
     59 * fatal, except in some cases involving non-configurable properties.
     60 */
     61 static constexpr unsigned JSPROP_RESOLVING = 0x08;
     62 
     63 /* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
     64 
     65 static constexpr unsigned JSPROP_FLAGS_MASK =
     66    JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING;
     67 
     68 namespace JS {
     69 
     70 // 6.1.7.1 Property Attributes
     71 enum class PropertyAttribute : uint8_t {
     72  // The descriptor is [[Configurable]] := true.
     73  Configurable,
     74 
     75  // The descriptor is [[Enumerable]] := true.
     76  Enumerable,
     77 
     78  // The descriptor is [[Writable]] := true. Only valid for data descriptors.
     79  Writable
     80 };
     81 
     82 class PropertyAttributes : public mozilla::EnumSet<PropertyAttribute> {
     83  // Re-use all EnumSet constructors.
     84  using mozilla::EnumSet<PropertyAttribute>::EnumSet;
     85 
     86 public:
     87  bool configurable() const {
     88    return contains(PropertyAttribute::Configurable);
     89  }
     90  bool enumerable() const { return contains(PropertyAttribute::Enumerable); }
     91  bool writable() const { return contains(PropertyAttribute::Writable); }
     92 };
     93 
     94 /**
     95 * A structure that represents a property on an object, or the absence of a
     96 * property.  Use {,Mutable}Handle<PropertyDescriptor> to interact with
     97 * instances of this structure rather than interacting directly with member
     98 * fields.
     99 */
    100 class JS_PUBLIC_API PropertyDescriptor {
    101 private:
    102  bool hasConfigurable_ : 1;
    103  bool configurable_ : 1;
    104 
    105  bool hasEnumerable_ : 1;
    106  bool enumerable_ : 1;
    107 
    108  bool hasWritable_ : 1;
    109  bool writable_ : 1;
    110 
    111  bool hasValue_ : 1;
    112  bool hasGetter_ : 1;
    113  bool hasSetter_ : 1;
    114 
    115  bool resolving_ : 1;
    116 
    117  JSObject* getter_;
    118  JSObject* setter_;
    119  Value value_;
    120 
    121 public:
    122  PropertyDescriptor()
    123      : hasConfigurable_(false),
    124        configurable_(false),
    125        hasEnumerable_(false),
    126        enumerable_(false),
    127        hasWritable_(false),
    128        writable_(false),
    129        hasValue_(false),
    130        hasGetter_(false),
    131        hasSetter_(false),
    132        resolving_(false),
    133        getter_(nullptr),
    134        setter_(nullptr),
    135        value_(UndefinedValue()) {}
    136 
    137  void trace(JSTracer* trc);
    138 
    139  // Construct a new complete DataDescriptor.
    140  static PropertyDescriptor Data(const Value& value,
    141                                 PropertyAttributes attributes = {}) {
    142    PropertyDescriptor desc;
    143    desc.setConfigurable(attributes.configurable());
    144    desc.setEnumerable(attributes.enumerable());
    145    desc.setWritable(attributes.writable());
    146    desc.setValue(value);
    147    desc.assertComplete();
    148    return desc;
    149  }
    150 
    151  // This constructor is only provided for legacy code!
    152  static PropertyDescriptor Data(const Value& value, unsigned attrs) {
    153    MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
    154                          JSPROP_READONLY | JSPROP_RESOLVING)) == 0);
    155 
    156    PropertyDescriptor desc;
    157    desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
    158    desc.setEnumerable(attrs & JSPROP_ENUMERATE);
    159    desc.setWritable(!(attrs & JSPROP_READONLY));
    160    desc.setValue(value);
    161    desc.setResolving(attrs & JSPROP_RESOLVING);
    162    desc.assertComplete();
    163    return desc;
    164  }
    165 
    166  // Construct a new complete AccessorDescriptor.
    167  // Note: This means JSPROP_GETTER and JSPROP_SETTER are always set.
    168  static PropertyDescriptor Accessor(JSObject* getter, JSObject* setter,
    169                                     PropertyAttributes attributes = {}) {
    170    MOZ_ASSERT(!attributes.writable());
    171 
    172    PropertyDescriptor desc;
    173    desc.setConfigurable(attributes.configurable());
    174    desc.setEnumerable(attributes.enumerable());
    175    desc.setGetter(getter);
    176    desc.setSetter(setter);
    177    desc.assertComplete();
    178    return desc;
    179  }
    180 
    181  // This constructor is only provided for legacy code!
    182  static PropertyDescriptor Accessor(JSObject* getter, JSObject* setter,
    183                                     unsigned attrs) {
    184    MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
    185                          JSPROP_RESOLVING)) == 0);
    186 
    187    PropertyDescriptor desc;
    188    desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
    189    desc.setEnumerable(attrs & JSPROP_ENUMERATE);
    190    desc.setGetter(getter);
    191    desc.setSetter(setter);
    192    desc.setResolving(attrs & JSPROP_RESOLVING);
    193    desc.assertComplete();
    194    return desc;
    195  }
    196 
    197  static PropertyDescriptor Accessor(mozilla::Maybe<JSObject*> getter,
    198                                     mozilla::Maybe<JSObject*> setter,
    199                                     unsigned attrs) {
    200    MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
    201                          JSPROP_RESOLVING)) == 0);
    202 
    203    PropertyDescriptor desc;
    204    desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
    205    desc.setEnumerable(attrs & JSPROP_ENUMERATE);
    206    if (getter) {
    207      desc.setGetter(*getter);
    208    }
    209    if (setter) {
    210      desc.setSetter(*setter);
    211    }
    212    desc.setResolving(attrs & JSPROP_RESOLVING);
    213    desc.assertValid();
    214    return desc;
    215  }
    216 
    217  // Construct a new incomplete empty PropertyDescriptor.
    218  // Using the spec syntax this would be { }. Specific fields like [[Value]]
    219  // can be added with e.g., setValue.
    220  static PropertyDescriptor Empty() {
    221    PropertyDescriptor desc;
    222    desc.assertValid();
    223    MOZ_ASSERT(!desc.hasConfigurable() && !desc.hasEnumerable() &&
    224               !desc.hasWritable() && !desc.hasValue() && !desc.hasGetter() &&
    225               !desc.hasSetter());
    226    return desc;
    227  }
    228 
    229 public:
    230  bool isAccessorDescriptor() const {
    231    MOZ_ASSERT_IF(hasGetter_ || hasSetter_, !isDataDescriptor());
    232    return hasGetter_ || hasSetter_;
    233  }
    234  bool isGenericDescriptor() const {
    235    return !isAccessorDescriptor() && !isDataDescriptor();
    236  }
    237  bool isDataDescriptor() const {
    238    MOZ_ASSERT_IF(hasWritable_ || hasValue_, !isAccessorDescriptor());
    239    return hasWritable_ || hasValue_;
    240  }
    241 
    242  bool hasConfigurable() const { return hasConfigurable_; }
    243  bool configurable() const {
    244    MOZ_ASSERT(hasConfigurable());
    245    return configurable_;
    246  }
    247  void setConfigurable(bool configurable) {
    248    hasConfigurable_ = true;
    249    configurable_ = configurable;
    250  }
    251 
    252  bool hasEnumerable() const { return hasEnumerable_; }
    253  bool enumerable() const {
    254    MOZ_ASSERT(hasEnumerable());
    255    return enumerable_;
    256  }
    257  void setEnumerable(bool enumerable) {
    258    hasEnumerable_ = true;
    259    enumerable_ = enumerable;
    260  }
    261 
    262  bool hasValue() const { return hasValue_; }
    263  Value value() const {
    264    MOZ_ASSERT(hasValue());
    265    return value_;
    266  }
    267  void setValue(const Value& v) {
    268    MOZ_ASSERT(!isAccessorDescriptor());
    269    hasValue_ = true;
    270    value_ = v;
    271  }
    272 
    273  bool hasWritable() const { return hasWritable_; }
    274  bool writable() const {
    275    MOZ_ASSERT(hasWritable());
    276    return writable_;
    277  }
    278  void setWritable(bool writable) {
    279    MOZ_ASSERT(!isAccessorDescriptor());
    280    hasWritable_ = true;
    281    writable_ = writable;
    282  }
    283 
    284  bool hasGetter() const { return hasGetter_; }
    285  JSObject* getter() const {
    286    MOZ_ASSERT(hasGetter());
    287    return getter_;
    288  }
    289  void setGetter(JSObject* obj) {
    290    MOZ_ASSERT(!isDataDescriptor());
    291    hasGetter_ = true;
    292    getter_ = obj;
    293  }
    294 
    295  bool hasSetter() const { return hasSetter_; }
    296  JSObject* setter() const {
    297    MOZ_ASSERT(hasSetter());
    298    return setter_;
    299  }
    300  void setSetter(JSObject* obj) {
    301    MOZ_ASSERT(!isDataDescriptor());
    302    hasSetter_ = true;
    303    setter_ = obj;
    304  }
    305 
    306  // Non-standard flag, which is set when defining properties in a resolve hook.
    307  bool resolving() const { return resolving_; }
    308  void setResolving(bool resolving) { resolving_ = resolving; }
    309 
    310  Value* valueDoNotUse() { return &value_; }
    311  Value const* valueDoNotUse() const { return &value_; }
    312  JSObject** getterDoNotUse() { return &getter_; }
    313  JSObject* const* getterDoNotUse() const { return &getter_; }
    314  void setGetterDoNotUse(JSObject* obj) { getter_ = obj; }
    315  JSObject** setterDoNotUse() { return &setter_; }
    316  JSObject* const* setterDoNotUse() const { return &setter_; }
    317  void setSetterDoNotUse(JSObject* obj) { setter_ = obj; }
    318 
    319  void assertValid() const {
    320 #ifdef DEBUG
    321    if (isAccessorDescriptor()) {
    322      MOZ_ASSERT(!hasWritable_);
    323      MOZ_ASSERT(!hasValue_);
    324    } else {
    325      MOZ_ASSERT(isGenericDescriptor() || isDataDescriptor());
    326      MOZ_ASSERT(!hasGetter_);
    327      MOZ_ASSERT(!hasSetter_);
    328    }
    329 
    330    MOZ_ASSERT_IF(!hasConfigurable_, !configurable_);
    331    MOZ_ASSERT_IF(!hasEnumerable_, !enumerable_);
    332    MOZ_ASSERT_IF(!hasWritable_, !writable_);
    333    MOZ_ASSERT_IF(!hasValue_, value_.isUndefined());
    334    MOZ_ASSERT_IF(!hasGetter_, !getter_);
    335    MOZ_ASSERT_IF(!hasSetter_, !setter_);
    336 
    337    MOZ_ASSERT_IF(resolving_, !isGenericDescriptor());
    338 #endif
    339  }
    340 
    341  void assertComplete() const {
    342 #ifdef DEBUG
    343    assertValid();
    344    MOZ_ASSERT(hasConfigurable());
    345    MOZ_ASSERT(hasEnumerable());
    346    MOZ_ASSERT(!isGenericDescriptor());
    347    MOZ_ASSERT_IF(isDataDescriptor(), hasValue() && hasWritable());
    348    MOZ_ASSERT_IF(isAccessorDescriptor(), hasGetter() && hasSetter());
    349 #endif
    350  }
    351 };
    352 
    353 }  // namespace JS
    354 
    355 namespace js {
    356 
    357 template <typename Wrapper>
    358 class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
    359  const JS::PropertyDescriptor& desc() const {
    360    return static_cast<const Wrapper*>(this)->get();
    361  }
    362 
    363 public:
    364  bool isAccessorDescriptor() const { return desc().isAccessorDescriptor(); }
    365  bool isGenericDescriptor() const { return desc().isGenericDescriptor(); }
    366  bool isDataDescriptor() const { return desc().isDataDescriptor(); }
    367 
    368  bool hasConfigurable() const { return desc().hasConfigurable(); }
    369  bool configurable() const { return desc().configurable(); }
    370 
    371  bool hasEnumerable() const { return desc().hasEnumerable(); }
    372  bool enumerable() const { return desc().enumerable(); }
    373 
    374  bool hasValue() const { return desc().hasValue(); }
    375  JS::Handle<JS::Value> value() const {
    376    MOZ_ASSERT(hasValue());
    377    return JS::Handle<JS::Value>::fromMarkedLocation(desc().valueDoNotUse());
    378  }
    379 
    380  bool hasWritable() const { return desc().hasWritable(); }
    381  bool writable() const { return desc().writable(); }
    382 
    383  bool hasGetter() const { return desc().hasGetter(); }
    384  JS::Handle<JSObject*> getter() const {
    385    MOZ_ASSERT(hasGetter());
    386    return JS::Handle<JSObject*>::fromMarkedLocation(desc().getterDoNotUse());
    387  }
    388  bool hasSetter() const { return desc().hasSetter(); }
    389  JS::Handle<JSObject*> setter() const {
    390    MOZ_ASSERT(hasSetter());
    391    return JS::Handle<JSObject*>::fromMarkedLocation(desc().setterDoNotUse());
    392  }
    393 
    394  bool resolving() const { return desc().resolving(); }
    395 
    396  void assertValid() const { desc().assertValid(); }
    397  void assertComplete() const { desc().assertComplete(); }
    398 };
    399 
    400 template <typename Wrapper>
    401 class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
    402    : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
    403  JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
    404 
    405 public:
    406  JS::MutableHandle<JS::Value> value() {
    407    MOZ_ASSERT(desc().hasValue());
    408    return JS::MutableHandle<JS::Value>::fromMarkedLocation(
    409        desc().valueDoNotUse());
    410  }
    411  void setValue(JS::Handle<JS::Value> v) { desc().setValue(v); }
    412 
    413  void setConfigurable(bool configurable) {
    414    desc().setConfigurable(configurable);
    415  }
    416  void setEnumerable(bool enumerable) { desc().setEnumerable(enumerable); }
    417  void setWritable(bool writable) { desc().setWritable(writable); }
    418 
    419  void setGetter(JSObject* obj) { desc().setGetter(obj); }
    420  void setSetter(JSObject* obj) { desc().setSetter(obj); }
    421 
    422  JS::MutableHandle<JSObject*> getter() {
    423    MOZ_ASSERT(desc().hasGetter());
    424    return JS::MutableHandle<JSObject*>::fromMarkedLocation(
    425        desc().getterDoNotUse());
    426  }
    427  JS::MutableHandle<JSObject*> setter() {
    428    MOZ_ASSERT(desc().hasSetter());
    429    return JS::MutableHandle<JSObject*>::fromMarkedLocation(
    430        desc().setterDoNotUse());
    431  }
    432 
    433  void setResolving(bool resolving) { desc().setResolving(resolving); }
    434 };
    435 
    436 }  // namespace js
    437 
    438 /**
    439 * Get a description of one of obj's own properties. If no such property exists
    440 * on obj, return true with desc.object() set to null.
    441 *
    442 * Implements: ES6 [[GetOwnProperty]] internal method.
    443 */
    444 extern JS_PUBLIC_API bool JS_GetOwnPropertyDescriptorById(
    445    JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
    446    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
    447 
    448 extern JS_PUBLIC_API bool JS_GetOwnPropertyDescriptor(
    449    JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
    450    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
    451 
    452 extern JS_PUBLIC_API bool JS_GetOwnUCPropertyDescriptor(
    453    JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
    454    size_t namelen,
    455    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
    456 
    457 /**
    458 * DEPRECATED
    459 *
    460 * Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain
    461 * if no own property is found directly on obj. The object on which the
    462 * property is found is returned in holder. If the property is not found
    463 * on the prototype chain, then desc is Nothing.
    464 */
    465 extern JS_PUBLIC_API bool JS_GetPropertyDescriptorById(
    466    JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
    467    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
    468    JS::MutableHandle<JSObject*> holder);
    469 
    470 extern JS_PUBLIC_API bool JS_GetPropertyDescriptor(
    471    JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
    472    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
    473    JS::MutableHandle<JSObject*> holder);
    474 
    475 extern JS_PUBLIC_API bool JS_GetUCPropertyDescriptor(
    476    JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
    477    size_t namelen,
    478    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
    479    JS::MutableHandle<JSObject*> holder);
    480 
    481 namespace JS {
    482 
    483 // https://tc39.es/ecma262/#sec-topropertydescriptor
    484 // https://tc39.es/ecma262/#sec-completepropertydescriptor
    485 //
    486 // Implements ToPropertyDescriptor combined with CompletePropertyDescriptor,
    487 // if the former is successful.
    488 extern JS_PUBLIC_API bool ToCompletePropertyDescriptor(
    489    JSContext* cx, Handle<Value> descriptor,
    490    MutableHandle<PropertyDescriptor> desc);
    491 
    492 /*
    493 * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
    494 *
    495 * If desc.isNothing(), then vp is set to undefined.
    496 */
    497 extern JS_PUBLIC_API bool FromPropertyDescriptor(
    498    JSContext* cx, Handle<mozilla::Maybe<PropertyDescriptor>> desc,
    499    MutableHandle<Value> vp);
    500 
    501 }  // namespace JS
    502 
    503 #endif /* js_PropertyDescriptor_h */