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 */