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.

make promise api based on coroutines

+504 -52
+4 -1
include/ant.h
··· 66 66 67 67 jsval_t js_mkpromise(struct js *js); 68 68 void js_resolve_promise(struct js *js, jsval_t promise, jsval_t value); 69 - void js_reject_promise(struct js *js, jsval_t promise, jsval_t value); 69 + void js_reject_promise(struct js *js, jsval_t promise, jsval_t value); 70 + 71 + int js_has_pending_coroutines(void); 72 + void js_process_coroutines(struct js *js);
+1 -1
include/modules/timer.h
··· 4 4 #include <stdint.h> 5 5 #include "ant.h" 6 6 7 - void init_timer_module(struct js *js, jsval_t ant_obj); 7 + void init_timer_module(struct js *js); 8 8 void process_timers(struct js *js); 9 9 void process_microtasks(struct js *js); 10 10 void queue_microtask(struct js *js, jsval_t callback);
+1 -1
meson.build
··· 41 41 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 42 42 43 43 version_conf = configuration_data() 44 - version_conf.set('ANT_VERSION', '0.0.5.39') 44 + version_conf.set('ANT_VERSION', '0.0.5.40') 45 45 version_conf.set('ANT_GIT_HASH', git_hash) 46 46 version_conf.set('ANT_BUILD_DATE', build_date) 47 47
+394 -23
src/ant.c
··· 37 37 int capacity; 38 38 } call_stack_t; 39 39 40 + // Coroutine support for async/await and generators 41 + typedef enum { 42 + CORO_ASYNC_AWAIT, // Async function waiting on promise 43 + CORO_GENERATOR, // Generator function (function*) 44 + CORO_ASYNC_GENERATOR // Async generator (async function*) 45 + } coroutine_type_t; 46 + 47 + typedef struct coroutine { 48 + struct js *js; // JS context 49 + coroutine_type_t type; // Type of coroutine 50 + jsval_t scope; // Captured scope 51 + jsval_t this_val; // Captured 'this' 52 + jsval_t awaited_promise; // Promise we're waiting on (for async) 53 + jsval_t result; // Result when resumed 54 + jsval_t async_func; // The async/generator function being executed 55 + jsval_t *args; // Function arguments 56 + int nargs; // Number of arguments 57 + bool is_settled; // Whether the awaited promise is settled 58 + bool is_error; // Whether promise was rejected 59 + bool is_done; // Whether generator is exhausted 60 + jsoff_t resume_point; // Code position to resume at 61 + jsval_t yield_value; // Value yielded by generator 62 + struct coroutine *next; // Next in scheduler queue 63 + } coroutine_t; 64 + 65 + typedef struct { 66 + coroutine_t *head; 67 + coroutine_t *tail; 68 + } coroutine_queue_t; 69 + 40 70 static this_stack_t global_this_stack = {NULL, 0, 0}; 41 71 static call_stack_t global_call_stack = {NULL, 0, 0}; 72 + static coroutine_queue_t pending_coroutines = {NULL, NULL}; 42 73 43 74 struct js { 44 75 jsoff_t css; // max observed C stack size ··· 94 125 95 126 enum { 96 127 T_OBJ, T_PROP, T_STR, T_UNDEF, T_NULL, T_NUM, 97 - T_BOOL, T_FUNC, T_CODEREF, T_CFUNC, T_ERR, T_ARR, T_PROMISE 128 + T_BOOL, T_FUNC, T_CODEREF, T_CFUNC, T_ERR, T_ARR, T_PROMISE, T_GENERATOR 98 129 }; 99 130 100 131 static const char *typestr(uint8_t t) { 101 132 const char *names[] = { 102 133 "object", "prop", "string", "undefined", "null", "number", 103 - "boolean", "function", "coderef", "cfunc", "err", "array", "promise" 134 + "boolean", "function", "coderef", "cfunc", "err", "array", "promise", "generator" 104 135 }; 105 136 106 137 return (t < sizeof(names) / sizeof(names[0])) ? names[t] : "??"; ··· 178 209 static jsval_t builtin_Promise_resolve(struct js *js, jsval_t *args, int nargs); 179 210 static jsval_t builtin_Promise_reject(struct js *js, jsval_t *args, int nargs); 180 211 static jsval_t builtin_Promise_try(struct js *js, jsval_t *args, int nargs); 212 + static jsval_t builtin_Promise_all(struct js *js, jsval_t *args, int nargs); 213 + static jsval_t builtin_Promise_race(struct js *js, jsval_t *args, int nargs); 181 214 static jsval_t builtin_promise_then(struct js *js, jsval_t *args, int nargs); 182 215 static jsval_t builtin_promise_catch(struct js *js, jsval_t *args, int nargs); 183 216 static jsval_t builtin_promise_finally(struct js *js, jsval_t *args, int nargs); 184 217 static jsval_t builtin_Date(struct js *js, jsval_t *args, int nargs); 185 218 static jsval_t builtin_Date_now(struct js *js, jsval_t *args, int nargs); 219 + 220 + static coroutine_t *create_coroutine(struct js *js, jsval_t promise, jsval_t async_func) { 221 + coroutine_t *coro = (coroutine_t *)malloc(sizeof(coroutine_t)); 222 + if (!coro) return NULL; 223 + 224 + coro->js = js; 225 + coro->type = CORO_ASYNC_AWAIT; 226 + coro->scope = js->scope; 227 + coro->this_val = js->this_val; 228 + coro->awaited_promise = promise; 229 + coro->result = js_mkundef(); 230 + coro->async_func = async_func; 231 + coro->args = NULL; 232 + coro->nargs = 0; 233 + coro->is_settled = false; 234 + coro->is_error = false; 235 + coro->is_done = false; 236 + coro->resume_point = 0; 237 + coro->yield_value = js_mkundef(); 238 + coro->next = NULL; 239 + 240 + return coro; 241 + } 242 + 243 + static coroutine_t *create_generator(struct js *js, jsval_t gen_func, jsval_t *args, int nargs, bool is_async) { 244 + coroutine_t *coro = (coroutine_t *)malloc(sizeof(coroutine_t)); 245 + if (!coro) return NULL; 246 + 247 + coro->js = js; 248 + coro->type = is_async ? CORO_ASYNC_GENERATOR : CORO_GENERATOR; 249 + coro->scope = js->scope; 250 + coro->this_val = js->this_val; 251 + coro->awaited_promise = js_mkundef(); 252 + coro->result = js_mkundef(); 253 + coro->async_func = gen_func; 254 + 255 + if (nargs > 0) { 256 + coro->args = (jsval_t *)malloc(sizeof(jsval_t) * nargs); 257 + if (coro->args) { 258 + memcpy(coro->args, args, sizeof(jsval_t) * nargs); 259 + } 260 + } else { 261 + coro->args = NULL; 262 + } 263 + coro->nargs = nargs; 264 + 265 + coro->is_settled = false; 266 + coro->is_error = false; 267 + coro->is_done = false; 268 + coro->resume_point = 0; 269 + coro->yield_value = js_mkundef(); 270 + coro->next = NULL; 271 + 272 + return coro; 273 + } 274 + 275 + static void enqueue_coroutine(coroutine_t *coro) { 276 + if (!coro) return; 277 + 278 + if (pending_coroutines.tail) { 279 + pending_coroutines.tail->next = coro; 280 + pending_coroutines.tail = coro; 281 + } else { 282 + pending_coroutines.head = coro; 283 + pending_coroutines.tail = coro; 284 + } 285 + } 286 + 287 + static coroutine_t *dequeue_coroutine(void) { 288 + coroutine_t *coro = pending_coroutines.head; 289 + if (coro) { 290 + pending_coroutines.head = coro->next; 291 + if (!pending_coroutines.head) { 292 + pending_coroutines.tail = NULL; 293 + } 294 + coro->next = NULL; 295 + } 296 + return coro; 297 + } 298 + 299 + static bool has_pending_coroutines(void) { 300 + return pending_coroutines.head != NULL; 301 + } 302 + 303 + static void free_coroutine(coroutine_t *coro) { 304 + if (coro) { 305 + if (coro->args) free(coro->args); 306 + free(coro); 307 + } 308 + } 309 + 310 + static jsval_t resume_coroutine_wrapper(struct js *js, jsval_t *args, int nargs); 311 + static jsval_t reject_coroutine_wrapper(struct js *js, jsval_t *args, int nargs); 186 312 187 313 static void setlwm(struct js *js) { 188 314 jsoff_t n = 0, css = 0; ··· 2483 2609 if (vtype(resolved) != T_PROMISE) { 2484 2610 return resolved; 2485 2611 } 2612 + 2486 2613 jsval_t p_obj = mkval(T_OBJ, vdata(resolved)); 2487 2614 jsoff_t state_off = lkp(js, p_obj, "__state", 7); 2488 2615 if (state_off == 0) return js_mkerr(js, "invalid promise state"); 2489 2616 2490 - int max_iterations = 10000; 2491 - int iterations = 0; 2492 - while (iterations < max_iterations) { 2493 - int state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); 2494 - if (state != 0) break; 2495 - if (!has_pending_microtasks()) break; 2496 - 2617 + int state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); 2618 + 2619 + if (state != 0) { 2620 + jsoff_t val_off = lkp(js, p_obj, "__value", 7); 2621 + if (val_off == 0) return js_mkerr(js, "invalid promise value"); 2622 + jsval_t val = resolveprop(js, mkval(T_PROP, val_off)); 2623 + if (state == 1) { 2624 + return val; 2625 + } else if (state == 2) { 2626 + return js_throw(js, val); 2627 + } 2628 + } 2629 + 2630 + coroutine_t *coro = create_coroutine(js, resolved, js->current_func); 2631 + if (!coro) return js_mkerr(js, "failed to create coroutine"); 2632 + 2633 + jsval_t resume_obj = mkobj(js, 0); 2634 + setprop(js, resume_obj, js_mkstr(js, "__native_func", 13), js_mkfun(resume_coroutine_wrapper)); 2635 + setprop(js, resume_obj, js_mkstr(js, "__coroutine", 11), tov((double)(uintptr_t)coro)); 2636 + jsval_t resume_fn = mkval(T_FUNC, vdata(resume_obj)); 2637 + 2638 + jsval_t reject_obj = mkobj(js, 0); 2639 + setprop(js, reject_obj, js_mkstr(js, "__native_func", 13), js_mkfun(reject_coroutine_wrapper)); 2640 + setprop(js, reject_obj, js_mkstr(js, "__coroutine", 11), tov((double)(uintptr_t)coro)); 2641 + jsval_t reject_fn = mkval(T_FUNC, vdata(reject_obj)); 2642 + 2643 + jsval_t then_args[] = { resume_fn, reject_fn }; 2644 + jsval_t saved_this = js->this_val; 2645 + js->this_val = resolved; 2646 + builtin_promise_then(js, then_args, 2); 2647 + js->this_val = saved_this; 2648 + 2649 + while (!coro->is_settled) { 2497 2650 process_microtasks(js); 2498 - iterations++; 2651 + 2652 + while (has_pending_coroutines()) { 2653 + coroutine_t *resumed = dequeue_coroutine(); 2654 + if (resumed == coro) { 2655 + jsval_t result = resumed->result; 2656 + bool is_error = resumed->is_error; 2657 + free_coroutine(resumed); 2658 + 2659 + if (is_error) { 2660 + return js_throw(js, result); 2661 + } 2662 + return result; 2663 + } 2664 + enqueue_coroutine(resumed); 2665 + } 2666 + 2667 + if (!has_pending_microtasks() && has_pending_timers()) { 2668 + int64_t next_timeout = get_next_timer_timeout(); 2669 + if (next_timeout <= 0) { 2670 + process_timers(js); 2671 + continue; 2672 + } 2673 + } 2499 2674 2500 - if (js->flags & F_THROW) return mkval(T_ERR, 0); 2501 - state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); 2502 - if (state != 0) break; 2675 + if (!has_pending_microtasks() && !has_pending_coroutines() && !has_pending_timers()) { 2676 + free_coroutine(coro); 2677 + return js_mkerr(js, "await: promise never settled"); 2678 + } 2503 2679 } 2504 2680 2505 - int state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); 2506 - jsoff_t val_off = lkp(js, p_obj, "__value", 7); 2507 - if (val_off == 0) return js_mkerr(js, "invalid promise value"); 2508 - jsval_t val = resolveprop(js, mkval(T_PROP, val_off)); 2509 - if (state == 1) { 2510 - return val; 2511 - } else if (state == 2) { 2512 - return js_mkerr(js, "await: promise rejected"); 2513 - } else { 2514 - return js_mkerr(js, "await: promise not resolved after processing microtasks"); 2681 + jsval_t result = coro->result; 2682 + bool is_error = coro->is_error; 2683 + free_coroutine(coro); 2684 + 2685 + if (is_error) { 2686 + return js_throw(js, result); 2515 2687 } 2688 + return result; 2516 2689 } else if (next(js) == TOK_NOT || js->tok == TOK_TILDA || js->tok == TOK_TYPEOF || 2517 2690 js->tok == TOK_VOID || js->tok == TOK_MINUS || js->tok == TOK_PLUS) { 2518 2691 uint8_t t = js->tok; ··· 4768 4941 return builtin_Promise_resolve(js, res_args, 1); 4769 4942 } 4770 4943 4944 + static jsval_t resume_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { 4945 + jsval_t me = js->current_func; 4946 + jsval_t coro_val = js_get(js, me, "__coroutine"); 4947 + if (vtype(coro_val) != T_NUM) return js_mkundef(); 4948 + 4949 + coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); 4950 + if (!coro) return js_mkundef(); 4951 + 4952 + coro->result = nargs > 0 ? args[0] : js_mkundef(); 4953 + coro->is_settled = true; 4954 + coro->is_error = false; 4955 + 4956 + enqueue_coroutine(coro); 4957 + return js_mkundef(); 4958 + } 4959 + 4960 + static jsval_t reject_coroutine_wrapper(struct js *js, jsval_t *args, int nargs) { 4961 + jsval_t me = js->current_func; 4962 + jsval_t coro_val = js_get(js, me, "__coroutine"); 4963 + 4964 + if (vtype(coro_val) != T_NUM) return js_mkundef(); 4965 + 4966 + coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); 4967 + if (!coro) return js_mkundef(); 4968 + 4969 + coro->result = nargs > 0 ? args[0] : js_mkundef(); 4970 + coro->is_settled = true; 4971 + coro->is_error = true; 4972 + 4973 + enqueue_coroutine(coro); 4974 + return js_mkundef(); 4975 + } 4976 + 4977 + static jsval_t builtin_Promise_all_resolve_handler(struct js *js, jsval_t *args, int nargs) { 4978 + jsval_t me = js->current_func; 4979 + jsval_t tracker = js_get(js, me, "tracker"); 4980 + jsval_t index_val = js_get(js, me, "index"); 4981 + 4982 + int index = (int)tod(index_val); 4983 + jsval_t value = nargs > 0 ? args[0] : js_mkundef(); 4984 + 4985 + jsval_t results = js_get(js, tracker, "results"); 4986 + char idx[16]; 4987 + snprintf(idx, sizeof(idx), "%d", index); 4988 + setprop(js, results, js_mkstr(js, idx, strlen(idx)), value); 4989 + 4990 + jsval_t remaining_val = js_get(js, tracker, "remaining"); 4991 + int remaining = (int)tod(remaining_val) - 1; 4992 + setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)remaining)); 4993 + 4994 + if (remaining == 0) { 4995 + jsval_t result_promise = js_get(js, tracker, "promise"); 4996 + resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); 4997 + } 4998 + 4999 + return js_mkundef(); 5000 + } 5001 + 5002 + static jsval_t builtin_Promise_all_reject_handler(struct js *js, jsval_t *args, int nargs) { 5003 + jsval_t me = js->current_func; 5004 + jsval_t tracker = js_get(js, me, "tracker"); 5005 + jsval_t result_promise = js_get(js, tracker, "promise"); 5006 + 5007 + jsval_t reason = nargs > 0 ? args[0] : js_mkundef(); 5008 + reject_promise(js, result_promise, reason); 5009 + 5010 + return js_mkundef(); 5011 + } 5012 + 5013 + static jsval_t builtin_Promise_all(struct js *js, jsval_t *args, int nargs) { 5014 + if (nargs < 1) return js_mkerr(js, "Promise.all requires an array"); 5015 + 5016 + jsval_t arr = args[0]; 5017 + if (vtype(arr) != T_ARR) return js_mkerr(js, "Promise.all requires an array"); 5018 + 5019 + jsoff_t len_off = lkp(js, arr, "length", 6); 5020 + if (len_off == 0) return builtin_Promise_resolve(js, NULL, 0); 5021 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 5022 + int len = (int)tod(len_val); 5023 + 5024 + if (len == 0) { 5025 + jsval_t empty_arr = mkarr(js); 5026 + setprop(js, empty_arr, js_mkstr(js, "length", 6), tov(0.0)); 5027 + jsval_t resolve_args[] = { mkval(T_ARR, vdata(empty_arr)) }; 5028 + return builtin_Promise_resolve(js, resolve_args, 1); 5029 + } 5030 + 5031 + jsval_t result_promise = mkpromise(js); 5032 + 5033 + jsval_t tracker = mkobj(js, 0); 5034 + setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len)); 5035 + setprop(js, tracker, js_mkstr(js, "results", 7), mkarr(js)); 5036 + setprop(js, tracker, js_mkstr(js, "promise", 7), result_promise); 5037 + 5038 + jsval_t results = resolveprop(js, js_get(js, tracker, "results")); 5039 + setprop(js, results, js_mkstr(js, "length", 6), tov((double)len)); 5040 + 5041 + for (int i = 0; i < len; i++) { 5042 + char idx[16]; 5043 + snprintf(idx, sizeof(idx), "%d", i); 5044 + jsval_t item = resolveprop(js, js_get(js, arr, idx)); 5045 + 5046 + if (vtype(item) != T_PROMISE) { 5047 + jsval_t wrap_args[] = { item }; 5048 + item = builtin_Promise_resolve(js, wrap_args, 1); 5049 + } 5050 + 5051 + jsval_t resolve_obj = mkobj(js, 0); 5052 + setprop(js, resolve_obj, js_mkstr(js, "__native_func", 13), js_mkfun(builtin_Promise_all_resolve_handler)); 5053 + setprop(js, resolve_obj, js_mkstr(js, "index", 5), tov((double)i)); 5054 + setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), tracker); 5055 + jsval_t resolve_fn = mkval(T_FUNC, vdata(resolve_obj)); 5056 + 5057 + jsval_t reject_obj = mkobj(js, 0); 5058 + setprop(js, reject_obj, js_mkstr(js, "__native_func", 13), js_mkfun(builtin_Promise_all_reject_handler)); 5059 + setprop(js, reject_obj, js_mkstr(js, "tracker", 7), tracker); 5060 + jsval_t reject_fn = mkval(T_FUNC, vdata(reject_obj)); 5061 + 5062 + jsval_t then_args[] = { resolve_fn, reject_fn }; 5063 + jsval_t saved_this = js->this_val; 5064 + js->this_val = item; 5065 + builtin_promise_then(js, then_args, 2); 5066 + js->this_val = saved_this; 5067 + } 5068 + 5069 + return result_promise; 5070 + } 5071 + 5072 + static jsval_t builtin_Promise_race(struct js *js, jsval_t *args, int nargs) { 5073 + if (nargs < 1) return js_mkerr(js, "Promise.race requires an array"); 5074 + 5075 + jsval_t arr = args[0]; 5076 + if (vtype(arr) != T_ARR) return js_mkerr(js, "Promise.race requires an array"); 5077 + 5078 + jsoff_t len_off = lkp(js, arr, "length", 6); 5079 + if (len_off == 0) return mkpromise(js); 5080 + jsval_t len_val = resolveprop(js, mkval(T_PROP, len_off)); 5081 + int len = (int)tod(len_val); 5082 + 5083 + if (len == 0) return mkpromise(js); 5084 + jsval_t result_promise = mkpromise(js); 5085 + 5086 + jsval_t resolve_obj = mkobj(js, 0); 5087 + setprop(js, resolve_obj, js_mkstr(js, "__native_func", 13), js_mkfun(builtin_resolve_internal)); 5088 + setprop(js, resolve_obj, js_mkstr(js, "promise", 7), result_promise); 5089 + jsval_t resolve_fn = mkval(T_FUNC, vdata(resolve_obj)); 5090 + 5091 + jsval_t reject_obj = mkobj(js, 0); 5092 + setprop(js, reject_obj, js_mkstr(js, "__native_func", 13), js_mkfun(builtin_reject_internal)); 5093 + setprop(js, reject_obj, js_mkstr(js, "promise", 7), result_promise); 5094 + jsval_t reject_fn = mkval(T_FUNC, vdata(reject_obj)); 5095 + 5096 + for (int i = 0; i < len; i++) { 5097 + char idx[16]; 5098 + snprintf(idx, sizeof(idx), "%d", i); 5099 + jsval_t item = resolveprop(js, js_get(js, arr, idx)); 5100 + 5101 + if (vtype(item) != T_PROMISE) { 5102 + resolve_promise(js, result_promise, item); 5103 + return result_promise; 5104 + } 5105 + 5106 + jsval_t item_obj = mkval(T_OBJ, vdata(item)); 5107 + jsoff_t state_off = lkp(js, item_obj, "__state", 7); 5108 + int state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); 5109 + 5110 + if (state == 1) { 5111 + jsoff_t val_off = lkp(js, item_obj, "__value", 7); 5112 + jsval_t val = resolveprop(js, mkval(T_PROP, val_off)); 5113 + resolve_promise(js, result_promise, val); 5114 + return result_promise; 5115 + } else if (state == 2) { 5116 + jsoff_t val_off = lkp(js, item_obj, "__value", 7); 5117 + jsval_t val = resolveprop(js, mkval(T_PROP, val_off)); 5118 + reject_promise(js, result_promise, val); 5119 + return result_promise; 5120 + } 5121 + 5122 + jsval_t then_args[] = { resolve_fn, reject_fn }; 5123 + jsval_t saved_this = js->this_val; 5124 + js->this_val = item; 5125 + builtin_promise_then(js, then_args, 2); 5126 + js->this_val = saved_this; 5127 + } 5128 + 5129 + return result_promise; 5130 + } 5131 + 4771 5132 static jsval_t do_instanceof(struct js *js, jsval_t l, jsval_t r) { 4772 5133 uint8_t ltype = vtype(l); 4773 5134 if (vtype(r) == T_FUNC) { ··· 4884 5245 setprop(js, p_ctor_obj, js_mkstr(js, "resolve", 7), js_mkfun(builtin_Promise_resolve)); 4885 5246 setprop(js, p_ctor_obj, js_mkstr(js, "reject", 6), js_mkfun(builtin_Promise_reject)); 4886 5247 setprop(js, p_ctor_obj, js_mkstr(js, "try", 3), js_mkfun(builtin_Promise_try)); 5248 + setprop(js, p_ctor_obj, js_mkstr(js, "all", 3), js_mkfun(builtin_Promise_all)); 5249 + setprop(js, p_ctor_obj, js_mkstr(js, "race", 4), js_mkfun(builtin_Promise_race)); 4887 5250 setprop(js, p_ctor_obj, js_mkstr(js, "prototype", 9), p_proto); 4888 5251 4889 5252 setprop(js, glob, js_mkstr(js, "Promise", 7), mkval(T_FUNC, vdata(p_ctor_obj))); ··· 4922 5285 if (vtype(obj) == T_FUNC) { 4923 5286 jsval_t func_obj = mkval(T_OBJ, vdata(obj)); 4924 5287 jsoff_t off = lkp(js, func_obj, key, strlen(key)); 5288 + return off == 0 ? js_mkundef() : resolveprop(js, mkval(T_PROP, off)); 5289 + } 5290 + 5291 + if (vtype(obj) == T_ARR) { 5292 + jsval_t arr_obj = mkval(T_OBJ, vdata(obj)); 5293 + jsoff_t off = lkp(js, arr_obj, key, strlen(key)); 4925 5294 return off == 0 ? js_mkundef() : resolveprop(js, mkval(T_PROP, off)); 4926 5295 } 4927 5296 ··· 5147 5516 } 5148 5517 5149 5518 jsval_t js_mkpromise(struct js *js) { return mkpromise(js); } 5519 + int js_has_pending_coroutines(void) { return has_pending_coroutines() ? 1 : 0; } 5150 5520 5521 + void js_process_coroutines(struct js *js) { (void)js; } 5151 5522 void js_resolve_promise(struct js *js, jsval_t promise, jsval_t value) { resolve_promise(js, promise, value); } 5152 5523 void js_reject_promise(struct js *js, jsval_t promise, jsval_t value) { reject_promise(js, promise, value); } 5153 5524
+1 -1
src/main.c
··· 245 245 init_json_module(); 246 246 init_fetch_module(); 247 247 248 + init_timer_module(js); 248 249 init_crypto_module(js, rt->ant_obj); 249 - init_timer_module(js, rt->ant_obj); 250 250 251 251 js_set(js, rt->ant_obj, "serve", js_mkfun(js_serve)); 252 252 js_set(js, rt->ant_obj, "require", js_mkfun(js_require));
+11 -10
src/modules/timer.c
··· 36 36 return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000; 37 37 } 38 38 39 - // Ant.setTimeout(callback, delay) 39 + // setTimeout(callback, delay) 40 40 static jsval_t js_set_timeout(struct js *js, jsval_t *args, int nargs) { 41 41 if (nargs < 2) { 42 42 return js_mkerr(js, "setTimeout requires 2 arguments (callback, delay)"); ··· 66 66 return js_mknum((double)entry->timer_id); 67 67 } 68 68 69 - // Ant.setInterval(callback, delay) 69 + // setInterval(callback, delay) 70 70 static jsval_t js_set_interval(struct js *js, jsval_t *args, int nargs) { 71 71 if (nargs < 2) { 72 72 return js_mkerr(js, "setInterval requires 2 arguments (callback, delay)"); ··· 96 96 return js_mknum((double)entry->timer_id); 97 97 } 98 98 99 - // Ant.clearTimeout(timerId) 99 + // clearTimeout(timerId) 100 100 static jsval_t js_clear_timeout(struct js *js, jsval_t *args, int nargs) { 101 101 if (nargs < 1) { 102 102 return js_mkundef(); ··· 114 114 return js_mkundef(); 115 115 } 116 116 117 - // Ant.queueMicrotask(callback) 117 + // queueMicrotask(callback) 118 118 static jsval_t js_queue_microtask(struct js *js, jsval_t *args, int nargs) { 119 119 if (nargs < 1) { 120 120 return js_mkerr(js, "queueMicrotask requires 1 argument (callback)"); ··· 217 217 return min_timeout; 218 218 } 219 219 220 - void init_timer_module(struct js *js, jsval_t ant_obj) { 220 + void init_timer_module(struct js *js) { 221 221 timer_state.js = js; 222 + jsval_t global = js_glob(js); 222 223 223 - js_set(js, ant_obj, "setTimeout", js_mkfun(js_set_timeout)); 224 - js_set(js, ant_obj, "clearTimeout", js_mkfun(js_clear_timeout)); 225 - js_set(js, ant_obj, "setInterval", js_mkfun(js_set_interval)); 226 - js_set(js, ant_obj, "clearInterval", js_mkfun(js_clear_timeout)); 227 - js_set(js, ant_obj, "queueMicrotask", js_mkfun(js_queue_microtask)); 224 + js_set(js, global, "setTimeout", js_mkfun(js_set_timeout)); 225 + js_set(js, global, "clearTimeout", js_mkfun(js_clear_timeout)); 226 + js_set(js, global, "setInterval", js_mkfun(js_set_interval)); 227 + js_set(js, global, "clearInterval", js_mkfun(js_clear_timeout)); 228 + js_set(js, global, "queueMicrotask", js_mkfun(js_queue_microtask)); 228 229 }
+38
tests/promise_advanced.cjs
··· 1 + // Test Promise.all with multiple concurrent fetches 2 + async function test_promise_all() { 3 + console.log('Testing Promise.all with concurrent fetches...'); 4 + 5 + const promises = [fetch('https://httpbin.org/delay/1'), fetch('https://httpbin.org/get'), fetch('https://httpbin.org/user-agent')]; 6 + 7 + const responses = await Promise.all(promises); 8 + console.log('All requests completed!'); 9 + console.log('Response count:', responses.length); 10 + } 11 + 12 + // Test Promise.race 13 + async function test_promise_race() { 14 + console.log('Testing Promise.race...'); 15 + 16 + const promises = [fetch('https://httpbin.org/delay/2'), fetch('https://httpbin.org/get'), fetch('https://httpbin.org/delay/3')]; 17 + 18 + const winner = await Promise.race(promises); 19 + console.log('Race winner status:', winner.status); 20 + } 21 + 22 + // Test with timers and promises 23 + async function test_timer_integration() { 24 + console.log('Testing timer integration with await...'); 25 + 26 + const start = Date.now(); 27 + await new Promise(resolve => setTimeout(resolve, 100)); 28 + 29 + const elapsed = Date.now() - start; 30 + console.log('Timer completed in', elapsed, 'ms'); 31 + } 32 + 33 + // Run tests 34 + void test_promise_all(); 35 + console.log('---'); 36 + void test_promise_race(); 37 + console.log('---'); 38 + void test_timer_integration();
+39
tests/promise_simple.cjs
··· 1 + // Test Promise.all with resolved promises 2 + async function test_promise_all_simple() { 3 + console.log('Testing Promise.all with simple values...'); 4 + 5 + const promises = [ 6 + Promise.resolve(1), 7 + Promise.resolve(2), 8 + Promise.resolve(3) 9 + ]; 10 + 11 + const results = await Promise.all(promises); 12 + console.log('Results:', results); 13 + } 14 + 15 + // Test Promise.race with resolved promises 16 + async function test_promise_race_simple() { 17 + console.log('Testing Promise.race...'); 18 + 19 + const promises = [ 20 + Promise.resolve('first'), 21 + Promise.resolve('second') 22 + ]; 23 + 24 + const winner = await Promise.race(promises); 25 + console.log('Winner:', winner); 26 + } 27 + 28 + // Test fetch still works 29 + async function test_fetch() { 30 + console.log('Testing fetch...'); 31 + const response = await fetch('https://httpbin.org/get'); 32 + console.log('Fetch status:', response.status); 33 + } 34 + 35 + void test_promise_all_simple(); 36 + console.log('---'); 37 + void test_promise_race_simple(); 38 + console.log('---'); 39 + void test_fetch();
+5 -5
tests/test_async.cjs
··· 54 54 // Test 6: Async function with setTimeout 55 55 console.log('\nTest 6: Async function with setTimeout'); 56 56 async function withTimeout() { 57 - Ant.setTimeout(() => { 57 + setTimeout(() => { 58 58 console.log('Timeout inside async executed'); 59 59 }, 100); 60 60 return 'timeout scheduled'; ··· 67 67 // Test 7: Async function with queueMicrotask 68 68 console.log('\nTest 7: Async function with queueMicrotask'); 69 69 async function withMicrotask() { 70 - Ant.queueMicrotask(() => { 70 + queueMicrotask(() => { 71 71 console.log('Microtask inside async executed'); 72 72 }); 73 73 return 'microtask queued'; ··· 84 84 } 85 85 86 86 chain1().then(v => { 87 - console.log('Chained async result: ' + (v * 2)); 87 + console.log('Chained async result: ' + v * 2); 88 88 }); 89 89 90 90 // Test 9: Async function as callback ··· 111 111 112 112 // Test 11: Async function expression 113 113 console.log('\nTest 11: Async function expression'); 114 - const asyncExpr = async function() { 114 + const asyncExpr = async function () { 115 115 return 'expression'; 116 116 }; 117 117 ··· 150 150 console.log('\nTest 14: Async object method'); 151 151 const obj = { 152 152 value: 100, 153 - asyncMethod: async function() { 153 + asyncMethod: async function () { 154 154 return this.value; 155 155 } 156 156 };
+2 -2
tests/test_promise_display.cjs
··· 47 47 48 48 // Test 10: Pending promise 49 49 console.log('\nTest 10: Pending promise'); 50 - const p10 = new Promise((resolve) => { 51 - Ant.setTimeout(() => { 50 + const p10 = new Promise(resolve => { 51 + setTimeout(() => { 52 52 resolve(100); 53 53 console.log('After timeout, promise is: ' + p10); 54 54 }, 100);
+8 -8
tests/test_timers.cjs
··· 1 1 // Test setTimeout 2 2 console.log('Starting timer tests...'); 3 3 4 - Ant.setTimeout(() => { 4 + setTimeout(() => { 5 5 console.log('setTimeout executed after 1000ms'); 6 6 }, 1000); 7 7 8 - Ant.setTimeout(() => { 8 + setTimeout(() => { 9 9 console.log('setTimeout executed after 500ms'); 10 10 }, 500); 11 11 12 12 // Test queueMicrotask 13 - Ant.queueMicrotask(() => { 13 + queueMicrotask(() => { 14 14 console.log('Microtask 1 executed'); 15 15 }); 16 16 17 - Ant.queueMicrotask(() => { 17 + queueMicrotask(() => { 18 18 console.log('Microtask 2 executed'); 19 19 }); 20 20 ··· 22 22 23 23 // Test setInterval 24 24 let count = 0; 25 - const intervalId = Ant.setInterval(() => { 25 + const intervalId = setInterval(() => { 26 26 count++; 27 27 console.log('Interval execution #' + count); 28 28 29 29 if (count >= 3) { 30 - Ant.clearInterval(intervalId); 30 + clearInterval(intervalId); 31 31 console.log('Interval cleared after 3 executions'); 32 32 } 33 33 }, 200); 34 34 35 35 // Test clearTimeout 36 - const timeoutId = Ant.setTimeout(() => { 36 + const timeoutId = setTimeout(() => { 37 37 console.log('This should NOT be printed'); 38 38 }, 300); 39 39 40 - Ant.clearTimeout(timeoutId); 40 + clearTimeout(timeoutId); 41 41 console.log('Timeout cleared before execution');