MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <math.h>
2#include <string.h>
3
4#include "ant.h"
5#include "errors.h"
6#include "internal.h"
7
8#include "modules/assert.h"
9#include "silver/engine.h"
10
11static ant_value_t assertion_error(ant_t *js, const char *msg, ant_value_t msg_val) {
12 if (vtype(msg_val) == T_STR) {
13 char *s = js_getstr(js, msg_val, NULL);
14 if (s) return js_mkerr(js, "%s", s);
15 }
16 return js_mkerr(js, "%s", msg);
17}
18
19// assert(value, message) / assert.ok
20static ant_value_t assert_ok(ant_t *js, ant_value_t *args, int nargs) {
21 if (nargs < 1 || !js_truthy(js, args[0]))
22 return assertion_error(js, "The expression evaluated to a falsy value", nargs >= 2 ? args[1] : js_mkundef());
23 return js_mkundef();
24}
25
26// assert.fail(message)
27static ant_value_t assert_fail(ant_t *js, ant_value_t *args, int nargs) {
28 if (nargs >= 1 && vtype(args[0]) == T_STR) {
29 char *msg = js_getstr(js, args[0], NULL);
30 if (msg) return js_mkerr(js, "%s", msg);
31 }
32 return js_mkerr(js, "Assertion failed");
33}
34
35// assert.ifError(value)
36static ant_value_t assert_if_error(ant_t *js, ant_value_t *args, int nargs) {
37 if (nargs < 1) return js_mkundef();
38 uint8_t t = vtype(args[0]);
39 if (t == T_NULL || t == T_UNDEF) return js_mkundef();
40 if (is_err(args[0])) return args[0];
41 char *msg = js_getstr(js, args[0], NULL);
42 return js_mkerr(js, "ifError got unwanted exception: %s", msg ? msg : "(unknown)");
43}
44
45static bool values_strict_equal(ant_t *js, ant_value_t a, ant_value_t b) {
46 uint8_t ta = vtype(a), tb = vtype(b);
47 if (ta != tb) return false;
48 if (ta == T_NULL || ta == T_UNDEF) return true;
49 if (ta == T_BOOL) return a == b;
50 if (ta == T_NUM) {
51 double na = js_getnum(a), nb = js_getnum(b);
52 return (na == nb) || (isnan(na) && isnan(nb));
53 }
54 if (ta == T_STR) {
55 char *sa = js_getstr(js, a, NULL);
56 char *sb = js_getstr(js, b, NULL);
57 return sa && sb && strcmp(sa, sb) == 0;
58 }
59 return vdata(a) == vdata(b);
60}
61
62// TODO: make into global helper
63static bool values_loose_equal(ant_t *js, ant_value_t a, ant_value_t b) {
64 uint8_t ta = vtype(a), tb = vtype(b);
65 if (ta == tb) return values_strict_equal(js, a, b);
66 if ((ta == T_NULL && tb == T_UNDEF) || (ta == T_UNDEF && tb == T_NULL)) return true;
67 if (ta == T_NUM && tb == T_STR) {
68 char *sb = js_getstr(js, b, NULL);
69 return sb && js_getnum(a) == strtod(sb, NULL);
70 }
71 if (ta == T_STR && tb == T_NUM) {
72 char *sa = js_getstr(js, a, NULL);
73 return sa && strtod(sa, NULL) == js_getnum(b);
74 }
75 return false;
76}
77
78static bool deep_equal_impl(ant_t *js, ant_value_t a, ant_value_t b, bool strict, int depth) {
79 if (depth > 64) return false;
80 uint8_t ta = vtype(a), tb = vtype(b);
81
82 if (ta == T_ARR && tb == T_ARR) {
83 ant_offset_t la = js_arr_len(js, a), lb = js_arr_len(js, b);
84 if (la != lb) return false;
85 for (ant_offset_t i = 0; i < la; i++) {
86 if (!deep_equal_impl(js, js_arr_get(js, a, i), js_arr_get(js, b, i), strict, depth + 1))
87 return false;
88 }
89 return true;
90 }
91
92 if (ta == T_OBJ && tb == T_OBJ) {
93 if (vdata(a) == vdata(b)) return true;
94 ant_iter_t iter = js_prop_iter_begin(js, a);
95 const char *key; size_t key_len; ant_value_t va;
96 while (js_prop_iter_next(&iter, &key, &key_len, &va)) {
97 ant_value_t vb = js_get(js, b, key);
98 if (!deep_equal_impl(js, va, vb, strict, depth + 1)) {
99 js_prop_iter_end(&iter);
100 return false;
101 }
102 }
103 js_prop_iter_end(&iter);
104 ant_iter_t iter2 = js_prop_iter_begin(js, b);
105 while (js_prop_iter_next(&iter2, &key, &key_len, &va)) {
106 ant_value_t va2 = js_get(js, a, key);
107 if (vtype(va2) == T_UNDEF && vtype(va) != T_UNDEF) {
108 js_prop_iter_end(&iter2);
109 return false;
110 }
111 }
112 js_prop_iter_end(&iter2);
113 return true;
114 }
115
116 return strict ? values_strict_equal(js, a, b) : values_loose_equal(js, a, b);
117}
118
119bool js_deep_equal(ant_t *js, ant_value_t a, ant_value_t b, bool strict) {
120 return deep_equal_impl(js, a, b, strict, 0);
121}
122
123static ant_value_t assert_equal(ant_t *js, ant_value_t *args, int nargs) {
124 if (nargs < 2) return js_mkundef();
125 if (!values_loose_equal(js, args[0], args[1]))
126 return assertion_error(js, "Expected values to be equal", nargs >= 3 ? args[2] : js_mkundef());
127 return js_mkundef();
128}
129
130static ant_value_t assert_not_equal(ant_t *js, ant_value_t *args, int nargs) {
131 if (nargs < 2) return js_mkundef();
132 if (values_loose_equal(js, args[0], args[1]))
133 return assertion_error(js, "Expected values to not be equal", nargs >= 3 ? args[2] : js_mkundef());
134 return js_mkundef();
135}
136
137static ant_value_t assert_strict_equal(ant_t *js, ant_value_t *args, int nargs) {
138 if (nargs < 2) return js_mkundef();
139 if (!values_strict_equal(js, args[0], args[1]))
140 return assertion_error(js, "Expected values to be strictly equal", nargs >= 3 ? args[2] : js_mkundef());
141 return js_mkundef();
142}
143
144static ant_value_t assert_not_strict_equal(ant_t *js, ant_value_t *args, int nargs) {
145 if (nargs < 2) return js_mkundef();
146 if (values_strict_equal(js, args[0], args[1]))
147 return assertion_error(js, "Expected values to not be strictly equal", nargs >= 3 ? args[2] : js_mkundef());
148 return js_mkundef();
149}
150
151static ant_value_t assert_deep_equal(ant_t *js, ant_value_t *args, int nargs) {
152 if (nargs < 2) return js_mkundef();
153 if (!js_deep_equal(js, args[0], args[1], false))
154 return assertion_error(js, "Expected values to be deeply equal", nargs >= 3 ? args[2] : js_mkundef());
155 return js_mkundef();
156}
157
158static ant_value_t assert_not_deep_equal(ant_t *js, ant_value_t *args, int nargs) {
159 if (nargs < 2) return js_mkundef();
160 if (js_deep_equal(js, args[0], args[1], false))
161 return assertion_error(js, "Expected values to not be deeply equal", nargs >= 3 ? args[2] : js_mkundef());
162 return js_mkundef();
163}
164
165static ant_value_t assert_deep_strict_equal(ant_t *js, ant_value_t *args, int nargs) {
166 if (nargs < 2) return js_mkundef();
167 if (!js_deep_equal(js, args[0], args[1], true))
168 return assertion_error(js, "Expected values to be deeply strictly equal", nargs >= 3 ? args[2] : js_mkundef());
169 return js_mkundef();
170}
171
172static ant_value_t assert_not_deep_strict_equal(ant_t *js, ant_value_t *args, int nargs) {
173 if (nargs < 2) return js_mkundef();
174 if (js_deep_equal(js, args[0], args[1], true))
175 return assertion_error(js, "Expected values to not be deeply strictly equal", nargs >= 3 ? args[2] : js_mkundef());
176 return js_mkundef();
177}
178
179static ant_value_t assert_throws(ant_t *js, ant_value_t *args, int nargs) {
180 if (nargs < 1 || vtype(args[0]) != T_FUNC)
181 return js_mkerr(js, "assert.throws: first argument must be a function");
182 ant_value_t result = sv_vm_call(js->vm, js, args[0], js_mkundef(), NULL, 0, NULL, false);
183 if (!is_err(result))
184 return js_mkerr(js, "Missing expected exception");
185 return js_mkundef();
186}
187
188static ant_value_t assert_does_not_throw(ant_t *js, ant_value_t *args, int nargs) {
189 if (nargs < 1 || vtype(args[0]) != T_FUNC)
190 return js_mkerr(js, "assert.doesNotThrow: first argument must be a function");
191 ant_value_t result = sv_vm_call(js->vm, js, args[0], js_mkundef(), NULL, 0, NULL, false);
192 if (is_err(result))
193 return js_mkerr(js, "Got unwanted exception: %s", js_str(js, result));
194 return js_mkundef();
195}
196
197static ant_value_t assert_rejects(ant_t *js, ant_value_t *args, int nargs) {
198 if (nargs < 1) return js_mkerr(js, "assert.rejects: first argument required");
199 ant_value_t promise = js_mkpromise(js);
200 ant_value_t result = vtype(args[0]) == T_FUNC
201 ? sv_vm_call(js->vm, js, args[0], js_mkundef(), NULL, 0, NULL, false)
202 : args[0];
203 if (is_err(result) || promise_was_rejected(result)) {
204 promise_mark_handled(result);
205 js_resolve_promise(js, promise, js_mkundef());
206 } else js_reject_promise(js, promise, js_mkerr(js, "Missing expected rejection"));
207 return promise;
208}
209
210static ant_value_t assert_does_not_reject(ant_t *js, ant_value_t *args, int nargs) {
211 if (nargs < 1) return js_mkerr(js, "assert.doesNotReject: first argument required");
212 ant_value_t promise = js_mkpromise(js);
213 ant_value_t result = vtype(args[0]) == T_FUNC
214 ? sv_vm_call(js->vm, js, args[0], js_mkundef(), NULL, 0, NULL, false)
215 : args[0];
216 if (is_err(result) || promise_was_rejected(result)) {
217 promise_mark_handled(result);
218 js_reject_promise(js, promise, js_mkerr(js, "Got unwanted rejection"));
219 } else js_resolve_promise(js, promise, js_mkundef());
220 return promise;
221}
222
223static ant_value_t assert_match(ant_t *js, ant_value_t *args, int nargs) {
224 if (nargs < 2) return js_mkundef();
225 ant_value_t test_fn = js_getprop_fallback(js, args[1], "test");
226 if (vtype(test_fn) != T_FUNC && vtype(test_fn) != T_CFUNC) return js_mkerr(js, "assert.match: second argument must be a RegExp");
227 ant_value_t test_args[1] = {args[0]};
228 ant_value_t result = sv_vm_call(js->vm, js, test_fn, args[1], test_args, 1, NULL, false);
229 if (!js_truthy(js, result))
230 return assertion_error(js, "Value does not match the regular expression", nargs >= 3 ? args[2] : js_mkundef());
231 return js_mkundef();
232}
233
234static ant_value_t assert_does_not_match(ant_t *js, ant_value_t *args, int nargs) {
235 if (nargs < 2) return js_mkundef();
236 ant_value_t test_fn = js_getprop_fallback(js, args[1], "test");
237 if (vtype(test_fn) != T_FUNC && vtype(test_fn) != T_CFUNC) return js_mkerr(js, "assert.doesNotMatch: second argument must be a RegExp");
238 ant_value_t test_args[1] = {args[0]};
239 ant_value_t result = sv_vm_call(js->vm, js, test_fn, args[1], test_args, 1, NULL, false);
240 if (js_truthy(js, result))
241 return assertion_error(js, "Value matches the regular expression", nargs >= 3 ? args[2] : js_mkundef());
242 return js_mkundef();
243}
244
245static ant_value_t assert_assertion_error_ctor(ant_t *js, ant_value_t *args, int nargs) {
246 ant_value_t self = js_getthis(js);
247 js_set(js, self, "name", js_mkstr(js, "AssertionError", 14));
248 if (nargs >= 1 && vtype(args[0]) == T_OBJ) {
249 const char *fields[] = {"message", "actual", "expected", "operator"};
250 for (int i = 0; i < 4; i++) {
251 ant_value_t v = js_get(js, args[0], fields[i]);
252 if (vtype(v) != T_UNDEF) js_set(js, self, fields[i], v);
253 }
254 }
255 return js_mkundef();
256}
257
258ant_value_t assert_library(ant_t *js) {
259 ant_value_t assert_obj = js_mkobj(js);
260 js_set_slot(assert_obj, SLOT_CFUNC, js_mkfun(assert_ok));
261
262 js_set(js, assert_obj, "ok", js_mkfun(assert_ok));
263 js_set(js, assert_obj, "fail", js_mkfun(assert_fail));
264 js_set(js, assert_obj, "ifError", js_mkfun(assert_if_error));
265 js_set(js, assert_obj, "equal", js_mkfun(assert_equal));
266 js_set(js, assert_obj, "notEqual", js_mkfun(assert_not_equal));
267 js_set(js, assert_obj, "strictEqual", js_mkfun(assert_strict_equal));
268 js_set(js, assert_obj, "notStrictEqual", js_mkfun(assert_not_strict_equal));
269 js_set(js, assert_obj, "deepEqual", js_mkfun(assert_deep_equal));
270 js_set(js, assert_obj, "notDeepEqual", js_mkfun(assert_not_deep_equal));
271 js_set(js, assert_obj, "deepStrictEqual", js_mkfun(assert_deep_strict_equal));
272 js_set(js, assert_obj, "notDeepStrictEqual", js_mkfun(assert_not_deep_strict_equal));
273 js_set(js, assert_obj, "throws", js_mkfun(assert_throws));
274 js_set(js, assert_obj, "doesNotThrow", js_mkfun(assert_does_not_throw));
275 js_set(js, assert_obj, "rejects", js_mkfun(assert_rejects));
276 js_set(js, assert_obj, "doesNotReject", js_mkfun(assert_does_not_reject));
277 js_set(js, assert_obj, "match", js_mkfun(assert_match));
278 js_set(js, assert_obj, "doesNotMatch", js_mkfun(assert_does_not_match));
279
280 ant_value_t ae_ctor = js_mkobj(js);
281 ant_value_t ae_proto = js_mkobj(js);
282
283 js_set(js, ae_proto, "name", js_mkstr(js, "AssertionError", 14));
284 js_set_slot(ae_ctor, SLOT_CFUNC, js_mkfun(assert_assertion_error_ctor));
285 js_mkprop_fast(js, ae_ctor, "prototype", 9, ae_proto);
286 js_mkprop_fast(js, ae_ctor, "name", 4, js_mkstr(js, "AssertionError", 14));
287
288 ant_value_t ae_fn = js_obj_to_func(ae_ctor);
289 js_set(js, ae_proto, "constructor", ae_fn);
290 js_set(js, assert_obj, "AssertionError", ae_fn);
291
292 ant_value_t lib = js_obj_to_func(assert_obj);
293 js_set(js, lib, "default", lib);
294
295 return lib;
296}