MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at mir/inline-method 296 lines 12 kB view raw
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}