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 #include "builtin/Reflect.h"
8 
9 #include "builtin/Array.h"
10 
11 #include "jit/InlinableNatives.h"
12 #include "js/PropertySpec.h"
13 #include "vm/ArgumentsObject.h"
14 #include "vm/JSContext.h"
15 #include "vm/Stack.h"
16 
17 #include "vm/Interpreter-inl.h"
18 
19 using namespace js;
20 
21 /*** Reflect methods ********************************************************/
22 
23 /* ES6 26.1.4 Reflect.deleteProperty (target, propertyKey) */
Reflect_deleteProperty(JSContext * cx,unsigned argc,Value * vp)24 static bool Reflect_deleteProperty(JSContext* cx, unsigned argc, Value* vp) {
25   CallArgs args = CallArgsFromVp(argc, vp);
26 
27   // Step 1.
28   RootedObject target(
29       cx,
30       RequireObjectArg(cx, "`target`", "Reflect.deleteProperty", args.get(0)));
31   if (!target) {
32     return false;
33   }
34 
35   // Steps 2-3.
36   RootedValue propertyKey(cx, args.get(1));
37   RootedId key(cx);
38   if (!ToPropertyKey(cx, propertyKey, &key)) {
39     return false;
40   }
41 
42   // Step 4.
43   ObjectOpResult result;
44   if (!DeleteProperty(cx, target, key, result)) {
45     return false;
46   }
47   args.rval().setBoolean(result.reallyOk());
48   return true;
49 }
50 
51 /* ES6 26.1.8 Reflect.getPrototypeOf(target) */
Reflect_getPrototypeOf(JSContext * cx,unsigned argc,Value * vp)52 bool js::Reflect_getPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
53   CallArgs args = CallArgsFromVp(argc, vp);
54 
55   // Step 1.
56   RootedObject target(
57       cx,
58       RequireObjectArg(cx, "`target`", "Reflect.getPrototypeOf", args.get(0)));
59   if (!target) {
60     return false;
61   }
62 
63   // Step 2.
64   RootedObject proto(cx);
65   if (!GetPrototype(cx, target, &proto)) {
66     return false;
67   }
68   args.rval().setObjectOrNull(proto);
69   return true;
70 }
71 
72 /* ES6 draft 26.1.10 Reflect.isExtensible(target) */
Reflect_isExtensible(JSContext * cx,unsigned argc,Value * vp)73 bool js::Reflect_isExtensible(JSContext* cx, unsigned argc, Value* vp) {
74   CallArgs args = CallArgsFromVp(argc, vp);
75 
76   // Step 1.
77   RootedObject target(
78       cx,
79       RequireObjectArg(cx, "`target`", "Reflect.isExtensible", args.get(0)));
80   if (!target) {
81     return false;
82   }
83 
84   // Step 2.
85   bool extensible;
86   if (!IsExtensible(cx, target, &extensible)) {
87     return false;
88   }
89   args.rval().setBoolean(extensible);
90   return true;
91 }
92 
93 // ES2018 draft rev c164be80f7ea91de5526b33d54e5c9321ed03d3f
94 // 26.1.10 Reflect.ownKeys ( target )
Reflect_ownKeys(JSContext * cx,unsigned argc,Value * vp)95 bool js::Reflect_ownKeys(JSContext* cx, unsigned argc, Value* vp) {
96   CallArgs args = CallArgsFromVp(argc, vp);
97 
98   // Step 1.
99   RootedObject target(
100       cx, RequireObjectArg(cx, "`target`", "Reflect.ownKeys", args.get(0)));
101   if (!target) {
102     return false;
103   }
104 
105   // Steps 2-3.
106   return GetOwnPropertyKeys(
107       cx, target, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, args.rval());
108 }
109 
110 /* ES6 26.1.12 Reflect.preventExtensions(target) */
Reflect_preventExtensions(JSContext * cx,unsigned argc,Value * vp)111 static bool Reflect_preventExtensions(JSContext* cx, unsigned argc, Value* vp) {
112   CallArgs args = CallArgsFromVp(argc, vp);
113 
114   // Step 1.
115   RootedObject target(
116       cx, RequireObjectArg(cx, "`target`", "Reflect.preventExtensions",
117                            args.get(0)));
118   if (!target) {
119     return false;
120   }
121 
122   // Step 2.
123   ObjectOpResult result;
124   if (!PreventExtensions(cx, target, result)) {
125     return false;
126   }
127   args.rval().setBoolean(result.reallyOk());
128   return true;
129 }
130 
131 /* ES6 26.1.13 Reflect.set(target, propertyKey, V [, receiver]) */
Reflect_set(JSContext * cx,unsigned argc,Value * vp)132 static bool Reflect_set(JSContext* cx, unsigned argc, Value* vp) {
133   CallArgs args = CallArgsFromVp(argc, vp);
134 
135   // Step 1.
136   RootedObject target(
137       cx, RequireObjectArg(cx, "`target`", "Reflect.set", args.get(0)));
138   if (!target) {
139     return false;
140   }
141 
142   // Steps 2-3.
143   RootedValue propertyKey(cx, args.get(1));
144   RootedId key(cx);
145   if (!ToPropertyKey(cx, propertyKey, &key)) {
146     return false;
147   }
148 
149   // Step 4.
150   RootedValue receiver(cx, args.length() > 3 ? args[3] : args.get(0));
151 
152   // Step 5.
153   ObjectOpResult result;
154   RootedValue value(cx, args.get(2));
155   if (!SetProperty(cx, target, key, value, receiver, result)) {
156     return false;
157   }
158   args.rval().setBoolean(result.reallyOk());
159   return true;
160 }
161 
162 /*
163  * ES6 26.1.3 Reflect.setPrototypeOf(target, proto)
164  *
165  * The specification is not quite similar enough to Object.setPrototypeOf to
166  * share code.
167  */
Reflect_setPrototypeOf(JSContext * cx,unsigned argc,Value * vp)168 static bool Reflect_setPrototypeOf(JSContext* cx, unsigned argc, Value* vp) {
169   CallArgs args = CallArgsFromVp(argc, vp);
170 
171   // Step 1.
172   RootedObject obj(cx, RequireObjectArg(cx, "`target`",
173                                         "Reflect.setPrototypeOf", args.get(0)));
174   if (!obj) {
175     return false;
176   }
177 
178   // Step 2.
179   if (!args.get(1).isObjectOrNull()) {
180     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
181                               JSMSG_NOT_EXPECTED_TYPE, "Reflect.setPrototypeOf",
182                               "an object or null",
183                               InformalValueTypeName(args.get(1)));
184     return false;
185   }
186   RootedObject proto(cx, args.get(1).toObjectOrNull());
187 
188   // Step 4.
189   ObjectOpResult result;
190   if (!SetPrototype(cx, obj, proto, result)) {
191     return false;
192   }
193   args.rval().setBoolean(result.reallyOk());
194   return true;
195 }
196 
197 static const JSFunctionSpec reflect_methods[] = {
198     JS_SELF_HOSTED_FN("apply", "Reflect_apply", 3, 0),
199     JS_SELF_HOSTED_FN("construct", "Reflect_construct", 2, 0),
200     JS_SELF_HOSTED_FN("defineProperty", "Reflect_defineProperty", 3, 0),
201     JS_FN("deleteProperty", Reflect_deleteProperty, 2, 0),
202     JS_SELF_HOSTED_FN("get", "Reflect_get", 2, 0),
203     JS_SELF_HOSTED_FN("getOwnPropertyDescriptor",
204                       "Reflect_getOwnPropertyDescriptor", 2, 0),
205     JS_INLINABLE_FN("getPrototypeOf", Reflect_getPrototypeOf, 1, 0,
206                     ReflectGetPrototypeOf),
207     JS_SELF_HOSTED_FN("has", "Reflect_has", 2, 0),
208     JS_FN("isExtensible", Reflect_isExtensible, 1, 0),
209     JS_FN("ownKeys", Reflect_ownKeys, 1, 0),
210     JS_FN("preventExtensions", Reflect_preventExtensions, 1, 0),
211     JS_FN("set", Reflect_set, 3, 0),
212     JS_FN("setPrototypeOf", Reflect_setPrototypeOf, 2, 0),
213     JS_FS_END};
214 
215 /*** Setup ******************************************************************/
216 
CreateReflectObject(JSContext * cx,JSProtoKey key)217 static JSObject* CreateReflectObject(JSContext* cx, JSProtoKey key) {
218   Handle<GlobalObject*> global = cx->global();
219   RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
220   if (!proto) {
221     return nullptr;
222   }
223   return NewSingletonObjectWithGivenProto<PlainObject>(cx, proto);
224 }
225 
226 static const ClassSpec ReflectClassSpec = {CreateReflectObject, nullptr,
227                                            reflect_methods, nullptr};
228 
229 const JSClass js::ReflectClass = {"Reflect", 0, JS_NULL_CLASS_OPS,
230                                   &ReflectClassSpec};
231