MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <stdlib.h>
2#include <string.h>
3
4#include "ant.h"
5#include "errors.h"
6#include "runtime.h"
7#include "internal.h"
8#include "silver/engine.h"
9
10#include "modules/reflect.h"
11#include "modules/symbol.h"
12
13static ant_value_t reflect_get(ant_t *js, ant_value_t *args, int nargs) {
14 if (nargs < 2) return js_mkundef();
15
16 ant_value_t target = args[0];
17 ant_value_t key = args[1];
18
19 if (!is_object_type(target)) return js_mkundef();
20
21 ant_value_t prop_key = key;
22 if (is_object_type(prop_key)) {
23 prop_key = js_to_primitive(js, prop_key, 1);
24 if (is_err(prop_key)) return prop_key;
25 }
26
27 if (vtype(prop_key) == T_SYMBOL)
28 return js_get_sym(js, target, prop_key);
29
30 if (vtype(prop_key) != T_STR) {
31 prop_key = js_tostring_val(js, prop_key);
32 if (is_err(prop_key)) return prop_key;
33 }
34
35 char *key_str = js_getstr(js, prop_key, NULL);
36 if (!key_str) return js_mkundef();
37
38 return js_get(js, target, key_str);
39}
40
41static ant_value_t reflect_set(ant_t *js, ant_value_t *args, int nargs) {
42 if (nargs < 3) return js_false;
43
44 ant_value_t target = args[0];
45 ant_value_t key = args[1];
46 ant_value_t value = args[2];
47
48 int t = vtype(target);
49 if (t != T_OBJ && t != T_FUNC) return js_false;
50
51 if (vtype(key) != T_STR) return js_false;
52
53 char *key_str = js_getstr(js, key, NULL);
54 if (!key_str) return js_false;
55
56 js_set(js, target, key_str, value);
57 return js_true;
58}
59
60static ant_value_t reflect_has(ant_t *js, ant_value_t *args, int nargs) {
61 if (nargs < 2) return js_false;
62
63 ant_value_t target = args[0];
64 ant_value_t key = args[1];
65
66 int t = vtype(target);
67 if (t != T_OBJ && t != T_FUNC) return js_false;
68
69 if (vtype(key) != T_STR) return js_false;
70
71 size_t key_len;
72 char *key_str = js_getstr(js, key, &key_len);
73 if (!key_str) return js_false;
74
75 ant_offset_t off = lkp_proto(js, target, key_str, key_len);
76 return js_bool(off > 0);
77}
78
79static ant_value_t reflect_delete_property(ant_t *js, ant_value_t *args, int nargs) {
80 if (nargs < 2) return js_false;
81
82 ant_value_t target = args[0];
83 ant_value_t key = args[1];
84
85 int t = vtype(target);
86 if (t != T_OBJ && t != T_FUNC) return js_false;
87
88 if (vtype(key) != T_STR) return js_false;
89
90 char *key_str = js_getstr(js, key, NULL);
91 if (!key_str) return js_false;
92
93 ant_value_t del_result = js_delete_prop(js, target, key_str, strlen(key_str));
94 bool deleted = !is_err(del_result) && js_truthy(js, del_result);
95 return js_bool(deleted);
96}
97
98static ant_value_t reflect_own_keys(ant_t *js, ant_value_t *args, int nargs) {
99 if (nargs < 1) return js_mkarr(js);
100
101 ant_value_t target = args[0];
102
103 int t = vtype(target);
104 if (t != T_OBJ && t != T_FUNC) {
105 return js_mkerr(js, "Reflect.ownKeys called on non-object");
106 }
107
108 ant_value_t keys_arr = js_mkarr(js);
109 ant_iter_t iter = js_prop_iter_begin(js, target);
110 const char *key; size_t key_len; ant_value_t value;
111
112 while (js_prop_iter_next(&iter, &key, &key_len, &value)) {
113 js_arr_push(js, keys_arr, js_mkstr(js, key, key_len));
114 }
115
116 js_prop_iter_end(&iter);
117 return keys_arr;
118}
119
120static ant_value_t reflect_construct(ant_t *js, ant_value_t *args, int nargs) {
121 if (nargs < 2) {
122 return js_mkerr(js, "Reflect.construct requires at least 2 arguments");
123 }
124
125 ant_value_t target = args[0];
126 ant_value_t args_arr = args[1];
127 ant_value_t new_target = (nargs >= 3) ? args[2] : target;
128
129 if (vtype(target) != T_FUNC && vtype(target) != T_CFUNC) {
130 return js_mkerr(js, "Reflect.construct: first argument must be a constructor");
131 }
132
133 if (vtype(new_target) != T_FUNC && vtype(new_target) != T_CFUNC) {
134 return js_mkerr(js, "Reflect.construct: third argument must be a constructor");
135 }
136
137 ant_value_t length_val = js_get(js, args_arr, "length");
138 int arg_count = 0;
139 if (vtype(length_val) == T_NUM) {
140 arg_count = (int)js_getnum(length_val);
141 }
142
143 ant_value_t *call_args = NULL;
144 if (arg_count > 0) {
145 call_args = malloc(arg_count * sizeof(ant_value_t));
146 if (!call_args) return js_mkerr(js, "Out of memory");
147
148 for (int i = 0; i < arg_count; i++) {
149 char idx[16];
150 snprintf(idx, sizeof(idx), "%d", i);
151 call_args[i] = js_get(js, args_arr, idx);
152 }
153 }
154
155 ant_value_t new_obj = js_mkobj(js);
156 ant_value_t proto = js_get(js, new_target, "prototype");
157 if (vtype(proto) == T_OBJ) js_set_proto_init(new_obj, proto);
158
159 ant_value_t saved_new_target = js->new_target;
160 js->new_target = new_target;
161
162 ant_value_t result = sv_vm_call(js->vm, js, target, new_obj, call_args, arg_count, NULL, true);
163 js->new_target = saved_new_target;
164
165 if (call_args) free(call_args);
166 if (is_object_type(result)) return result;
167
168 return new_obj;
169}
170
171static ant_value_t reflect_apply(ant_t *js, ant_value_t *args, int nargs) {
172 if (nargs < 3) {
173 return js_mkerr(js, "Reflect.apply requires 3 arguments");
174 }
175
176 ant_value_t target = args[0];
177 ant_value_t this_arg = args[1];
178 ant_value_t args_arr = args[2];
179
180 if (vtype(target) != T_FUNC && vtype(target) != T_CFUNC) {
181 return js_mkerr(js, "Reflect.apply: first argument must be a function");
182 }
183
184 if (vtype(args_arr) == T_UNDEF || vtype(args_arr) == T_NULL) return js_mkerr_typed(
185 js, JS_ERR_TYPE,
186 "Reflect.apply: third argument must be an array-like object"
187 );
188
189 ant_value_t result;
190 if (vtype(args_arr) == T_ARR) {
191 ant_value_t *call_args = NULL;
192 int arg_count = extract_array_args(js, args_arr, &call_args);
193 result = sv_vm_call_explicit_this(
194 js->vm, js, target, this_arg,
195 call_args, arg_count
196 );
197 if (call_args) free(call_args);
198 return result;
199 }
200
201 ant_value_t length_val = js_get(js, args_arr, "length");
202 int arg_count = 0;
203 if (vtype(length_val) == T_NUM) {
204 arg_count = (int)js_getnum(length_val);
205 }
206
207 ant_value_t *call_args = NULL;
208 if (arg_count > 0) {
209 call_args = malloc(arg_count * sizeof(ant_value_t));
210 if (!call_args) return js_mkerr(js, "Out of memory");
211
212 for (int i = 0; i < arg_count; i++) {
213 char idx[16];
214 snprintf(idx, sizeof(idx), "%d", i);
215 call_args[i] = js_get(js, args_arr, idx);
216 }
217 }
218
219 result = sv_vm_call_explicit_this(
220 js->vm, js, target, this_arg,
221 call_args, arg_count
222 );
223
224 if (call_args) free(call_args);
225 return result;
226}
227
228static ant_value_t reflect_get_own_property_descriptor(ant_t *js, ant_value_t *args, int nargs) {
229 if (nargs < 2) return js_mkundef();
230
231 ant_value_t target = args[0];
232 ant_value_t key = args[1];
233
234 int t = vtype(target);
235 if (t != T_OBJ && t != T_FUNC) return js_mkundef();
236
237 if (vtype(key) != T_STR) return js_mkundef();
238
239 size_t key_len;
240 char *key_str = js_getstr(js, key, &key_len);
241 if (!key_str) return js_mkundef();
242
243 ant_offset_t off = lkp(js, target, key_str, key_len);
244 if (off <= 0) return js_mkundef();
245
246 ant_value_t value = js_get(js, target, key_str);
247 ant_value_t desc = js_mkobj(js);
248 js_set(js, desc, "value", value);
249 js_set(js, desc, "writable", js_true);
250 js_set(js, desc, "enumerable", js_true);
251 js_set(js, desc, "configurable", js_true);
252
253 return desc;
254}
255
256static ant_value_t reflect_define_property(ant_t *js, ant_value_t *args, int nargs) {
257 if (nargs < 3) return js_false;
258 return js_define_property(js, args[0], args[1], args[2], true);
259}
260
261static ant_value_t reflect_get_prototype_of(ant_t *js, ant_value_t *args, int nargs) {
262 if (nargs < 1) {
263 return js_mkerr(js, "Reflect.getPrototypeOf requires an argument");
264 }
265
266 ant_value_t target = args[0];
267
268 if (!is_object_type(target)) {
269 return js_mkerr(js, "Reflect.getPrototypeOf: argument must be an object");
270 }
271
272 return js_get_proto(js, target);
273}
274
275static ant_value_t reflect_set_prototype_of(ant_t *js, ant_value_t *args, int nargs) {
276 if (nargs < 2) return js_false;
277
278 ant_value_t target = args[0];
279 ant_value_t proto = args[1];
280
281 if (!is_object_type(target)) return js_false;
282 if (!is_object_type(proto) && vtype(proto) != T_NULL) return js_false;
283 if (vtype(proto) != T_NULL && proto_chain_contains(js, proto, target)) return js_false;
284
285 js_set_proto_wb(js, target, proto);
286
287 return js_true;
288}
289
290static ant_value_t reflect_is_extensible(ant_t *js, ant_value_t *args, int nargs) {
291 if (nargs < 1) return js_false;
292
293 ant_value_t target = args[0];
294 int t = vtype(target);
295
296 if (t != T_OBJ && t != T_FUNC) return js_false;
297
298 ant_object_t *obj = js_obj_ptr(js_as_obj(target));
299 if (!obj) return js_false;
300 if (obj->frozen || obj->sealed) return js_false;
301 return js_bool(obj->extensible);
302}
303
304static ant_value_t reflect_prevent_extensions(ant_t *js, ant_value_t *args, int nargs) {
305 if (nargs < 1) return js_false;
306
307 ant_value_t target = args[0];
308 int t = vtype(target);
309
310 if (t != T_OBJ && t != T_FUNC) return js_false;
311
312 ant_object_t *obj = js_obj_ptr(js_as_obj(target));
313 if (!obj) return js_false;
314 obj->extensible = 0;
315 return js_true;
316}
317
318void init_reflect_module(void) {
319 ant_t *js = rt->js;
320 ant_value_t reflect_obj = js_mkobj(js);
321
322 js_set(js, reflect_obj, "get", js_mkfun(reflect_get));
323 js_set(js, reflect_obj, "set", js_mkfun(reflect_set));
324 js_set(js, reflect_obj, "has", js_mkfun(reflect_has));
325 js_set(js, reflect_obj, "deleteProperty", js_mkfun(reflect_delete_property));
326 js_set(js, reflect_obj, "ownKeys", js_mkfun(reflect_own_keys));
327 js_set(js, reflect_obj, "construct", js_mkfun(reflect_construct));
328 js_set(js, reflect_obj, "apply", js_mkfun(reflect_apply));
329 js_set(js, reflect_obj, "getOwnPropertyDescriptor", js_mkfun(reflect_get_own_property_descriptor));
330 js_set(js, reflect_obj, "defineProperty", js_mkfun(reflect_define_property));
331 js_set(js, reflect_obj, "getPrototypeOf", js_mkfun(reflect_get_prototype_of));
332 js_set(js, reflect_obj, "setPrototypeOf", js_mkfun(reflect_set_prototype_of));
333 js_set(js, reflect_obj, "isExtensible", js_mkfun(reflect_is_extensible));
334 js_set(js, reflect_obj, "preventExtensions", js_mkfun(reflect_prevent_extensions));
335
336 js_set_sym(js, reflect_obj, get_toStringTag_sym(), js_mkstr(js, "Reflect", 7));
337 js_set(js, js_glob(js), "Reflect", reflect_obj);
338}