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 <stdint.h> // uint8_t
17 #include "jstypes.h" // JS_PUBLIC_API
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
23 struct JS_PUBLIC_API JSContext
;
24 class JS_PUBLIC_API JSObject
;
25 class JS_PUBLIC_API JSTracer
;
27 /* Property attributes, set in JSPropertySpec and passed to API functions.
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.
34 /** The property is visible in for/in loops. */
35 static constexpr uint8_t JSPROP_ENUMERATE
= 0x01;
38 * The property is non-writable. This flag is only valid for data properties.
40 static constexpr uint8_t JSPROP_READONLY
= 0x02;
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.
46 static constexpr uint8_t JSPROP_PERMANENT
= 0x04;
49 * Resolve hooks and enumerate hooks must pass this flag when calling
50 * JS_Define* APIs to reify lazily-defined properties.
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.
57 * For enumerate hooks, triggering the resolve hook would be merely silly, not
58 * fatal, except in some cases involving non-configurable properties.
60 static constexpr unsigned JSPROP_RESOLVING
= 0x08;
62 /* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
64 static constexpr unsigned JSPROP_FLAGS_MASK
=
65 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
| JSPROP_RESOLVING
;
69 // 6.1.7.1 Property Attributes
70 enum class PropertyAttribute
: uint8_t {
71 // The descriptor is [[Configurable]] := true.
74 // The descriptor is [[Enumerable]] := true.
77 // The descriptor is [[Writable]] := true. Only valid for data descriptors.
81 class PropertyAttributes
: public mozilla::EnumSet
<PropertyAttribute
> {
82 // Re-use all EnumSet constructors.
83 using mozilla::EnumSet
<PropertyAttribute
>::EnumSet
;
86 bool configurable() const {
87 return contains(PropertyAttribute::Configurable
);
89 bool enumerable() const { return contains(PropertyAttribute::Enumerable
); }
90 bool writable() const { return contains(PropertyAttribute::Writable
); }
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
99 class JS_PUBLIC_API PropertyDescriptor
{
101 bool hasConfigurable_
: 1;
102 bool configurable_
: 1;
104 bool hasEnumerable_
: 1;
105 bool enumerable_
: 1;
107 bool hasWritable_
: 1;
122 : hasConfigurable_(false),
123 configurable_(false),
124 hasEnumerable_(false),
134 value_(UndefinedValue()) {}
136 void trace(JSTracer
* trc
);
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();
150 // This constructor is only provided for legacy code!
151 static PropertyDescriptor
Data(const Value
& value
, unsigned attrs
) {
152 MOZ_ASSERT((attrs
& ~(JSPROP_PERMANENT
| JSPROP_ENUMERATE
|
153 JSPROP_READONLY
| JSPROP_RESOLVING
)) == 0);
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();
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());
171 PropertyDescriptor desc
;
172 desc
.setConfigurable(attributes
.configurable());
173 desc
.setEnumerable(attributes
.enumerable());
174 desc
.setGetter(getter
);
175 desc
.setSetter(setter
);
176 desc
.assertComplete();
180 // This constructor is only provided for legacy code!
181 static PropertyDescriptor
Accessor(JSObject
* getter
, JSObject
* setter
,
183 MOZ_ASSERT((attrs
& ~(JSPROP_PERMANENT
| JSPROP_ENUMERATE
|
184 JSPROP_RESOLVING
)) == 0);
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();
196 static PropertyDescriptor
Accessor(mozilla::Maybe
<JSObject
*> getter
,
197 mozilla::Maybe
<JSObject
*> setter
,
199 MOZ_ASSERT((attrs
& ~(JSPROP_PERMANENT
| JSPROP_ENUMERATE
|
200 JSPROP_RESOLVING
)) == 0);
202 PropertyDescriptor desc
;
203 desc
.setConfigurable(!(attrs
& JSPROP_PERMANENT
));
204 desc
.setEnumerable(attrs
& JSPROP_ENUMERATE
);
206 desc
.setGetter(*getter
);
209 desc
.setSetter(*setter
);
211 desc
.setResolving(attrs
& JSPROP_RESOLVING
);
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.
219 static PropertyDescriptor
Empty() {
220 PropertyDescriptor desc
;
222 MOZ_ASSERT(!desc
.hasConfigurable() && !desc
.hasEnumerable() &&
223 !desc
.hasWritable() && !desc
.hasValue() && !desc
.hasGetter() &&
229 bool isAccessorDescriptor() const {
230 MOZ_ASSERT_IF(hasGetter_
|| hasSetter_
, !isDataDescriptor());
231 return hasGetter_
|| hasSetter_
;
233 bool isGenericDescriptor() const {
234 return !isAccessorDescriptor() && !isDataDescriptor();
236 bool isDataDescriptor() const {
237 MOZ_ASSERT_IF(hasWritable_
|| hasValue_
, !isAccessorDescriptor());
238 return hasWritable_
|| hasValue_
;
241 bool hasConfigurable() const { return hasConfigurable_
; }
242 bool configurable() const {
243 MOZ_ASSERT(hasConfigurable());
244 return configurable_
;
246 void setConfigurable(bool configurable
) {
247 hasConfigurable_
= true;
248 configurable_
= configurable
;
251 bool hasEnumerable() const { return hasEnumerable_
; }
252 bool enumerable() const {
253 MOZ_ASSERT(hasEnumerable());
256 void setEnumerable(bool enumerable
) {
257 hasEnumerable_
= true;
258 enumerable_
= enumerable
;
261 bool hasValue() const { return hasValue_
; }
262 Value
value() const {
263 MOZ_ASSERT(hasValue());
266 void setValue(const Value
& v
) {
267 MOZ_ASSERT(!isAccessorDescriptor());
272 bool hasWritable() const { return hasWritable_
; }
273 bool writable() const {
274 MOZ_ASSERT(hasWritable());
277 void setWritable(bool writable
) {
278 MOZ_ASSERT(!isAccessorDescriptor());
280 writable_
= writable
;
283 bool hasGetter() const { return hasGetter_
; }
284 JSObject
* getter() const {
285 MOZ_ASSERT(hasGetter());
288 void setGetter(JSObject
* obj
) {
289 MOZ_ASSERT(!isDataDescriptor());
294 bool hasSetter() const { return hasSetter_
; }
295 JSObject
* setter() const {
296 MOZ_ASSERT(hasSetter());
299 void setSetter(JSObject
* obj
) {
300 MOZ_ASSERT(!isDataDescriptor());
305 // Non-standard flag, which is set when defining properties in a resolve hook.
306 bool resolving() const { return resolving_
; }
307 void setResolving(bool resolving
) { resolving_
= resolving
; }
309 Value
* valueDoNotUse() { return &value_
; }
310 Value
const* valueDoNotUse() const { return &value_
; }
311 JSObject
** getterDoNotUse() { return &getter_
; }
312 JSObject
* const* getterDoNotUse() const { return &getter_
; }
313 void setGetterDoNotUse(JSObject
* obj
) { getter_
= obj
; }
314 JSObject
** setterDoNotUse() { return &setter_
; }
315 JSObject
* const* setterDoNotUse() const { return &setter_
; }
316 void setSetterDoNotUse(JSObject
* obj
) { setter_
= obj
; }
318 void assertValid() const {
320 if (isAccessorDescriptor()) {
321 MOZ_ASSERT(!hasWritable_
);
322 MOZ_ASSERT(!hasValue_
);
324 MOZ_ASSERT(isGenericDescriptor() || isDataDescriptor());
325 MOZ_ASSERT(!hasGetter_
);
326 MOZ_ASSERT(!hasSetter_
);
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_
);
336 MOZ_ASSERT_IF(resolving_
, !isGenericDescriptor());
340 void assertComplete() const {
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());
356 template <typename Wrapper
>
357 class WrappedPtrOperations
<JS::PropertyDescriptor
, Wrapper
> {
358 const JS::PropertyDescriptor
& desc() const {
359 return static_cast<const Wrapper
*>(this)->get();
363 bool isAccessorDescriptor() const { return desc().isAccessorDescriptor(); }
364 bool isGenericDescriptor() const { return desc().isGenericDescriptor(); }
365 bool isDataDescriptor() const { return desc().isDataDescriptor(); }
367 bool hasConfigurable() const { return desc().hasConfigurable(); }
368 bool configurable() const { return desc().configurable(); }
370 bool hasEnumerable() const { return desc().hasEnumerable(); }
371 bool enumerable() const { return desc().enumerable(); }
373 bool hasValue() const { return desc().hasValue(); }
374 JS::Handle
<JS::Value
> value() const {
375 MOZ_ASSERT(hasValue());
376 return JS::Handle
<JS::Value
>::fromMarkedLocation(desc().valueDoNotUse());
379 bool hasWritable() const { return desc().hasWritable(); }
380 bool writable() const { return desc().writable(); }
382 bool hasGetter() const { return desc().hasGetter(); }
383 JS::Handle
<JSObject
*> getter() const {
384 MOZ_ASSERT(hasGetter());
385 return JS::Handle
<JSObject
*>::fromMarkedLocation(desc().getterDoNotUse());
387 bool hasSetter() const { return desc().hasSetter(); }
388 JS::Handle
<JSObject
*> setter() const {
389 MOZ_ASSERT(hasSetter());
390 return JS::Handle
<JSObject
*>::fromMarkedLocation(desc().setterDoNotUse());
393 bool resolving() const { return desc().resolving(); }
395 void assertValid() const { desc().assertValid(); }
396 void assertComplete() const { desc().assertComplete(); }
399 template <typename Wrapper
>
400 class MutableWrappedPtrOperations
<JS::PropertyDescriptor
, Wrapper
>
401 : public js::WrappedPtrOperations
<JS::PropertyDescriptor
, Wrapper
> {
402 JS::PropertyDescriptor
& desc() { return static_cast<Wrapper
*>(this)->get(); }
405 JS::MutableHandle
<JS::Value
> value() {
406 MOZ_ASSERT(desc().hasValue());
407 return JS::MutableHandle
<JS::Value
>::fromMarkedLocation(
408 desc().valueDoNotUse());
410 void setValue(JS::Handle
<JS::Value
> v
) { desc().setValue(v
); }
412 void setConfigurable(bool configurable
) {
413 desc().setConfigurable(configurable
);
415 void setEnumerable(bool enumerable
) { desc().setEnumerable(enumerable
); }
416 void setWritable(bool writable
) { desc().setWritable(writable
); }
418 void setGetter(JSObject
* obj
) { desc().setGetter(obj
); }
419 void setSetter(JSObject
* obj
) { desc().setSetter(obj
); }
421 JS::MutableHandle
<JSObject
*> getter() {
422 MOZ_ASSERT(desc().hasGetter());
423 return JS::MutableHandle
<JSObject
*>::fromMarkedLocation(
424 desc().getterDoNotUse());
426 JS::MutableHandle
<JSObject
*> setter() {
427 MOZ_ASSERT(desc().hasSetter());
428 return JS::MutableHandle
<JSObject
*>::fromMarkedLocation(
429 desc().setterDoNotUse());
432 void setResolving(bool resolving
) { desc().setResolving(resolving
); }
439 extern JS_PUBLIC_API
bool ObjectToCompletePropertyDescriptor(
440 JSContext
* cx
, Handle
<JSObject
*> obj
, Handle
<Value
> descriptor
,
441 MutableHandle
<PropertyDescriptor
> desc
);
444 * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
446 * If desc.isNothing(), then vp is set to undefined.
448 extern JS_PUBLIC_API
bool FromPropertyDescriptor(
449 JSContext
* cx
, Handle
<mozilla::Maybe
<PropertyDescriptor
>> desc
,
450 MutableHandle
<Value
> vp
);
454 #endif /* js_PropertyDescriptor_h */