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 <stdint.h>  // uint8_t
16 
17 #include "jstypes.h"  // JS_PUBLIC_API
18 
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
22 
23 struct JS_PUBLIC_API JSContext;
24 class JS_PUBLIC_API JSObject;
25 class JS_PUBLIC_API JSTracer;
26 
27 /* Property attributes, set in JSPropertySpec and passed to API functions.
28  *
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.
32  */
33 
34 /** The property is visible in for/in loops. */
35 static constexpr uint8_t JSPROP_ENUMERATE = 0x01;
36 
37 /**
38  * The property is non-writable.  This flag is only valid for data properties.
39  */
40 static constexpr uint8_t JSPROP_READONLY = 0x02;
41 
42 /**
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.
45  */
46 static constexpr uint8_t JSPROP_PERMANENT = 0x04;
47 
48 /**
49  * Resolve hooks and enumerate hooks must pass this flag when calling
50  * JS_Define* APIs to reify lazily-defined properties.
51  *
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.
56  *
57  * For enumerate hooks, triggering the resolve hook would be merely silly, not
58  * fatal, except in some cases involving non-configurable properties.
59  */
60 static constexpr unsigned JSPROP_RESOLVING = 0x08;
61 
62 /* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
63 
64 static constexpr unsigned JSPROP_FLAGS_MASK =
65     JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING;
66 
67 namespace JS {
68 
69 // 6.1.7.1 Property Attributes
70 enum class PropertyAttribute : uint8_t {
71   // The descriptor is [[Configurable]] := true.
72   Configurable,
73 
74   // The descriptor is [[Enumerable]] := true.
75   Enumerable,
76 
77   // The descriptor is [[Writable]] := true. Only valid for data descriptors.
78   Writable
79 };
80 
81 class PropertyAttributes : public mozilla::EnumSet<PropertyAttribute> {
82   // Re-use all EnumSet constructors.
83   using mozilla::EnumSet<PropertyAttribute>::EnumSet;
84 
85  public:
configurable()86   bool configurable() const {
87     return contains(PropertyAttribute::Configurable);
88   }
enumerable()89   bool enumerable() const { return contains(PropertyAttribute::Enumerable); }
writable()90   bool writable() const { return contains(PropertyAttribute::Writable); }
91 };
92 
93 /**
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
97  * fields.
98  */
99 class JS_PUBLIC_API PropertyDescriptor {
100  private:
101   bool hasConfigurable_ : 1;
102   bool configurable_ : 1;
103 
104   bool hasEnumerable_ : 1;
105   bool enumerable_ : 1;
106 
107   bool hasWritable_ : 1;
108   bool writable_ : 1;
109 
110   bool hasValue_ : 1;
111   bool hasGetter_ : 1;
112   bool hasSetter_ : 1;
113 
114   bool resolving_ : 1;
115 
116   JSObject* getter_;
117   JSObject* setter_;
118   Value value_;
119 
120  public:
PropertyDescriptor()121   PropertyDescriptor()
122       : hasConfigurable_(false),
123         configurable_(false),
124         hasEnumerable_(false),
125         enumerable_(false),
126         hasWritable_(false),
127         writable_(false),
128         hasValue_(false),
129         hasGetter_(false),
130         hasSetter_(false),
131         resolving_(false),
132         getter_(nullptr),
133         setter_(nullptr),
134         value_(UndefinedValue()) {}
135 
136   void trace(JSTracer* trc);
137 
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();
147     return desc;
148   }
149 
150   // This constructor is only provided for legacy code!
Data(const Value & value,unsigned attrs)151   static PropertyDescriptor Data(const Value& value, unsigned attrs) {
152     MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
153                           JSPROP_READONLY | JSPROP_RESOLVING)) == 0);
154 
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();
162     return desc;
163   }
164 
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());
170 
171     PropertyDescriptor desc;
172     desc.setConfigurable(attributes.configurable());
173     desc.setEnumerable(attributes.enumerable());
174     desc.setGetter(getter);
175     desc.setSetter(setter);
176     desc.assertComplete();
177     return desc;
178   }
179 
180   // This constructor is only provided for legacy code!
Accessor(JSObject * getter,JSObject * setter,unsigned attrs)181   static PropertyDescriptor Accessor(JSObject* getter, JSObject* setter,
182                                      unsigned attrs) {
183     MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
184                           JSPROP_RESOLVING)) == 0);
185 
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();
193     return desc;
194   }
195 
Accessor(mozilla::Maybe<JSObject * > getter,mozilla::Maybe<JSObject * > setter,unsigned attrs)196   static PropertyDescriptor Accessor(mozilla::Maybe<JSObject*> getter,
197                                      mozilla::Maybe<JSObject*> setter,
198                                      unsigned attrs) {
199     MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
200                           JSPROP_RESOLVING)) == 0);
201 
202     PropertyDescriptor desc;
203     desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
204     desc.setEnumerable(attrs & JSPROP_ENUMERATE);
205     if (getter) {
206       desc.setGetter(*getter);
207     }
208     if (setter) {
209       desc.setSetter(*setter);
210     }
211     desc.setResolving(attrs & JSPROP_RESOLVING);
212     desc.assertValid();
213     return desc;
214   }
215 
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.
Empty()219   static PropertyDescriptor Empty() {
220     PropertyDescriptor desc;
221     desc.assertValid();
222     MOZ_ASSERT(!desc.hasConfigurable() && !desc.hasEnumerable() &&
223                !desc.hasWritable() && !desc.hasValue() && !desc.hasGetter() &&
224                !desc.hasSetter());
225     return desc;
226   }
227 
228  public:
isAccessorDescriptor()229   bool isAccessorDescriptor() const {
230     MOZ_ASSERT_IF(hasGetter_ || hasSetter_, !isDataDescriptor());
231     return hasGetter_ || hasSetter_;
232   }
isGenericDescriptor()233   bool isGenericDescriptor() const {
234     return !isAccessorDescriptor() && !isDataDescriptor();
235   }
isDataDescriptor()236   bool isDataDescriptor() const {
237     MOZ_ASSERT_IF(hasWritable_ || hasValue_, !isAccessorDescriptor());
238     return hasWritable_ || hasValue_;
239   }
240 
hasConfigurable()241   bool hasConfigurable() const { return hasConfigurable_; }
configurable()242   bool configurable() const {
243     MOZ_ASSERT(hasConfigurable());
244     return configurable_;
245   }
setConfigurable(bool configurable)246   void setConfigurable(bool configurable) {
247     hasConfigurable_ = true;
248     configurable_ = configurable;
249   }
250 
hasEnumerable()251   bool hasEnumerable() const { return hasEnumerable_; }
enumerable()252   bool enumerable() const {
253     MOZ_ASSERT(hasEnumerable());
254     return enumerable_;
255   }
setEnumerable(bool enumerable)256   void setEnumerable(bool enumerable) {
257     hasEnumerable_ = true;
258     enumerable_ = enumerable;
259   }
260 
hasValue()261   bool hasValue() const { return hasValue_; }
value()262   Value value() const {
263     MOZ_ASSERT(hasValue());
264     return value_;
265   }
setValue(const Value & v)266   void setValue(const Value& v) {
267     MOZ_ASSERT(!isAccessorDescriptor());
268     hasValue_ = true;
269     value_ = v;
270   }
271 
hasWritable()272   bool hasWritable() const { return hasWritable_; }
writable()273   bool writable() const {
274     MOZ_ASSERT(hasWritable());
275     return writable_;
276   }
setWritable(bool writable)277   void setWritable(bool writable) {
278     MOZ_ASSERT(!isAccessorDescriptor());
279     hasWritable_ = true;
280     writable_ = writable;
281   }
282 
hasGetter()283   bool hasGetter() const { return hasGetter_; }
getter()284   JSObject* getter() const {
285     MOZ_ASSERT(hasGetter());
286     return getter_;
287   }
setGetter(JSObject * obj)288   void setGetter(JSObject* obj) {
289     MOZ_ASSERT(!isDataDescriptor());
290     hasGetter_ = true;
291     getter_ = obj;
292   }
293 
hasSetter()294   bool hasSetter() const { return hasSetter_; }
setter()295   JSObject* setter() const {
296     MOZ_ASSERT(hasSetter());
297     return setter_;
298   }
setSetter(JSObject * obj)299   void setSetter(JSObject* obj) {
300     MOZ_ASSERT(!isDataDescriptor());
301     hasSetter_ = true;
302     setter_ = obj;
303   }
304 
305   // Non-standard flag, which is set when defining properties in a resolve hook.
resolving()306   bool resolving() const { return resolving_; }
setResolving(bool resolving)307   void setResolving(bool resolving) { resolving_ = resolving; }
308 
valueDoNotUse()309   Value* valueDoNotUse() { return &value_; }
valueDoNotUse()310   Value const* valueDoNotUse() const { return &value_; }
getterDoNotUse()311   JSObject** getterDoNotUse() { return &getter_; }
getterDoNotUse()312   JSObject* const* getterDoNotUse() const { return &getter_; }
setGetterDoNotUse(JSObject * obj)313   void setGetterDoNotUse(JSObject* obj) { getter_ = obj; }
setterDoNotUse()314   JSObject** setterDoNotUse() { return &setter_; }
setterDoNotUse()315   JSObject* const* setterDoNotUse() const { return &setter_; }
setSetterDoNotUse(JSObject * obj)316   void setSetterDoNotUse(JSObject* obj) { setter_ = obj; }
317 
assertValid()318   void assertValid() const {
319 #ifdef DEBUG
320     if (isAccessorDescriptor()) {
321       MOZ_ASSERT(!hasWritable_);
322       MOZ_ASSERT(!hasValue_);
323     } else {
324       MOZ_ASSERT(isGenericDescriptor() || isDataDescriptor());
325       MOZ_ASSERT(!hasGetter_);
326       MOZ_ASSERT(!hasSetter_);
327     }
328 
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_);
335 
336     MOZ_ASSERT_IF(resolving_, !isGenericDescriptor());
337 #endif
338   }
339 
assertComplete()340   void assertComplete() const {
341 #ifdef DEBUG
342     assertValid();
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());
348 #endif
349   }
350 };
351 
352 }  // namespace JS
353 
354 namespace js {
355 
356 template <typename Wrapper>
357 class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
desc()358   const JS::PropertyDescriptor& desc() const {
359     return static_cast<const Wrapper*>(this)->get();
360   }
361 
362  public:
isAccessorDescriptor()363   bool isAccessorDescriptor() const { return desc().isAccessorDescriptor(); }
isGenericDescriptor()364   bool isGenericDescriptor() const { return desc().isGenericDescriptor(); }
isDataDescriptor()365   bool isDataDescriptor() const { return desc().isDataDescriptor(); }
366 
hasConfigurable()367   bool hasConfigurable() const { return desc().hasConfigurable(); }
configurable()368   bool configurable() const { return desc().configurable(); }
369 
hasEnumerable()370   bool hasEnumerable() const { return desc().hasEnumerable(); }
enumerable()371   bool enumerable() const { return desc().enumerable(); }
372 
hasValue()373   bool hasValue() const { return desc().hasValue(); }
value()374   JS::Handle<JS::Value> value() const {
375     MOZ_ASSERT(hasValue());
376     return JS::Handle<JS::Value>::fromMarkedLocation(desc().valueDoNotUse());
377   }
378 
hasWritable()379   bool hasWritable() const { return desc().hasWritable(); }
writable()380   bool writable() const { return desc().writable(); }
381 
hasGetter()382   bool hasGetter() const { return desc().hasGetter(); }
getter()383   JS::Handle<JSObject*> getter() const {
384     MOZ_ASSERT(hasGetter());
385     return JS::Handle<JSObject*>::fromMarkedLocation(desc().getterDoNotUse());
386   }
hasSetter()387   bool hasSetter() const { return desc().hasSetter(); }
setter()388   JS::Handle<JSObject*> setter() const {
389     MOZ_ASSERT(hasSetter());
390     return JS::Handle<JSObject*>::fromMarkedLocation(desc().setterDoNotUse());
391   }
392 
resolving()393   bool resolving() const { return desc().resolving(); }
394 
assertValid()395   void assertValid() const { desc().assertValid(); }
assertComplete()396   void assertComplete() const { desc().assertComplete(); }
397 };
398 
399 template <typename Wrapper>
400 class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
401     : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
desc()402   JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
403 
404  public:
value()405   JS::MutableHandle<JS::Value> value() {
406     MOZ_ASSERT(desc().hasValue());
407     return JS::MutableHandle<JS::Value>::fromMarkedLocation(
408         desc().valueDoNotUse());
409   }
setValue(JS::Handle<JS::Value> v)410   void setValue(JS::Handle<JS::Value> v) { desc().setValue(v); }
411 
setConfigurable(bool configurable)412   void setConfigurable(bool configurable) {
413     desc().setConfigurable(configurable);
414   }
setEnumerable(bool enumerable)415   void setEnumerable(bool enumerable) { desc().setEnumerable(enumerable); }
setWritable(bool writable)416   void setWritable(bool writable) { desc().setWritable(writable); }
417 
setGetter(JSObject * obj)418   void setGetter(JSObject* obj) { desc().setGetter(obj); }
setSetter(JSObject * obj)419   void setSetter(JSObject* obj) { desc().setSetter(obj); }
420 
getter()421   JS::MutableHandle<JSObject*> getter() {
422     MOZ_ASSERT(desc().hasGetter());
423     return JS::MutableHandle<JSObject*>::fromMarkedLocation(
424         desc().getterDoNotUse());
425   }
setter()426   JS::MutableHandle<JSObject*> setter() {
427     MOZ_ASSERT(desc().hasSetter());
428     return JS::MutableHandle<JSObject*>::fromMarkedLocation(
429         desc().setterDoNotUse());
430   }
431 
setResolving(bool resolving)432   void setResolving(bool resolving) { desc().setResolving(resolving); }
433 };
434 
435 }  // namespace js
436 
437 namespace JS {
438 
439 extern JS_PUBLIC_API bool ObjectToCompletePropertyDescriptor(
440     JSContext* cx, Handle<JSObject*> obj, Handle<Value> descriptor,
441     MutableHandle<PropertyDescriptor> desc);
442 
443 /*
444  * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
445  *
446  * If desc.isNothing(), then vp is set to undefined.
447  */
448 extern JS_PUBLIC_API bool FromPropertyDescriptor(
449     JSContext* cx, Handle<mozilla::Maybe<PropertyDescriptor>> desc,
450     MutableHandle<Value> vp);
451 
452 }  // namespace JS
453 
454 #endif /* js_PropertyDescriptor_h */
455