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;
39 * The property is non-writable. This flag is only valid for data properties.
41 static constexpr uint8_t JSPROP_READONLY
= 0x02;
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;
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
;
70 // 6.1.7.1 Property Attributes
71 enum class PropertyAttribute
: uint8_t {
72 // The descriptor is [[Configurable]] := true.
75 // The descriptor is [[Enumerable]] := true.
78 // The descriptor is [[Writable]] := true. Only valid for data descriptors.
82 class PropertyAttributes
: public mozilla::EnumSet
<PropertyAttribute
> {
83 // Re-use all EnumSet constructors.
84 using mozilla::EnumSet
<PropertyAttribute
>::EnumSet
;
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
); }
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
100 class JS_PUBLIC_API PropertyDescriptor
{
102 bool hasConfigurable_
: 1;
103 bool configurable_
: 1;
105 bool hasEnumerable_
: 1;
106 bool enumerable_
: 1;
108 bool hasWritable_
: 1;
123 : hasConfigurable_(false),
124 configurable_(false),
125 hasEnumerable_(false),
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();
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();
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();
181 // This constructor is only provided for legacy code!
182 static PropertyDescriptor
Accessor(JSObject
* getter
, JSObject
* setter
,
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();
197 static PropertyDescriptor
Accessor(mozilla::Maybe
<JSObject
*> getter
,
198 mozilla::Maybe
<JSObject
*> setter
,
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
);
207 desc
.setGetter(*getter
);
210 desc
.setSetter(*setter
);
212 desc
.setResolving(attrs
& JSPROP_RESOLVING
);
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
;
223 MOZ_ASSERT(!desc
.hasConfigurable() && !desc
.hasEnumerable() &&
224 !desc
.hasWritable() && !desc
.hasValue() && !desc
.hasGetter() &&
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());
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());
267 void setValue(const Value
& v
) {
268 MOZ_ASSERT(!isAccessorDescriptor());
273 bool hasWritable() const { return hasWritable_
; }
274 bool writable() const {
275 MOZ_ASSERT(hasWritable());
278 void setWritable(bool writable
) {
279 MOZ_ASSERT(!isAccessorDescriptor());
281 writable_
= writable
;
284 bool hasGetter() const { return hasGetter_
; }
285 JSObject
* getter() const {
286 MOZ_ASSERT(hasGetter());
289 void setGetter(JSObject
* obj
) {
290 MOZ_ASSERT(!isDataDescriptor());
295 bool hasSetter() const { return hasSetter_
; }
296 JSObject
* setter() const {
297 MOZ_ASSERT(hasSetter());
300 void setSetter(JSObject
* obj
) {
301 MOZ_ASSERT(!isDataDescriptor());
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 {
321 if (isAccessorDescriptor()) {
322 MOZ_ASSERT(!hasWritable_
);
323 MOZ_ASSERT(!hasValue_
);
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());
341 void assertComplete() const {
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());
357 template <typename Wrapper
>
358 class WrappedPtrOperations
<JS::PropertyDescriptor
, Wrapper
> {
359 const JS::PropertyDescriptor
& desc() const {
360 return static_cast<const Wrapper
*>(this)->get();
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(); }
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
); }
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
,
455 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
);
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
,
478 JS::MutableHandle
<mozilla::Maybe
<JS::PropertyDescriptor
>> desc
,
479 JS::MutableHandle
<JSObject
*> holder
);
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
);
503 #endif /* js_PropertyDescriptor_h */