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.

add coroutine scope stack management

+193 -27
+2 -2
examples/demo/bunny.js
··· 340 340 } 341 341 bunny.squeak(); 342 342 std.io.println(add(5, 10)); 343 - `; 343 + `.trim(); 344 344 345 - console.log('Source:'); 345 + console.log('Source:\n'); 346 346 console.log(source); 347 347 console.log('\n' + '='.repeat(40) + '\n'); 348 348
+28
examples/spec/async_loops.js
··· 159 159 results.conditionalAwait = v; 160 160 }); 161 161 162 + async function inner(id, val) { 163 + const arr = []; 164 + for (let i = 0; i < 2; i++) { 165 + await delay(1); 166 + arr.push(`${id}:${val}:${i}`); 167 + } 168 + return arr; 169 + } 170 + 171 + async function outer(id) { 172 + const taskResults = []; 173 + for (let j = 0; j < 2; j++) { 174 + await delay(1); 175 + const r = await inner(id, j); 176 + taskResults.push(...r); 177 + } 178 + return taskResults; 179 + } 180 + 181 + Promise.all([outer('A'), outer('B'), outer('C')]).then(([a, b, c]) => { 182 + results.nestedAsyncA = a.join(','); 183 + results.nestedAsyncB = b.join(','); 184 + results.nestedAsyncC = c.join(','); 185 + }); 186 + 162 187 setTimeout(() => { 163 188 test('for-let with async closure', results.forLetClosure, '0,1,2'); 164 189 test('block-scoped capture', results.blockScopedCapture, '0,10,20'); ··· 171 196 test('try-catch in loop', results.tryCatchInLoop, '0,1,2'); 172 197 test('do-while with const', results.doWhile, '0,1,2'); 173 198 test('conditional await', results.conditionalAwait, '0,1,2'); 199 + test('nested async calls A', results.nestedAsyncA, 'A:0:0,A:0:1,A:1:0,A:1:1'); 200 + test('nested async calls B', results.nestedAsyncB, 'B:0:0,B:0:1,B:1:0,B:1:1'); 201 + test('nested async calls C', results.nestedAsyncC, 'C:0:0,C:0:1,C:1:0,C:1:1'); 174 202 summary(); 175 203 }, 200);
+52
examples/spec/crypto.js
··· 1 + import { test, summary } from './helpers.js'; 2 + 3 + console.log('Crypto Tests\n'); 4 + 5 + test('crypto exists', typeof crypto, 'object'); 6 + test('crypto toStringTag', Object.prototype.toString.call(crypto), '[object Crypto]'); 7 + 8 + const rand1 = crypto.random(); 9 + const rand2 = crypto.random(); 10 + test('random returns number', typeof rand1, 'number'); 11 + test('random returns positive', rand1 >= 0, true); 12 + test('random returns different values', rand1 !== rand2, true); 13 + 14 + const bytes = crypto.randomBytes(16); 15 + test('randomBytes returns object', typeof bytes, 'object'); 16 + test('randomBytes length', bytes.length, 16); 17 + test('randomBytes values are numbers', typeof bytes[0], 'number'); 18 + test('randomBytes values in range', bytes[0] >= 0 && bytes[0] <= 255, true); 19 + 20 + const uuid1 = crypto.randomUUID(); 21 + const uuid2 = crypto.randomUUID(); 22 + test('randomUUID returns string', typeof uuid1, 'string'); 23 + test('randomUUID length', uuid1.length, 36); 24 + test('randomUUID format', /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(uuid1), true); 25 + test('randomUUID unique', uuid1 !== uuid2, true); 26 + 27 + const uuidv7_1 = crypto.randomUUIDv7(); 28 + const uuidv7_2 = crypto.randomUUIDv7(); 29 + test('randomUUIDv7 returns string', typeof uuidv7_1, 'string'); 30 + test('randomUUIDv7 length', uuidv7_1.length, 36); 31 + test('randomUUIDv7 version 7', uuidv7_1[14], '7'); 32 + test('randomUUIDv7 unique', uuidv7_1 !== uuidv7_2, true); 33 + test('randomUUIDv7 monotonic', uuidv7_1 < uuidv7_2, true); 34 + 35 + const arr = new Uint8Array(8); 36 + const result = crypto.getRandomValues(arr); 37 + test('getRandomValues returns same array', result === arr, true); 38 + test( 39 + 'getRandomValues fills array', 40 + Array.from(arr).some(v => v !== 0), 41 + true 42 + ); 43 + 44 + const arr32 = new Uint32Array(4); 45 + crypto.getRandomValues(arr32); 46 + test( 47 + 'getRandomValues works with Uint32Array', 48 + Array.from(arr32).some(v => v !== 0), 49 + true 50 + ); 51 + 52 + summary();
+21
examples/spec/performance.js
··· 1 + import { test, summary } from './helpers.js'; 2 + 3 + console.log('Performance Tests\n'); 4 + 5 + test('performance exists', typeof performance, 'object'); 6 + test('performance toStringTag', Object.prototype.toString.call(performance), '[object Performance]'); 7 + 8 + const t1 = performance.now(); 9 + test('now returns number', typeof t1, 'number'); 10 + test('now returns positive', t1 >= 0, true); 11 + 12 + let sum = 0; 13 + for (let i = 0; i < 10000; i++) sum += i; 14 + const t2 = performance.now(); 15 + test('now advances', t2 >= t1, true); 16 + 17 + test('timeOrigin exists', typeof performance.timeOrigin, 'number'); 18 + test('timeOrigin positive', performance.timeOrigin > 0, true); 19 + test('timeOrigin before now', performance.timeOrigin <= Date.now(), true); 20 + 21 + summary();
+36
examples/spec/shell.js
··· 1 + import { test, summary } from './helpers.js'; 2 + import { $ } from 'ant:shell'; 3 + 4 + console.log('Shell Tests\n'); 5 + 6 + const result = $`echo hello`; 7 + test('shell returns object', typeof result, 'object'); 8 + test('shell has exitCode', typeof result.exitCode, 'number'); 9 + test('shell exitCode success', result.exitCode, 0); 10 + test('shell has text method', typeof result.text, 'function'); 11 + test('shell has lines method', typeof result.lines, 'function'); 12 + 13 + test('shell text returns string', typeof result.text(), 'string'); 14 + test('shell text content', result.text(), 'hello'); 15 + 16 + const lines = result.lines(); 17 + test('shell lines returns array', Array.isArray(lines), true); 18 + test('shell lines content', lines[0], 'hello'); 19 + 20 + const multiResult = $`printf "line1\nline2\nline3"`; 21 + const multiLines = multiResult.lines(); 22 + test('shell multi-line count', multiLines.length, 3); 23 + test('shell multi-line first', multiLines[0], 'line1'); 24 + test('shell multi-line last', multiLines[2], 'line3'); 25 + 26 + const name = 'world'; 27 + const interpResult = $`echo hello ${name}`; 28 + test('shell interpolation', interpResult.text(), 'hello world'); 29 + 30 + const failResult = $`exit 1`; 31 + test('shell exit code failure', failResult.exitCode, 1); 32 + 33 + const strResult = $('echo string arg'); 34 + test('shell string arg', strResult.text(), 'string arg'); 35 + 36 + summary();
+54 -25
src/ant.c
··· 117 117 struct for_let_ctx *for_let_stack; 118 118 int for_let_stack_len; 119 119 int for_let_stack_cap; 120 + UT_array *scope_stack; // coroutine's own scope stack 120 121 } coroutine_t; 121 122 122 123 typedef struct { ··· 914 915 static void free_coroutine(coroutine_t *coro); 915 916 static void for_let_swap_with_coro(struct js *js, coroutine_t *coro); 916 917 918 + typedef struct { 919 + jsval_t scope; 920 + UT_array *scope_stack; 921 + } coro_saved_state_t; 922 + 923 + static inline coro_saved_state_t coro_enter(struct js *js, coroutine_t *coro) { 924 + extern UT_array *global_scope_stack; 925 + coro_saved_state_t saved = { js->scope, global_scope_stack }; 926 + js->scope = coro->scope; 927 + global_scope_stack = coro->scope_stack; 928 + for_let_swap_with_coro(js, coro); 929 + return saved; 930 + } 931 + 932 + static inline void coro_leave(struct js *js, coroutine_t *coro, coro_saved_state_t saved) { 933 + extern UT_array *global_scope_stack; 934 + coro->scope = js->scope; 935 + coro->scope_stack = global_scope_stack; 936 + js->scope = saved.scope; 937 + global_scope_stack = saved.scope_stack; 938 + for_let_swap_with_coro(js, coro); 939 + } 940 + 917 941 static size_t calculate_coro_stack_size(void) { 918 942 if (coro_stack_size_initialized) return 0; 919 943 coro_stack_size_initialized = true; ··· 1018 1042 if (!temp->is_ready || !temp->mco || mco_status(temp->mco) != MCO_SUSPENDED) continue; 1019 1043 remove_coroutine(temp); 1020 1044 1021 - jsval_t saved_scope = temp->js->scope; 1022 - temp->js->scope = temp->scope; 1023 - 1024 - for_let_swap_with_coro(temp->js, temp); 1045 + coro_saved_state_t saved = coro_enter(temp->js, temp); 1025 1046 mco_result res = mco_resume(temp->mco); 1026 - 1027 - temp->scope = temp->js->scope; 1028 - temp->js->scope = saved_scope; 1029 - for_let_swap_with_coro(temp->js, temp); 1047 + coro_leave(temp->js, temp, saved); 1030 1048 1031 1049 if (res == MCO_SUCCESS && mco_status(temp->mco) != MCO_DEAD) { 1032 1050 temp->is_ready = false; ··· 1144 1162 coro->for_let_stack_len = 0; 1145 1163 coro->for_let_stack_cap = 0; 1146 1164 1165 + extern UT_array *global_scope_stack; 1166 + utarray_new(coro->scope_stack, &jsoff_icd); 1167 + jsoff_t glob_off = (jsoff_t)vdata(js_glob(js)); 1168 + utarray_push_back(coro->scope_stack, &glob_off); 1169 + 1147 1170 ctx->coro = coro; 1148 1171 enqueue_coroutine(coro); 1149 1172 1150 - jsval_t saved_scope = js->scope; 1151 - js->scope = coro->scope; 1152 - for_let_swap_with_coro(js, coro); 1173 + coro_saved_state_t saved = coro_enter(js, coro); 1153 1174 res = mco_resume(mco); 1154 - 1155 - coro->scope = js->scope; 1156 - js->scope = saved_scope; 1157 - for_let_swap_with_coro(js, coro); 1175 + coro_leave(js, coro, saved); 1158 1176 1159 1177 if (res != MCO_SUCCESS && mco_status(mco) != MCO_DEAD) { 1160 1178 remove_coroutine(coro); ··· 1182 1200 } 1183 1201 if (coro->args) CORO_FREE(coro->args); 1184 1202 if (coro->for_let_stack) free(coro->for_let_stack); 1203 + if (coro->scope_stack) utarray_free(coro->scope_stack); 1185 1204 CORO_FREE(coro); 1186 1205 } 1187 1206 } ··· 4619 4638 jsoff_t prev = (jsoff_t) vdata(js->scope); 4620 4639 utarray_push_back(global_scope_stack, &prev); 4621 4640 js->scope = mkobj(js, prev); 4622 - 4623 4641 return js->scope; 4624 4642 } 4625 4643 ··· 6976 6994 } 6977 6995 skip_fields: 6978 6996 if (is_async) { 6979 - res = start_async_in_coroutine(js, code_str, fnlen, closure_scope, bound_args, bound_argc); 6980 - } else { 6981 - res = call_js_internal(js, code_str, fnlen, closure_scope, bound_args, bound_argc, func); 6982 - } 6997 + UT_array *call_args; 6998 + utarray_new(call_args, &jsval_icd); 6999 + for (int i = 0; i < bound_argc; i++) utarray_push_back(call_args, &bound_args[i]); 7000 + jsval_t err; 7001 + int call_argc = parse_call_args(js, call_args, &err); 7002 + if (call_argc < 0) { 7003 + utarray_free(call_args); 7004 + pop_call_frame(); 7005 + if (bound_args) ANT_GC_FREE(bound_args); 7006 + js->super_val = saved_super; 7007 + js->current_func = saved_func; 7008 + return err; 7009 + } 7010 + jsval_t *argv = (jsval_t *)utarray_front(call_args); 7011 + int argc = (int)utarray_len(call_args); 7012 + res = start_async_in_coroutine(js, code_str, fnlen, closure_scope, argv, argc); 7013 + utarray_free(call_args); 7014 + } else res = call_js_internal(js, code_str, fnlen, closure_scope, bound_args, bound_argc, func); 6983 7015 pop_call_frame(); 6984 7016 if (bound_args) ANT_GC_FREE(bound_args); 6985 7017 js->super_val = saved_super; ··· 22572 22604 FWD_VAL(coro->for_let_stack[i].body_scope); 22573 22605 FWD_OFF(coro->for_let_stack[i].prop_off); 22574 22606 } 22607 + if (coro->scope_stack) UTARRAY_EACH(coro->scope_stack, jsoff_t, off) FWD_OFF(*off); 22575 22608 if (coro->mco) { 22576 22609 async_exec_context_t *actx = (async_exec_context_t *)mco_get_user_data(coro->mco); 22577 - if (actx) { 22578 - FWD_VAL(actx->closure_scope); 22579 - FWD_VAL(actx->result); 22580 - FWD_VAL(actx->promise); 22581 - } 22610 + if (actx) { FWD_VAL(actx->closure_scope); FWD_VAL(actx->result); FWD_VAL(actx->promise); } 22582 22611 } 22583 22612 } 22584 22613