Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / js / public / PropertyDescriptor.h
blobd298aa42743eb31d4ed98a4e61eeff752a5deb08
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/. */
6 /* Property descriptors and flags. */
8 #ifndef js_PropertyDescriptor_h
9 #define js_PropertyDescriptor_h
11 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF
12 #include "mozilla/EnumSet.h" // mozilla::EnumSet
13 #include "mozilla/Maybe.h" // mozilla::Maybe
15 #include <stddef.h> // size_t
16 #include <stdint.h> // uint8_t
18 #include "jstypes.h" // JS_PUBLIC_API
20 #include "js/Id.h" // jsid
21 #include "js/RootingAPI.h" // JS::Handle, js::{,Mutable}WrappedPtrOperations
22 #include "js/Value.h" // JS::Value
24 struct JS_PUBLIC_API JSContext;
25 class JS_PUBLIC_API JSObject;
26 class JS_PUBLIC_API JSTracer;
28 /* Property attributes, set in JSPropertySpec and passed to API functions.
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.
35 /** The property is visible in for/in loops. */
36 static constexpr uint8_t JSPROP_ENUMERATE = 0x01;
38 /**
39 * The property is non-writable. This flag is only valid for data properties.
41 static constexpr uint8_t JSPROP_READONLY = 0x02;
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.
47 static constexpr uint8_t JSPROP_PERMANENT = 0x04;
49 /**
50 * Resolve hooks and enumerate hooks must pass this flag when calling
51 * JS_Define* APIs to reify lazily-defined properties.
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.
58 * For enumerate hooks, triggering the resolve hook would be merely silly, not
59 * fatal, except in some cases involving non-configurable properties.
61 static constexpr unsigned JSPROP_RESOLVING = 0x08;
63 /* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
65 static constexpr unsigned JSPROP_FLAGS_MASK =
66 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING;
68 namespace JS {
70 // 6.1.7.1 Property Attributes
71 enum class PropertyAttribute : uint8_t {
72 // The descriptor is [[Configurable]] := true.
73 Configurable,
75 // The descriptor is [[Enumerable]] := true.
76 Enumerable,
78 // The descriptor is [[Writable]] := true. Only valid for data descriptors.
79 Writable
82 class PropertyAttributes : public mozilla::EnumSet<PropertyAttribute> {
83 // Re-use all EnumSet constructors.
84 using mozilla::EnumSet<PropertyAttribute>::EnumSet;
86 public:
87 bool configurable() const {
88 return contains(PropertyAttribute::Configurable);
90 bool enumerable() const { return contains(PropertyAttribute::Enumerable); }
91 bool writable() const { return contains(PropertyAttribute::Writable); }
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.
100 class JS_PUBLIC_API PropertyDescriptor {
101 private:
102 bool hasConfigurable_ : 1;
103 bool configurable_ : 1;
105 bool hasEnumerable_ : 1;
106 bool enumerable_ : 1;
108 bool hasWritable_ : 1;
109 bool writable_ : 1;
111 bool hasValue_ : 1;
112 bool hasGetter_ : 1;
113 bool hasSetter_ : 1;
115 bool resolving_ : 1;
117 JSObject* getter_;
118 JSObject* setter_;
119 Value value_;
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()) {}
137 void trace(JSTracer* trc);
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;
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);
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;
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());
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;
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);
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;
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);
203 PropertyDescriptor desc;
204 desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
205 desc.setEnumerable(attrs & JSPROP_ENUMERATE);
206 if (getter) {
207 desc.setGetter(*getter);
209 if (setter) {
210 desc.setSetter(*setter);
212 desc.setResolving(attrs & JSPROP_RESOLVING);
213 desc.assertValid();
214 return desc;
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;
229 public:
230 bool isAccessorDescriptor() const {
231 MOZ_ASSERT_IF(hasGetter_ || hasSetter_, !isDataDescriptor());
232 return hasGetter_ || hasSetter_;
234 bool isGenericDescriptor() const {
235 return !isAccessorDescriptor() && !isDataDescriptor();
237 bool isDataDescriptor() const {
238 MOZ_ASSERT_IF(hasWritable_ || hasValue_, !isAccessorDescriptor());
239 return hasWritable_ || hasValue_;
242 bool hasConfigurable() const { return hasConfigurable_; }
243 bool configurable() const {
244 MOZ_ASSERT(hasConfigurable());
245 return configurable_;
247 void setConfigurable(bool configurable) {
248 hasConfigurable_ = true;
249 configurable_ = configurable;
252 bool hasEnumerable() const { return hasEnumerable_; }
253 bool enumerable() const {
254 MOZ_ASSERT(hasEnumerable());
255 return enumerable_;
257 void setEnumerable(bool enumerable) {
258 hasEnumerable_ = true;
259 enumerable_ = enumerable;
262 bool hasValue() const { return hasValue_; }
263 Value value() const {
264 MOZ_ASSERT(hasValue());
265 return value_;
267 void setValue(const Value& v) {
268 MOZ_ASSERT(!isAccessorDescriptor());
269 hasValue_ = true;
270 value_ = v;
273 bool hasWritable() const { return hasWritable_; }
274 bool writable() const {
275 MOZ_ASSERT(hasWritable());
276 return writable_;
278 void setWritable(bool writable) {
279 MOZ_ASSERT(!isAccessorDescriptor());
280 hasWritable_ = true;
281 writable_ = writable;
284 bool hasGetter() const { return hasGetter_; }
285 JSObject* getter() const {
286 MOZ_ASSERT(hasGetter());
287 return getter_;
289 void setGetter(JSObject* obj) {
290 MOZ_ASSERT(!isDataDescriptor());
291 hasGetter_ = true;
292 getter_ = obj;
295 bool hasSetter() const { return hasSetter_; }
296 JSObject* setter() const {
297 MOZ_ASSERT(hasSetter());
298 return setter_;
300 void setSetter(JSObject* obj) {
301 MOZ_ASSERT(!isDataDescriptor());
302 hasSetter_ = true;
303 setter_ = obj;
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; }
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; }
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_);
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_);
337 MOZ_ASSERT_IF(resolving_, !isGenericDescriptor());
338 #endif
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
353 } // namespace JS
355 namespace js {
357 template <typename Wrapper>
358 class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
359 const JS::PropertyDescriptor& desc() const {
360 return static_cast<const Wrapper*>(this)->get();
363 public:
364 bool isAccessorDescriptor() const { return desc().isAccessorDescriptor(); }
365 bool isGenericDescriptor() const { return desc().isGenericDescriptor(); }
366 bool isDataDescriptor() const { return desc().isDataDescriptor(); }
368 bool hasConfigurable() const { return desc().hasConfigurable(); }
369 bool configurable() const { return desc().configurable(); }
371 bool hasEnumerable() const { return desc().hasEnumerable(); }
372 bool enumerable() const { return desc().enumerable(); }
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());
380 bool hasWritable() const { return desc().hasWritable(); }
381 bool writable() const { return desc().writable(); }
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());
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());
394 bool resolving() const { return desc().resolving(); }
396 void assertValid() const { desc().assertValid(); }
397 void assertComplete() const { desc().assertComplete(); }
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(); }
405 public:
406 JS::MutableHandle<JS::Value> value() {
407 MOZ_ASSERT(desc().hasValue());
408 return JS::MutableHandle<JS::Value>::fromMarkedLocation(
409 desc().valueDoNotUse());
411 void setValue(JS::Handle<JS::Value> v) { desc().setValue(v); }
413 void setConfigurable(bool configurable) {
414 desc().setConfigurable(configurable);
416 void setEnumerable(bool enumerable) { desc().setEnumerable(enumerable); }
417 void setWritable(bool writable) { desc().setWritable(writable); }
419 void setGetter(JSObject* obj) { desc().setGetter(obj); }
420 void setSetter(JSObject* obj) { desc().setSetter(obj); }
422 JS::MutableHandle<JSObject*> getter() {
423 MOZ_ASSERT(desc().hasGetter());
424 return JS::MutableHandle<JSObject*>::fromMarkedLocation(
425 desc().getterDoNotUse());
427 JS::MutableHandle<JSObject*> setter() {
428 MOZ_ASSERT(desc().hasSetter());
429 return JS::MutableHandle<JSObject*>::fromMarkedLocation(
430 desc().setterDoNotUse());
433 void setResolving(bool resolving) { desc().setResolving(resolving); }
436 } // namespace js
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.
442 * Implements: ES6 [[GetOwnProperty]] internal method.
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);
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);
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);
458 * DEPRECATED
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.
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);
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);
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);
481 namespace JS {
483 // https://tc39.es/ecma262/#sec-topropertydescriptor
484 // https://tc39.es/ecma262/#sec-completepropertydescriptor
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);
493 * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
495 * If desc.isNothing(), then vp is set to undefined.
497 extern JS_PUBLIC_API bool FromPropertyDescriptor(
498 JSContext* cx, Handle<mozilla::Maybe<PropertyDescriptor>> desc,
499 MutableHandle<Value> vp);
501 } // namespace JS
503 #endif /* js_PropertyDescriptor_h */