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
13 #include <stdint.h> // uint8_t
15 #include "jstypes.h" // JS_PUBLIC_API
17 #include "js/Class.h" // JS{Getter,Setter}Op
18 #include "js/RootingAPI.h" // JS::Handle, js::{,Mutable}WrappedPtrOperations
19 #include "js/Value.h" // JS::Value
21 struct JS_PUBLIC_API JSContext
;
22 class JS_PUBLIC_API JSObject
;
23 class JS_PUBLIC_API JSTracer
;
25 /* Property attributes, set in JSPropertySpec and passed to API functions.
27 * The data structure in which some of these values are stored only uses a
28 * uint8_t to store the relevant information. Proceed with caution if trying to
29 * reorder or change the the first byte worth of flags.
32 /** The property is visible in for/in loops. */
33 static constexpr uint8_t JSPROP_ENUMERATE
= 0x01;
36 * The property is non-writable. This flag is only valid when neither
37 * JSPROP_GETTER nor JSPROP_SETTER is set.
39 static constexpr uint8_t JSPROP_READONLY
= 0x02;
42 * The property is non-configurable: it can't be deleted, and if it's an
43 * accessor descriptor, its getter and setter can't be changed.
45 static constexpr uint8_t JSPROP_PERMANENT
= 0x04;
47 /* (0x08 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
49 /** The property has a getter function. */
50 static constexpr uint8_t JSPROP_GETTER
= 0x10;
52 /** The property has a setter function. */
53 static constexpr uint8_t JSPROP_SETTER
= 0x20;
55 /* (0x40 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
57 /** A bit for internal JS engine use only. */
58 static constexpr uint8_t JSPROP_INTERNAL_USE_BIT
= 0x80;
60 /* (0x1000 is unused; add to JSPROP_FLAGS_MASK if ever defined) */
63 * Resolve hooks and enumerate hooks must pass this flag when calling
64 * JS_Define* APIs to reify lazily-defined properties.
66 * JSPROP_RESOLVING is used only with property-defining APIs. It tells the
67 * engine to skip the resolve hook when performing the lookup at the beginning
68 * of property definition. This keeps the resolve hook from accidentally
69 * triggering itself: unchecked recursion.
71 * For enumerate hooks, triggering the resolve hook would be merely silly, not
72 * fatal, except in some cases involving non-configurable properties.
74 static constexpr unsigned JSPROP_RESOLVING
= 0x2000;
77 * When redefining an existing property, ignore the value of the
78 * JSPROP_ENUMERATE flag. This flag is ignored in other situations.
80 static constexpr unsigned JSPROP_IGNORE_ENUMERATE
= 0x4000;
83 * When redefining an existing property, ignore the value of the JSPROP_READONLY
84 * flag. This flag is ignored in other situations.
86 static constexpr unsigned JSPROP_IGNORE_READONLY
= 0x8000;
89 * When redefining an existing property, ignore the value of the
90 * JSPROP_PERMANENT flag. This flag is ignored in other situations.
92 static constexpr unsigned JSPROP_IGNORE_PERMANENT
= 0x10000;
95 * When redefining an existing property, ignore the Value in the descriptor.
96 * This flag is ignored in other situations.
98 static constexpr unsigned JSPROP_IGNORE_VALUE
= 0x20000;
100 /* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
102 static constexpr unsigned JSPROP_FLAGS_MASK
=
103 JSPROP_ENUMERATE
| JSPROP_READONLY
| JSPROP_PERMANENT
| JSPROP_GETTER
|
104 JSPROP_SETTER
| JSPROP_INTERNAL_USE_BIT
| JSPROP_RESOLVING
|
105 JSPROP_IGNORE_ENUMERATE
| JSPROP_IGNORE_READONLY
| JSPROP_IGNORE_PERMANENT
|
111 * A structure that represents a property on an object, or the absence of a
112 * property. Use {,Mutable}Handle<PropertyDescriptor> to interact with
113 * instances of this structure rather than interacting directly with member
116 struct JS_PUBLIC_API PropertyDescriptor
{
117 JSObject
* obj
= nullptr;
119 JSGetterOp getter
= nullptr;
120 JSSetterOp setter
= nullptr;
123 PropertyDescriptor() = default;
125 void trace(JSTracer
* trc
);
132 template <typename Wrapper
>
133 class WrappedPtrOperations
<JS::PropertyDescriptor
, Wrapper
> {
134 const JS::PropertyDescriptor
& desc() const {
135 return static_cast<const Wrapper
*>(this)->get();
138 bool has(unsigned bit
) const {
139 MOZ_ASSERT(bit
!= 0);
140 MOZ_ASSERT((bit
& (bit
- 1)) == 0); // only a single bit
141 return (desc().attrs
& bit
) != 0;
144 bool hasAny(unsigned bits
) const { return (desc().attrs
& bits
) != 0; }
146 bool hasAll(unsigned bits
) const { return (desc().attrs
& bits
) == bits
; }
149 // Descriptors with JSGetterOp/JSSetterOp are considered data
150 // descriptors. It's complicated.
151 bool isAccessorDescriptor() const {
152 return hasAny(JSPROP_GETTER
| JSPROP_SETTER
);
154 bool isGenericDescriptor() const {
155 return (desc().attrs
& (JSPROP_GETTER
| JSPROP_SETTER
|
156 JSPROP_IGNORE_READONLY
| JSPROP_IGNORE_VALUE
)) ==
157 (JSPROP_IGNORE_READONLY
| JSPROP_IGNORE_VALUE
);
159 bool isDataDescriptor() const {
160 return !isAccessorDescriptor() && !isGenericDescriptor();
163 bool hasConfigurable() const { return !has(JSPROP_IGNORE_PERMANENT
); }
164 bool configurable() const {
165 MOZ_ASSERT(hasConfigurable());
166 return !has(JSPROP_PERMANENT
);
169 bool hasEnumerable() const { return !has(JSPROP_IGNORE_ENUMERATE
); }
170 bool enumerable() const {
171 MOZ_ASSERT(hasEnumerable());
172 return has(JSPROP_ENUMERATE
);
175 bool hasValue() const {
176 return !isAccessorDescriptor() && !has(JSPROP_IGNORE_VALUE
);
178 JS::HandleValue
value() const {
179 return JS::Handle
<JS::Value
>::fromMarkedLocation(&desc().value
);
182 bool hasWritable() const {
183 return !isAccessorDescriptor() && !has(JSPROP_IGNORE_READONLY
);
185 bool writable() const {
186 MOZ_ASSERT(hasWritable());
187 return !has(JSPROP_READONLY
);
190 bool hasGetterObject() const { return has(JSPROP_GETTER
); }
191 JS::Handle
<JSObject
*> getterObject() const {
192 MOZ_ASSERT(hasGetterObject());
193 return JS::Handle
<JSObject
*>::fromMarkedLocation(
194 reinterpret_cast<JSObject
* const*>(&desc().getter
));
196 bool hasSetterObject() const { return has(JSPROP_SETTER
); }
197 JS::Handle
<JSObject
*> setterObject() const {
198 MOZ_ASSERT(hasSetterObject());
199 return JS::Handle
<JSObject
*>::fromMarkedLocation(
200 reinterpret_cast<JSObject
* const*>(&desc().setter
));
203 bool hasGetterOrSetter() const { return desc().getter
|| desc().setter
; }
205 JS::Handle
<JSObject
*> object() const {
206 return JS::Handle
<JSObject
*>::fromMarkedLocation(&desc().obj
);
208 unsigned attributes() const { return desc().attrs
; }
209 JSGetterOp
getter() const { return desc().getter
; }
210 JSSetterOp
setter() const { return desc().setter
; }
212 void assertValid() const {
216 ~(JSPROP_ENUMERATE
| JSPROP_IGNORE_ENUMERATE
| JSPROP_PERMANENT
|
217 JSPROP_IGNORE_PERMANENT
| JSPROP_READONLY
| JSPROP_IGNORE_READONLY
|
218 JSPROP_IGNORE_VALUE
| JSPROP_GETTER
| JSPROP_SETTER
|
219 JSPROP_RESOLVING
| JSPROP_INTERNAL_USE_BIT
)) == 0);
220 MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE
| JSPROP_ENUMERATE
));
221 MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT
| JSPROP_PERMANENT
));
222 if (isAccessorDescriptor()) {
223 MOZ_ASSERT(!has(JSPROP_READONLY
));
224 MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY
));
225 MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE
));
226 MOZ_ASSERT(!has(JSPROP_INTERNAL_USE_BIT
));
227 MOZ_ASSERT(value().isUndefined());
228 MOZ_ASSERT_IF(!has(JSPROP_GETTER
), !getter());
229 MOZ_ASSERT_IF(!has(JSPROP_SETTER
), !setter());
231 MOZ_ASSERT(!hasAll(JSPROP_IGNORE_READONLY
| JSPROP_READONLY
));
232 MOZ_ASSERT_IF(has(JSPROP_IGNORE_VALUE
), value().isUndefined());
235 MOZ_ASSERT_IF(has(JSPROP_RESOLVING
), !has(JSPROP_IGNORE_ENUMERATE
));
236 MOZ_ASSERT_IF(has(JSPROP_RESOLVING
), !has(JSPROP_IGNORE_PERMANENT
));
237 MOZ_ASSERT_IF(has(JSPROP_RESOLVING
), !has(JSPROP_IGNORE_READONLY
));
238 MOZ_ASSERT_IF(has(JSPROP_RESOLVING
), !has(JSPROP_IGNORE_VALUE
));
242 void assertComplete() const {
246 (attributes() & ~(JSPROP_ENUMERATE
| JSPROP_PERMANENT
|
247 JSPROP_READONLY
| JSPROP_GETTER
| JSPROP_SETTER
|
248 JSPROP_RESOLVING
| JSPROP_INTERNAL_USE_BIT
)) == 0);
249 MOZ_ASSERT_IF(isAccessorDescriptor(),
250 has(JSPROP_GETTER
) && has(JSPROP_SETTER
));
254 void assertCompleteIfFound() const {
263 template <typename Wrapper
>
264 class MutableWrappedPtrOperations
<JS::PropertyDescriptor
, Wrapper
>
265 : public js::WrappedPtrOperations
<JS::PropertyDescriptor
, Wrapper
> {
266 JS::PropertyDescriptor
& desc() { return static_cast<Wrapper
*>(this)->get(); }
270 object().set(nullptr);
274 value().setUndefined();
277 void initFields(JS::Handle
<JSObject
*> obj
, JS::Handle
<JS::Value
> v
,
278 unsigned attrs
, JSGetterOp getterOp
, JSSetterOp setterOp
) {
281 setAttributes(attrs
);
286 void assign(JS::PropertyDescriptor
& other
) {
287 object().set(other
.obj
);
288 setAttributes(other
.attrs
);
289 setGetter(other
.getter
);
290 setSetter(other
.setter
);
291 value().set(other
.value
);
294 void setDataDescriptor(JS::Handle
<JS::Value
> v
, unsigned attrs
) {
295 MOZ_ASSERT((attrs
& ~(JSPROP_ENUMERATE
| JSPROP_PERMANENT
|
296 JSPROP_READONLY
| JSPROP_IGNORE_ENUMERATE
|
297 JSPROP_IGNORE_PERMANENT
| JSPROP_IGNORE_READONLY
)) ==
299 object().set(nullptr);
300 setAttributes(attrs
);
306 JS::MutableHandle
<JSObject
*> object() {
307 return JS::MutableHandle
<JSObject
*>::fromMarkedLocation(&desc().obj
);
309 unsigned& attributesRef() { return desc().attrs
; }
310 JSGetterOp
& getter() { return desc().getter
; }
311 JSSetterOp
& setter() { return desc().setter
; }
312 JS::MutableHandle
<JS::Value
> value() {
313 return JS::MutableHandle
<JS::Value
>::fromMarkedLocation(&desc().value
);
315 void setValue(JS::Handle
<JS::Value
> v
) {
316 MOZ_ASSERT(!(desc().attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
317 attributesRef() &= ~JSPROP_IGNORE_VALUE
;
321 void setConfigurable(bool configurable
) {
323 (desc().attrs
& ~(JSPROP_IGNORE_PERMANENT
| JSPROP_PERMANENT
)) |
324 (configurable
? 0 : JSPROP_PERMANENT
));
326 void setEnumerable(bool enumerable
) {
328 (desc().attrs
& ~(JSPROP_IGNORE_ENUMERATE
| JSPROP_ENUMERATE
)) |
329 (enumerable
? JSPROP_ENUMERATE
: 0));
331 void setWritable(bool writable
) {
332 MOZ_ASSERT(!(desc().attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
333 setAttributes((desc().attrs
& ~(JSPROP_IGNORE_READONLY
| JSPROP_READONLY
)) |
334 (writable
? 0 : JSPROP_READONLY
));
336 void setAttributes(unsigned attrs
) { desc().attrs
= attrs
; }
338 void setGetter(JSGetterOp op
) { desc().getter
= op
; }
339 void setSetter(JSSetterOp op
) { desc().setter
= op
; }
340 void setGetterObject(JSObject
* obj
) {
341 desc().getter
= reinterpret_cast<JSGetterOp
>(obj
);
343 ~(JSPROP_IGNORE_VALUE
| JSPROP_IGNORE_READONLY
| JSPROP_READONLY
);
344 desc().attrs
|= JSPROP_GETTER
;
346 void setSetterObject(JSObject
* obj
) {
347 desc().setter
= reinterpret_cast<JSSetterOp
>(obj
);
349 ~(JSPROP_IGNORE_VALUE
| JSPROP_IGNORE_READONLY
| JSPROP_READONLY
);
350 desc().attrs
|= JSPROP_SETTER
;
353 JS::MutableHandle
<JSObject
*> getterObject() {
354 MOZ_ASSERT(this->hasGetterObject());
355 return JS::MutableHandle
<JSObject
*>::fromMarkedLocation(
356 reinterpret_cast<JSObject
**>(&desc().getter
));
358 JS::MutableHandle
<JSObject
*> setterObject() {
359 MOZ_ASSERT(this->hasSetterObject());
360 return JS::MutableHandle
<JSObject
*>::fromMarkedLocation(
361 reinterpret_cast<JSObject
**>(&desc().setter
));
369 extern JS_PUBLIC_API
bool ObjectToCompletePropertyDescriptor(
370 JSContext
* cx
, Handle
<JSObject
*> obj
, Handle
<Value
> descriptor
,
371 MutableHandle
<PropertyDescriptor
> desc
);
374 * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
376 * If desc.object() is null, then vp is set to undefined.
378 extern JS_PUBLIC_API
bool FromPropertyDescriptor(
379 JSContext
* cx
, Handle
<PropertyDescriptor
> desc
, MutableHandle
<Value
> vp
);
383 #endif /* js_PropertyDescriptor_h */