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 <stddef.h>  // size_t
16 #include <stdint.h>  // uint8_t
17 
18 #include "jstypes.h"  // JS_PUBLIC_API
19 
20 #include "js/Class.h"       // JS{Getter,Setter}Op
21 #include "js/Id.h"          // jsid
22 #include "js/RootingAPI.h"  // JS::Handle, js::{,Mutable}WrappedPtrOperations
23 #include "js/Value.h"       // JS::Value
24 
25 struct JS_PUBLIC_API JSContext;
26 class JS_PUBLIC_API JSObject;
27 class JS_PUBLIC_API JSTracer;
28 
29 /* Property attributes, set in JSPropertySpec and passed to API functions.
30  *
31  * The data structure in which some of these values are stored only uses a
32  * uint8_t to store the relevant information.  Proceed with caution if trying to
33  * reorder or change the the first byte worth of flags.
34  */
35 
36 /** The property is visible in for/in loops. */
37 static constexpr uint8_t JSPROP_ENUMERATE = 0x01;
38 
39 /**
40  * The property is non-writable.  This flag is only valid for data properties.
41  */
42 static constexpr uint8_t JSPROP_READONLY = 0x02;
43 
44 /**
45  * The property is non-configurable: it can't be deleted, and if it's an
46  * accessor descriptor, its getter and setter can't be changed.
47  */
48 static constexpr uint8_t JSPROP_PERMANENT = 0x04;
49 
50 /**
51  * Resolve hooks and enumerate hooks must pass this flag when calling
52  * JS_Define* APIs to reify lazily-defined properties.
53  *
54  * JSPROP_RESOLVING is used only with property-defining APIs. It tells the
55  * engine to skip the resolve hook when performing the lookup at the beginning
56  * of property definition. This keeps the resolve hook from accidentally
57  * triggering itself: unchecked recursion.
58  *
59  * For enumerate hooks, triggering the resolve hook would be merely silly, not
60  * fatal, except in some cases involving non-configurable properties.
61  */
62 static constexpr unsigned JSPROP_RESOLVING = 0x08;
63 
64 /* (higher flags are unused; add to JSPROP_FLAGS_MASK if ever defined) */
65 
66 static constexpr unsigned JSPROP_FLAGS_MASK =
67     JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING;
68 
69 namespace JS {
70 
71 // 6.1.7.1 Property Attributes
72 enum class PropertyAttribute : uint8_t {
73   // The descriptor is [[Configurable]] := true.
74   Configurable,
75 
76   // The descriptor is [[Enumerable]] := true.
77   Enumerable,
78 
79   // The descriptor is [[Writable]] := true. Only valid for data descriptors.
80   Writable
81 };
82 
83 class PropertyAttributes : public mozilla::EnumSet<PropertyAttribute> {
84   // Re-use all EnumSet constructors.
85   using mozilla::EnumSet<PropertyAttribute>::EnumSet;
86 
87  public:
configurable()88   bool configurable() const {
89     return contains(PropertyAttribute::Configurable);
90   }
enumerable()91   bool enumerable() const { return contains(PropertyAttribute::Enumerable); }
writable()92   bool writable() const { return contains(PropertyAttribute::Writable); }
93 };
94 
95 /**
96  * A structure that represents a property on an object, or the absence of a
97  * property.  Use {,Mutable}Handle<PropertyDescriptor> to interact with
98  * instances of this structure rather than interacting directly with member
99  * fields.
100  */
101 class JS_PUBLIC_API PropertyDescriptor {
102  private:
103   bool hasConfigurable_ : 1;
104   bool configurable_ : 1;
105 
106   bool hasEnumerable_ : 1;
107   bool enumerable_ : 1;
108 
109   bool hasWritable_ : 1;
110   bool writable_ : 1;
111 
112   bool hasValue_ : 1;
113   bool hasGetter_ : 1;
114   bool hasSetter_ : 1;
115 
116   bool resolving_ : 1;
117 
118   JSObject* getter_;
119   JSObject* setter_;
120   Value value_;
121 
122  public:
PropertyDescriptor()123   PropertyDescriptor()
124       : hasConfigurable_(false),
125         configurable_(false),
126         hasEnumerable_(false),
127         enumerable_(false),
128         hasWritable_(false),
129         writable_(false),
130         hasValue_(false),
131         hasGetter_(false),
132         hasSetter_(false),
133         resolving_(false),
134         getter_(nullptr),
135         setter_(nullptr),
136         value_(UndefinedValue()) {}
137 
138   void trace(JSTracer* trc);
139 
140   // Construct a new complete DataDescriptor.
141   static PropertyDescriptor Data(const Value& value,
142                                  PropertyAttributes attributes = {}) {
143     PropertyDescriptor desc;
144     desc.setConfigurable(attributes.configurable());
145     desc.setEnumerable(attributes.enumerable());
146     desc.setWritable(attributes.writable());
147     desc.setValue(value);
148     desc.assertComplete();
149     return desc;
150   }
151 
152   // This constructor is only provided for legacy code!
Data(const Value & value,unsigned attrs)153   static PropertyDescriptor Data(const Value& value, unsigned attrs) {
154     MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
155                           JSPROP_READONLY | JSPROP_RESOLVING)) == 0);
156 
157     PropertyDescriptor desc;
158     desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
159     desc.setEnumerable(attrs & JSPROP_ENUMERATE);
160     desc.setWritable(!(attrs & JSPROP_READONLY));
161     desc.setValue(value);
162     desc.setResolving(attrs & JSPROP_RESOLVING);
163     desc.assertComplete();
164     return desc;
165   }
166 
167   // Construct a new complete AccessorDescriptor.
168   // Note: This means JSPROP_GETTER and JSPROP_SETTER are always set.
169   static PropertyDescriptor Accessor(JSObject* getter, JSObject* setter,
170                                      PropertyAttributes attributes = {}) {
171     MOZ_ASSERT(!attributes.writable());
172 
173     PropertyDescriptor desc;
174     desc.setConfigurable(attributes.configurable());
175     desc.setEnumerable(attributes.enumerable());
176     desc.setGetter(getter);
177     desc.setSetter(setter);
178     desc.assertComplete();
179     return desc;
180   }
181 
182   // This constructor is only provided for legacy code!
Accessor(JSObject * getter,JSObject * setter,unsigned attrs)183   static PropertyDescriptor Accessor(JSObject* getter, JSObject* setter,
184                                      unsigned attrs) {
185     MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
186                           JSPROP_RESOLVING)) == 0);
187 
188     PropertyDescriptor desc;
189     desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
190     desc.setEnumerable(attrs & JSPROP_ENUMERATE);
191     desc.setGetter(getter);
192     desc.setSetter(setter);
193     desc.setResolving(attrs & JSPROP_RESOLVING);
194     desc.assertComplete();
195     return desc;
196   }
197 
Accessor(mozilla::Maybe<JSObject * > getter,mozilla::Maybe<JSObject * > setter,unsigned attrs)198   static PropertyDescriptor Accessor(mozilla::Maybe<JSObject*> getter,
199                                      mozilla::Maybe<JSObject*> setter,
200                                      unsigned attrs) {
201     MOZ_ASSERT((attrs & ~(JSPROP_PERMANENT | JSPROP_ENUMERATE |
202                           JSPROP_RESOLVING)) == 0);
203 
204     PropertyDescriptor desc;
205     desc.setConfigurable(!(attrs & JSPROP_PERMANENT));
206     desc.setEnumerable(attrs & JSPROP_ENUMERATE);
207     if (getter) {
208       desc.setGetter(*getter);
209     }
210     if (setter) {
211       desc.setSetter(*setter);
212     }
213     desc.setResolving(attrs & JSPROP_RESOLVING);
214     desc.assertValid();
215     return desc;
216   }
217 
218   // Construct a new incomplete empty PropertyDescriptor.
219   // Using the spec syntax this would be { }. Specific fields like [[Value]]
220   // can be added with e.g., setValue.
Empty()221   static PropertyDescriptor Empty() {
222     PropertyDescriptor desc;
223     desc.assertValid();
224     MOZ_ASSERT(!desc.hasConfigurable() && !desc.hasEnumerable() &&
225                !desc.hasWritable() && !desc.hasValue() && !desc.hasGetter() &&
226                !desc.hasSetter());
227     return desc;
228   }
229 
230  public:
isAccessorDescriptor()231   bool isAccessorDescriptor() const {
232     MOZ_ASSERT_IF(hasGetter_ || hasSetter_, !isDataDescriptor());
233     return hasGetter_ || hasSetter_;
234   }
isGenericDescriptor()235   bool isGenericDescriptor() const {
236     return !isAccessorDescriptor() && !isDataDescriptor();
237   }
isDataDescriptor()238   bool isDataDescriptor() const {
239     MOZ_ASSERT_IF(hasWritable_ || hasValue_, !isAccessorDescriptor());
240     return hasWritable_ || hasValue_;
241   }
242 
hasConfigurable()243   bool hasConfigurable() const { return hasConfigurable_; }
configurable()244   bool configurable() const {
245     MOZ_ASSERT(hasConfigurable());
246     return configurable_;
247   }
setConfigurable(bool configurable)248   void setConfigurable(bool configurable) {
249     hasConfigurable_ = true;
250     configurable_ = configurable;
251   }
252 
hasEnumerable()253   bool hasEnumerable() const { return hasEnumerable_; }
enumerable()254   bool enumerable() const {
255     MOZ_ASSERT(hasEnumerable());
256     return enumerable_;
257   }
setEnumerable(bool enumerable)258   void setEnumerable(bool enumerable) {
259     hasEnumerable_ = true;
260     enumerable_ = enumerable;
261   }
262 
hasValue()263   bool hasValue() const { return hasValue_; }
value()264   Value value() const {
265     MOZ_ASSERT(hasValue());
266     return value_;
267   }
setValue(const Value & v)268   void setValue(const Value& v) {
269     MOZ_ASSERT(!isAccessorDescriptor());
270     hasValue_ = true;
271     value_ = v;
272   }
273 
hasWritable()274   bool hasWritable() const { return hasWritable_; }
writable()275   bool writable() const {
276     MOZ_ASSERT(hasWritable());
277     return writable_;
278   }
setWritable(bool writable)279   void setWritable(bool writable) {
280     MOZ_ASSERT(!isAccessorDescriptor());
281     hasWritable_ = true;
282     writable_ = writable;
283   }
284 
hasGetter()285   bool hasGetter() const { return hasGetter_; }
getter()286   JSObject* getter() const {
287     MOZ_ASSERT(hasGetter());
288     return getter_;
289   }
setGetter(JSObject * obj)290   void setGetter(JSObject* obj) {
291     MOZ_ASSERT(!isDataDescriptor());
292     hasGetter_ = true;
293     getter_ = obj;
294   }
295 
hasSetter()296   bool hasSetter() const { return hasSetter_; }
setter()297   JSObject* setter() const {
298     MOZ_ASSERT(hasSetter());
299     return setter_;
300   }
setSetter(JSObject * obj)301   void setSetter(JSObject* obj) {
302     MOZ_ASSERT(!isDataDescriptor());
303     hasSetter_ = true;
304     setter_ = obj;
305   }
306 
307   // Non-standard flag, which is set when defining properties in a resolve hook.
resolving()308   bool resolving() const { return resolving_; }
setResolving(bool resolving)309   void setResolving(bool resolving) { resolving_ = resolving; }
310 
valueDoNotUse()311   Value* valueDoNotUse() { return &value_; }
valueDoNotUse()312   Value const* valueDoNotUse() const { return &value_; }
getterDoNotUse()313   JSObject** getterDoNotUse() { return &getter_; }
getterDoNotUse()314   JSObject* const* getterDoNotUse() const { return &getter_; }
setGetterDoNotUse(JSObject * obj)315   void setGetterDoNotUse(JSObject* obj) { getter_ = obj; }
setterDoNotUse()316   JSObject** setterDoNotUse() { return &setter_; }
setterDoNotUse()317   JSObject* const* setterDoNotUse() const { return &setter_; }
setSetterDoNotUse(JSObject * obj)318   void setSetterDoNotUse(JSObject* obj) { setter_ = obj; }
319 
assertValid()320   void assertValid() const {
321 #ifdef DEBUG
322     if (isAccessorDescriptor()) {
323       MOZ_ASSERT(!hasWritable_);
324       MOZ_ASSERT(!hasValue_);
325     } else {
326       MOZ_ASSERT(isGenericDescriptor() || isDataDescriptor());
327       MOZ_ASSERT(!hasGetter_);
328       MOZ_ASSERT(!hasSetter_);
329     }
330 
331     MOZ_ASSERT_IF(!hasConfigurable_, !configurable_);
332     MOZ_ASSERT_IF(!hasEnumerable_, !enumerable_);
333     MOZ_ASSERT_IF(!hasWritable_, !writable_);
334     MOZ_ASSERT_IF(!hasValue_, value_.isUndefined());
335     MOZ_ASSERT_IF(!hasGetter_, !getter_);
336     MOZ_ASSERT_IF(!hasSetter_, !setter_);
337 
338     MOZ_ASSERT_IF(resolving_, !isGenericDescriptor());
339 #endif
340   }
341 
assertComplete()342   void assertComplete() const {
343 #ifdef DEBUG
344     assertValid();
345     MOZ_ASSERT(hasConfigurable());
346     MOZ_ASSERT(hasEnumerable());
347     MOZ_ASSERT(!isGenericDescriptor());
348     MOZ_ASSERT_IF(isDataDescriptor(), hasValue() && hasWritable());
349     MOZ_ASSERT_IF(isAccessorDescriptor(), hasGetter() && hasSetter());
350 #endif
351   }
352 };
353 
354 }  // namespace JS
355 
356 namespace js {
357 
358 template <typename Wrapper>
359 class WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
desc()360   const JS::PropertyDescriptor& desc() const {
361     return static_cast<const Wrapper*>(this)->get();
362   }
363 
364  public:
isAccessorDescriptor()365   bool isAccessorDescriptor() const { return desc().isAccessorDescriptor(); }
isGenericDescriptor()366   bool isGenericDescriptor() const { return desc().isGenericDescriptor(); }
isDataDescriptor()367   bool isDataDescriptor() const { return desc().isDataDescriptor(); }
368 
hasConfigurable()369   bool hasConfigurable() const { return desc().hasConfigurable(); }
configurable()370   bool configurable() const { return desc().configurable(); }
371 
hasEnumerable()372   bool hasEnumerable() const { return desc().hasEnumerable(); }
enumerable()373   bool enumerable() const { return desc().enumerable(); }
374 
hasValue()375   bool hasValue() const { return desc().hasValue(); }
value()376   JS::Handle<JS::Value> value() const {
377     MOZ_ASSERT(hasValue());
378     return JS::Handle<JS::Value>::fromMarkedLocation(desc().valueDoNotUse());
379   }
380 
hasWritable()381   bool hasWritable() const { return desc().hasWritable(); }
writable()382   bool writable() const { return desc().writable(); }
383 
hasGetter()384   bool hasGetter() const { return desc().hasGetter(); }
getter()385   JS::Handle<JSObject*> getter() const {
386     MOZ_ASSERT(hasGetter());
387     return JS::Handle<JSObject*>::fromMarkedLocation(desc().getterDoNotUse());
388   }
hasSetter()389   bool hasSetter() const { return desc().hasSetter(); }
setter()390   JS::Handle<JSObject*> setter() const {
391     MOZ_ASSERT(hasSetter());
392     return JS::Handle<JSObject*>::fromMarkedLocation(desc().setterDoNotUse());
393   }
394 
resolving()395   bool resolving() const { return desc().resolving(); }
396 
assertValid()397   void assertValid() const { desc().assertValid(); }
assertComplete()398   void assertComplete() const { desc().assertComplete(); }
399 };
400 
401 template <typename Wrapper>
402 class MutableWrappedPtrOperations<JS::PropertyDescriptor, Wrapper>
403     : public js::WrappedPtrOperations<JS::PropertyDescriptor, Wrapper> {
desc()404   JS::PropertyDescriptor& desc() { return static_cast<Wrapper*>(this)->get(); }
405 
406  public:
value()407   JS::MutableHandle<JS::Value> value() {
408     MOZ_ASSERT(desc().hasValue());
409     return JS::MutableHandle<JS::Value>::fromMarkedLocation(
410         desc().valueDoNotUse());
411   }
setValue(JS::Handle<JS::Value> v)412   void setValue(JS::Handle<JS::Value> v) { desc().setValue(v); }
413 
setConfigurable(bool configurable)414   void setConfigurable(bool configurable) {
415     desc().setConfigurable(configurable);
416   }
setEnumerable(bool enumerable)417   void setEnumerable(bool enumerable) { desc().setEnumerable(enumerable); }
setWritable(bool writable)418   void setWritable(bool writable) { desc().setWritable(writable); }
419 
setGetter(JSObject * obj)420   void setGetter(JSObject* obj) { desc().setGetter(obj); }
setSetter(JSObject * obj)421   void setSetter(JSObject* obj) { desc().setSetter(obj); }
422 
getter()423   JS::MutableHandle<JSObject*> getter() {
424     MOZ_ASSERT(desc().hasGetter());
425     return JS::MutableHandle<JSObject*>::fromMarkedLocation(
426         desc().getterDoNotUse());
427   }
setter()428   JS::MutableHandle<JSObject*> setter() {
429     MOZ_ASSERT(desc().hasSetter());
430     return JS::MutableHandle<JSObject*>::fromMarkedLocation(
431         desc().setterDoNotUse());
432   }
433 
setResolving(bool resolving)434   void setResolving(bool resolving) { desc().setResolving(resolving); }
435 };
436 
437 }  // namespace js
438 
439 /**
440  * Get a description of one of obj's own properties. If no such property exists
441  * on obj, return true with desc.object() set to null.
442  *
443  * Implements: ES6 [[GetOwnProperty]] internal method.
444  */
445 extern JS_PUBLIC_API bool JS_GetOwnPropertyDescriptorById(
446     JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
447     JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
448 
449 extern JS_PUBLIC_API bool JS_GetOwnPropertyDescriptor(
450     JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
451     JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
452 
453 extern JS_PUBLIC_API bool JS_GetOwnUCPropertyDescriptor(
454     JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
455     size_t namelen,
456     JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
457 
458 /**
459  * DEPRECATED
460  *
461  * Like JS_GetOwnPropertyDescriptorById, but also searches the prototype chain
462  * if no own property is found directly on obj. The object on which the
463  * property is found is returned in holder. If the property is not found
464  * on the prototype chain, then desc is Nothing.
465  */
466 extern JS_PUBLIC_API bool JS_GetPropertyDescriptorById(
467     JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
468     JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
469     JS::MutableHandle<JSObject*> holder);
470 
471 extern JS_PUBLIC_API bool JS_GetPropertyDescriptor(
472     JSContext* cx, JS::Handle<JSObject*> obj, const char* name,
473     JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
474     JS::MutableHandle<JSObject*> holder);
475 
476 extern JS_PUBLIC_API bool JS_GetUCPropertyDescriptor(
477     JSContext* cx, JS::Handle<JSObject*> obj, const char16_t* name,
478     size_t namelen,
479     JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
480     JS::MutableHandle<JSObject*> holder);
481 
482 namespace JS {
483 
484 extern JS_PUBLIC_API bool ObjectToCompletePropertyDescriptor(
485     JSContext* cx, Handle<JSObject*> obj, Handle<Value> descriptor,
486     MutableHandle<PropertyDescriptor> desc);
487 
488 /*
489  * ES6 draft rev 32 (2015 Feb 2) 6.2.4.4 FromPropertyDescriptor(Desc).
490  *
491  * If desc.isNothing(), then vp is set to undefined.
492  */
493 extern JS_PUBLIC_API bool FromPropertyDescriptor(
494     JSContext* cx, Handle<mozilla::Maybe<PropertyDescriptor>> desc,
495     MutableHandle<Value> vp);
496 
497 }  // namespace JS
498 
499 #endif /* js_PropertyDescriptor_h */
500