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