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