1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* Fundamental operations on objects. */
8 
9 #ifndef vm_ObjectOperations_h
10 #define vm_ObjectOperations_h
11 
12 #include "mozilla/Attributes.h"  // MOZ_ALWAYS_INLINE
13 #include "mozilla/Maybe.h"
14 
15 #include <stdint.h>  // uint32_t
16 
17 #include "jsapi.h"  // JSPROP_ENUMERATE, JS::PropertyDescriptor
18 
19 #include "js/Class.h"       // JS::ObjectOpResult
20 #include "js/Id.h"          // INT_TO_JSID, jsid, JSID_INT_MAX, SYMBOL_TO_JSID
21 #include "js/RootingAPI.h"  // JS::Handle, JS::MutableHandle, JS::Rooted
22 #include "js/Value.h"       // JS::Value
23 #include "vm/JSContext.h"   // JSContext
24 #include "vm/JSObject.h"    // JSObject
25 #include "vm/StringType.h"  // js::NameToId
26 #include "vm/SymbolType.h"  // JS::Symbol
27 
28 namespace js {
29 
30 class PropertyName;
31 
32 // The functions below are the fundamental operations on objects. See the
33 // comment about "Standard internal methods" in jsapi.h.
34 
35 /*
36  * ES6 [[GetPrototypeOf]]. Get obj's prototype, storing it in protop.
37  *
38  * If obj is definitely not a proxy, the infallible obj->getProto() can be used
39  * instead. See the comment on JSObject::getTaggedProto().
40  */
41 inline bool GetPrototype(JSContext* cx, JS::Handle<JSObject*> obj,
42                          JS::MutableHandle<JSObject*> protop);
43 
44 /*
45  * ES6 [[SetPrototypeOf]]. Change obj's prototype to proto.
46  *
47  * Returns false on error, success of operation in *result. For example, if
48  * obj is not extensible, its prototype is fixed. js::SetPrototype will return
49  * true, because no exception is thrown for this; but *result will be false.
50  */
51 extern bool SetPrototype(JSContext* cx, JS::Handle<JSObject*> obj,
52                          JS::Handle<JSObject*> proto,
53                          JS::ObjectOpResult& result);
54 
55 /* Convenience function: like the above, but throw on failure. */
56 extern bool SetPrototype(JSContext* cx, JS::Handle<JSObject*> obj,
57                          JS::Handle<JSObject*> proto);
58 
59 /*
60  * ES6 [[IsExtensible]]. Extensible objects can have new properties defined on
61  * them. Inextensible objects can't, and their [[Prototype]] slot is fixed as
62  * well.
63  */
64 inline bool IsExtensible(JSContext* cx, JS::Handle<JSObject*> obj,
65                          bool* extensible);
66 
67 /*
68  * ES6 [[PreventExtensions]]. Attempt to change the [[Extensible]] bit on |obj|
69  * to false.  Indicate success or failure through the |result| outparam, or
70  * actual error through the return value.
71  */
72 extern bool PreventExtensions(JSContext* cx, JS::Handle<JSObject*> obj,
73                               JS::ObjectOpResult& result);
74 
75 /* Convenience function. As above, but throw on failure. */
76 extern bool PreventExtensions(JSContext* cx, JS::Handle<JSObject*> obj);
77 
78 /*
79  * ES6 [[GetOwnProperty]]. Get a description of one of obj's own properties.
80  *
81  * If no such property exists on obj, desc will be Nothing().
82  */
83 extern bool GetOwnPropertyDescriptor(
84     JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
85     JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
86 
87 /* ES6 [[DefineOwnProperty]]. Define a property on obj. */
88 extern bool DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
89                            JS::Handle<jsid> id,
90                            Handle<JS::PropertyDescriptor> desc,
91                            JS::ObjectOpResult& result);
92 
93 /*
94  * When the 'result' out-param is omitted, the behavior is the same as above,
95  * except that any failure results in a TypeError.
96  */
97 extern bool DefineProperty(JSContext* cx, JS::Handle<JSObject*> obj,
98                            JS::Handle<jsid> id,
99                            JS::Handle<JS::PropertyDescriptor> desc);
100 
101 extern bool DefineAccessorProperty(JSContext* cx, JS::Handle<JSObject*> obj,
102                                    JS::Handle<jsid> id,
103                                    JS::Handle<JSObject*> getter,
104                                    JS::Handle<JSObject*> setter, unsigned attrs,
105                                    JS::ObjectOpResult& result);
106 
107 extern bool DefineDataProperty(JSContext* cx, JS::Handle<JSObject*> obj,
108                                JS::Handle<jsid> id, JS::Handle<JS::Value> value,
109                                unsigned attrs, JS::ObjectOpResult& result);
110 
111 extern bool DefineAccessorProperty(JSContext* cx, JS::Handle<JSObject*> obj,
112                                    JS::Handle<jsid> id,
113                                    JS::Handle<JSObject*> getter,
114                                    JS::Handle<JSObject*> setter,
115                                    unsigned attrs = JSPROP_ENUMERATE);
116 
117 extern bool DefineDataProperty(JSContext* cx, JS::Handle<JSObject*> obj,
118                                JS::Handle<jsid> id, JS::Handle<JS::Value> value,
119                                unsigned attrs = JSPROP_ENUMERATE);
120 
121 extern bool DefineDataProperty(JSContext* cx, JS::Handle<JSObject*> obj,
122                                PropertyName* name, JS::Handle<JS::Value> value,
123                                unsigned attrs = JSPROP_ENUMERATE);
124 
125 extern bool DefineDataElement(JSContext* cx, JS::Handle<JSObject*> obj,
126                               uint32_t index, JS::Handle<JS::Value> value,
127                               unsigned attrs = JSPROP_ENUMERATE);
128 
129 /*
130  * ES6 [[Has]]. Set *foundp to true if `id in obj` (that is, if obj has an own
131  * or inherited property obj[id]), false otherwise.
132  */
133 inline bool HasProperty(JSContext* cx, JS::Handle<JSObject*> obj,
134                         JS::Handle<jsid> id, bool* foundp);
135 
136 inline bool HasProperty(JSContext* cx, JS::Handle<JSObject*> obj,
137                         PropertyName* name, bool* foundp);
138 
139 /*
140  * ES6 [[Get]]. Get the value of the property `obj[id]`, or undefined if no
141  * such property exists.
142  *
143  * Typically obj == receiver; if obj != receiver then the caller is most likely
144  * a proxy using GetProperty to finish a property get that started out as
145  * `receiver[id]`, and we've already searched the prototype chain up to `obj`.
146  */
147 inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
148                         JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
149                         JS::MutableHandle<JS::Value> vp);
150 
151 inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
152                         JS::Handle<JS::Value> receiver, PropertyName* name,
153                         JS::MutableHandle<JS::Value> vp);
154 
155 inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
156                         JS::Handle<JSObject*> receiver, JS::Handle<jsid> id,
157                         JS::MutableHandle<JS::Value> vp);
158 
159 inline bool GetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
160                         JS::Handle<JSObject*> receiver, PropertyName* name,
161                         JS::MutableHandle<JS::Value> vp);
162 
163 inline bool GetElement(JSContext* cx, JS::Handle<JSObject*> obj,
164                        JS::Handle<JS::Value> receiver, uint32_t index,
165                        JS::MutableHandle<JS::Value> vp);
166 
167 inline bool GetElement(JSContext* cx, JS::Handle<JSObject*> obj,
168                        JS::Handle<JSObject*> receiver, uint32_t index,
169                        JS::MutableHandle<JS::Value> vp);
170 
171 inline bool GetPropertyNoGC(JSContext* cx, JSObject* obj,
172                             const JS::Value& receiver, jsid id, JS::Value* vp);
173 
174 inline bool GetPropertyNoGC(JSContext* cx, JSObject* obj,
175                             const JS::Value& receiver, PropertyName* name,
176                             JS::Value* vp);
177 
178 inline bool GetElementNoGC(JSContext* cx, JSObject* obj,
179                            const JS::Value& receiver, uint32_t index,
180                            JS::Value* vp);
181 
182 // Returns whether |obj| or an object on its proto chain may have an interesting
183 // symbol property (see JSObject::hasInterestingSymbolProperty). If it returns
184 // true, *holder is set to the object that may have this property.
185 MOZ_ALWAYS_INLINE bool MaybeHasInterestingSymbolProperty(
186     JSContext* cx, JSObject* obj, JS::Symbol* symbol,
187     JSObject** holder = nullptr);
188 
189 // Like GetProperty but optimized for interesting symbol properties like
190 // @@toStringTag.
191 MOZ_ALWAYS_INLINE bool GetInterestingSymbolProperty(
192     JSContext* cx, JS::Handle<JSObject*> obj, JS::Symbol* sym,
193     JS::MutableHandle<JS::Value> vp);
194 
195 /*
196  * ES6 [[Set]]. Carry out the assignment `obj[id] = v`.
197  *
198  * The `receiver` argument has to do with how [[Set]] interacts with the
199  * prototype chain and proxies. It's hard to explain and ES6 doesn't really
200  * try. Long story short, if you just want bog-standard assignment, pass
201  * `ObjectValue(*obj)` as receiver. Or better, use one of the signatures that
202  * doesn't have a receiver parameter.
203  *
204  * Callers pass obj != receiver e.g. when a proxy is involved, obj is the
205  * proxy's target, and the proxy is using SetProperty to finish an assignment
206  * that started out as `receiver[id] = v`, by delegating it to obj.
207  */
208 inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
209                         JS::Handle<jsid> id, JS::Handle<JS::Value> v,
210                         JS::Handle<JS::Value> receiver,
211                         JS::ObjectOpResult& result);
212 
213 inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
214                         JS::Handle<jsid> id, JS::Handle<JS::Value> v);
215 
216 inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
217                         PropertyName* name, JS::Handle<JS::Value> v,
218                         JS::Handle<JS::Value> receiver,
219                         JS::ObjectOpResult& result);
220 
221 inline bool SetProperty(JSContext* cx, JS::Handle<JSObject*> obj,
222                         PropertyName* name, JS::Handle<JS::Value> v);
223 
224 inline bool SetElement(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t index,
225                        JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
226                        JS::ObjectOpResult& result);
227 
228 /*
229  * ES6 draft rev 31 (15 Jan 2015) 7.3.3 Put (O, P, V, Throw), except that on
230  * success, the spec says this is supposed to return a boolean value, which we
231  * don't bother doing.
232  */
233 inline bool PutProperty(JSContext* cx, JS::Handle<JSObject*> obj,
234                         JS::Handle<jsid> id, JS::Handle<JS::Value> v,
235                         bool strict);
236 
237 /*
238  * ES6 [[Delete]]. Equivalent to the JS code `delete obj[id]`.
239  */
240 inline bool DeleteProperty(JSContext* cx, JS::Handle<JSObject*> obj,
241                            JS::Handle<jsid> id, JS::ObjectOpResult& result);
242 
243 inline bool DeleteElement(JSContext* cx, JS::Handle<JSObject*> obj,
244                           uint32_t index, JS::ObjectOpResult& result);
245 
246 /*** SpiderMonkey nonstandard internal methods ******************************/
247 
248 /**
249  * If |obj| (underneath any functionally-transparent wrapper proxies) has as
250  * its [[GetPrototypeOf]] trap the ordinary [[GetPrototypeOf]] behavior defined
251  * for ordinary objects, set |*isOrdinary = true| and store |obj|'s prototype
252  * in |result|.  Otherwise set |*isOrdinary = false|.  In case of error, both
253  * outparams have unspecified value.
254  */
255 extern bool GetPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> obj,
256                                    bool* isOrdinary,
257                                    JS::MutableHandle<JSObject*> protop);
258 
259 /*
260  * Attempt to make |obj|'s [[Prototype]] immutable, such that subsequently
261  * trying to change it will not work.  If an internal error occurred,
262  * returns false.  Otherwise, |*succeeded| is set to true iff |obj|'s
263  * [[Prototype]] is now immutable.
264  */
265 extern bool SetImmutablePrototype(JSContext* cx, JS::Handle<JSObject*> obj,
266                                   bool* succeeded);
267 
268 /*
269  * Deprecated. Finds a PropertyDescriptor somewhere along the prototype chain,
270  * similar to GetOwnPropertyDescriptor. |holder| indicates on which object the
271  * property was found.
272  */
273 extern bool GetPropertyDescriptor(
274     JSContext* cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
275     MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
276     JS::MutableHandle<JSObject*> holder);
277 
278 /*
279  * Deprecated. A version of HasProperty that also returns the object on which
280  * the property was found (but that information is unreliable for proxies), and
281  * the Shape of the property, if native.
282  */
283 extern bool LookupProperty(JSContext* cx, JS::Handle<JSObject*> obj,
284                            JS::Handle<jsid> id,
285                            JS::MutableHandle<JSObject*> objp,
286                            PropertyResult* propp);
287 
LookupProperty(JSContext * cx,JS::Handle<JSObject * > obj,PropertyName * name,JS::MutableHandle<JSObject * > objp,PropertyResult * propp)288 inline bool LookupProperty(JSContext* cx, JS::Handle<JSObject*> obj,
289                            PropertyName* name,
290                            JS::MutableHandle<JSObject*> objp,
291                            PropertyResult* propp) {
292   JS::Rooted<jsid> id(cx, NameToId(name));
293   return LookupProperty(cx, obj, id, objp, propp);
294 }
295 
296 /* Set *result to tell whether obj has an own property with the given id. */
297 extern bool HasOwnProperty(JSContext* cx, JS::Handle<JSObject*> obj,
298                            JS::Handle<jsid> id, bool* result);
299 
300 } /* namespace js */
301 
302 #endif /* vm_ObjectOperations_h */
303