1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 #include "builtin/Reflect.h"
8 
9 #include "jsarray.h"
10 #include "jscntxt.h"
11 
12 #include "vm/ArgumentsObject.h"
13 #include "vm/Stack.h"
14 
15 #include "vm/Interpreter-inl.h"
16 
17 using namespace js;
18 
19 
20 /*** Reflect methods *****************************************************************************/
21 
22 /* ES6 26.1.3 Reflect.defineProperty(target, propertyKey, attributes) */
23 static bool
Reflect_defineProperty(JSContext * cx,unsigned argc,Value * vp)24 Reflect_defineProperty(JSContext* cx, unsigned argc, Value* vp)
25 {
26     CallArgs args = CallArgsFromVp(argc, vp);
27 
28     // Step 1.
29     RootedObject obj(cx, NonNullObject(cx, args.get(0)));
30     if (!obj)
31         return false;
32 
33     // Steps 2-3.
34     RootedValue propertyKey(cx, args.get(1));
35     RootedId key(cx);
36     if (!ToPropertyKey(cx, propertyKey, &key))
37         return false;
38 
39     // Steps 4-5.
40     Rooted<PropertyDescriptor> desc(cx);
41     if (!ToPropertyDescriptor(cx, args.get(2), true, &desc))
42         return false;
43 
44     // Step 6.
45     ObjectOpResult result;
46     if (!DefineProperty(cx, obj, key, desc, result))
47         return false;
48     args.rval().setBoolean(bool(result));
49     return true;
50 }
51 
52 /* ES6 26.1.4 Reflect.deleteProperty (target, propertyKey) */
53 static bool
Reflect_deleteProperty(JSContext * cx,unsigned argc,Value * vp)54 Reflect_deleteProperty(JSContext* cx, unsigned argc, Value* vp)
55 {
56     CallArgs args = CallArgsFromVp(argc, vp);
57 
58     // Step 1.
59     RootedObject target(cx, NonNullObject(cx, args.get(0)));
60     if (!target)
61         return false;
62 
63     // Steps 2-3.
64     RootedValue propertyKey(cx, args.get(1));
65     RootedId key(cx);
66     if (!ToPropertyKey(cx, propertyKey, &key))
67         return false;
68 
69     // Step 4.
70     ObjectOpResult result;
71     if (!DeleteProperty(cx, target, key, result))
72         return false;
73     args.rval().setBoolean(bool(result));
74     return true;
75 }
76 
77 /* ES6 26.1.6 Reflect.get(target, propertyKey [, receiver]) */
78 static bool
Reflect_get(JSContext * cx,unsigned argc,Value * vp)79 Reflect_get(JSContext* cx, unsigned argc, Value* vp)
80 {
81     CallArgs args = CallArgsFromVp(argc, vp);
82 
83     // Step 1.
84     RootedObject obj(cx, NonNullObject(cx, args.get(0)));
85     if (!obj)
86         return false;
87 
88     // Steps 2-3.
89     RootedValue propertyKey(cx, args.get(1));
90     RootedId key(cx);
91     if (!ToPropertyKey(cx, propertyKey, &key))
92         return false;
93 
94     // Step 4.
95     RootedValue receiver(cx, args.length() > 2 ? args[2] : args.get(0));
96 
97     // Step 5.
98     return GetProperty(cx, obj, receiver, key, args.rval());
99 }
100 
101 /* ES6 26.1.7 Reflect.getOwnPropertyDescriptor(target, propertyKey) */
102 static bool
Reflect_getOwnPropertyDescriptor(JSContext * cx,unsigned argc,Value * vp)103 Reflect_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp)
104 {
105     // Step 1.
106     CallArgs args = CallArgsFromVp(argc, vp);
107     if (!NonNullObject(cx, args.get(0)))
108         return false;
109 
110     // The other steps are identical to ES6 draft rev 32 (2015 Feb 2) 19.1.2.6
111     // Object.getOwnPropertyDescriptor.
112     return js::obj_getOwnPropertyDescriptor(cx, argc, vp);
113 }
114 
115 /* ES6 26.1.8 Reflect.getPrototypeOf(target) */
116 bool
Reflect_getPrototypeOf(JSContext * cx,unsigned argc,Value * vp)117 js::Reflect_getPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
118 {
119     CallArgs args = CallArgsFromVp(argc, vp);
120 
121     // Step 1.
122     RootedObject target(cx, NonNullObject(cx, args.get(0)));
123     if (!target)
124         return false;
125 
126     // Step 2.
127     RootedObject proto(cx);
128     if (!GetPrototype(cx, target, &proto))
129         return false;
130     args.rval().setObjectOrNull(proto);
131     return true;
132 }
133 
134 /* ES6 draft 26.1.10 Reflect.isExtensible(target) */
135 bool
Reflect_isExtensible(JSContext * cx,unsigned argc,Value * vp)136 js::Reflect_isExtensible(JSContext* cx, unsigned argc, Value* vp)
137 {
138     CallArgs args = CallArgsFromVp(argc, vp);
139 
140     // Step 1.
141     RootedObject target(cx, NonNullObject(cx, args.get(0)));
142     if (!target)
143         return false;
144 
145     // Step 2.
146     bool extensible;
147     if (!IsExtensible(cx, target, &extensible))
148         return false;
149     args.rval().setBoolean(extensible);
150     return true;
151 }
152 
153 /* ES6 26.1.11 Reflect.ownKeys(target) */
154 static bool
Reflect_ownKeys(JSContext * cx,unsigned argc,Value * vp)155 Reflect_ownKeys(JSContext* cx, unsigned argc, Value* vp)
156 {
157     CallArgs args = CallArgsFromVp(argc, vp);
158 
159     // Step 1.
160     if (!NonNullObject(cx, args.get(0)))
161         return false;
162 
163     // Steps 2-4.
164     return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
165 }
166 
167 /* ES6 26.1.12 Reflect.preventExtensions(target) */
168 static bool
Reflect_preventExtensions(JSContext * cx,unsigned argc,Value * vp)169 Reflect_preventExtensions(JSContext* cx, unsigned argc, Value* vp)
170 {
171     CallArgs args = CallArgsFromVp(argc, vp);
172 
173     // Step 1.
174     RootedObject target(cx, NonNullObject(cx, args.get(0)));
175     if (!target)
176         return false;
177 
178     // Step 2.
179     ObjectOpResult result;
180     if (!PreventExtensions(cx, target, result))
181         return false;
182     args.rval().setBoolean(bool(result));
183     return true;
184 }
185 
186 /* ES6 26.1.13 Reflect.set(target, propertyKey, V [, receiver]) */
187 static bool
Reflect_set(JSContext * cx,unsigned argc,Value * vp)188 Reflect_set(JSContext* cx, unsigned argc, Value* vp)
189 {
190     CallArgs args = CallArgsFromVp(argc, vp);
191 
192     // Step 1.
193     RootedObject target(cx, NonNullObject(cx, args.get(0)));
194     if (!target)
195         return false;
196 
197     // Steps 2-3.
198     RootedValue propertyKey(cx, args.get(1));
199     RootedId key(cx);
200     if (!ToPropertyKey(cx, propertyKey, &key))
201         return false;
202 
203     // Step 4.
204     RootedValue receiver(cx, args.length() > 3 ? args[3] : args.get(0));
205 
206     // Step 5.
207     ObjectOpResult result;
208     RootedValue value(cx, args.get(2));
209     if (!SetProperty(cx, target, key, value, receiver, result))
210         return false;
211     args.rval().setBoolean(bool(result));
212     return true;
213 }
214 
215 /*
216  * ES6 26.1.3 Reflect.setPrototypeOf(target, proto)
217  *
218  * The specification is not quite similar enough to Object.setPrototypeOf to
219  * share code.
220  */
221 static bool
Reflect_setPrototypeOf(JSContext * cx,unsigned argc,Value * vp)222 Reflect_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
223 {
224     CallArgs args = CallArgsFromVp(argc, vp);
225 
226     // Step 1.
227     RootedObject obj(cx, NonNullObject(cx, args.get(0)));
228     if (!obj)
229         return false;
230 
231     // Step 2.
232     if (!args.get(1).isObjectOrNull()) {
233         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
234                                   "Reflect.setPrototypeOf", "an object or null",
235                                   InformalValueTypeName(args.get(1)));
236         return false;
237     }
238     RootedObject proto(cx, args.get(1).toObjectOrNull());
239 
240     // Step 4.
241     ObjectOpResult result;
242     if (!SetPrototype(cx, obj, proto, result))
243         return false;
244     args.rval().setBoolean(bool(result));
245     return true;
246 }
247 
248 static const JSFunctionSpec methods[] = {
249     JS_SELF_HOSTED_FN("apply", "Reflect_apply", 3, 0),
250     JS_SELF_HOSTED_FN("construct", "Reflect_construct", 2, 0),
251     JS_FN("defineProperty", Reflect_defineProperty, 3, 0),
252     JS_FN("deleteProperty", Reflect_deleteProperty, 2, 0),
253     JS_FN("get", Reflect_get, 2, 0),
254     JS_FN("getOwnPropertyDescriptor", Reflect_getOwnPropertyDescriptor, 2, 0),
255     JS_FN("getPrototypeOf", Reflect_getPrototypeOf, 1, 0),
256     JS_SELF_HOSTED_FN("has", "Reflect_has", 2, 0),
257     JS_FN("isExtensible", Reflect_isExtensible, 1, 0),
258     JS_FN("ownKeys", Reflect_ownKeys, 1, 0),
259     JS_FN("preventExtensions", Reflect_preventExtensions, 1, 0),
260     JS_FN("set", Reflect_set, 3, 0),
261     JS_FN("setPrototypeOf", Reflect_setPrototypeOf, 2, 0),
262     JS_FS_END
263 };
264 
265 
266 /*** Setup **************************************************************************************/
267 
268 JSObject*
InitReflect(JSContext * cx,HandleObject obj)269 js::InitReflect(JSContext* cx, HandleObject obj)
270 {
271     RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
272     if (!proto)
273         return nullptr;
274 
275     RootedObject reflect(cx, NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject));
276     if (!reflect)
277         return nullptr;
278     if (!JS_DefineFunctions(cx, reflect, methods))
279         return nullptr;
280 
281     RootedValue value(cx, ObjectValue(*reflect));
282     if (!DefineProperty(cx, obj, cx->names().Reflect, value, nullptr, nullptr, JSPROP_RESOLVING))
283         return nullptr;
284 
285     obj->as<GlobalObject>().setConstructor(JSProto_Reflect, value);
286 
287     return reflect;
288 }
289