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 async, await, promise

+553 -27
+2
include/modules/timer.h
··· 7 7 void init_timer_module(struct js *js, jsval_t ant_obj); 8 8 void process_timers(struct js *js); 9 9 void process_microtasks(struct js *js); 10 + void queue_microtask(struct js *js, jsval_t callback); 10 11 int has_pending_timers(void); 12 + int has_pending_microtasks(void); 11 13 int64_t get_next_timer_timeout(void); 12 14 13 15 #endif
+265 -12
src/ant.c
··· 11 11 12 12 #include "ant.h" 13 13 #include "config.h" 14 + #include "modules/timer.h" 14 15 15 16 typedef uint32_t jsoff_t; 16 17 ··· 49 50 enum { 50 51 TOK_ERR, TOK_EOF, TOK_IDENTIFIER, TOK_NUMBER, TOK_STRING, TOK_SEMICOLON, 51 52 TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE, TOK_LBRACKET, TOK_RBRACKET, 52 - TOK_BREAK = 50, TOK_CASE, TOK_CATCH, TOK_CLASS, TOK_CONST, TOK_CONTINUE, 53 + TOK_ASYNC = 50, TOK_AWAIT, TOK_BREAK, TOK_CASE, TOK_CATCH, TOK_CLASS, TOK_CONST, TOK_CONTINUE, 53 54 TOK_DEFAULT, TOK_DELETE, TOK_DO, TOK_ELSE, TOK_FINALLY, TOK_FOR, TOK_FUNC, 54 55 TOK_IF, TOK_IN, TOK_INSTANCEOF, TOK_LET, TOK_NEW, TOK_RETURN, TOK_SWITCH, 55 56 TOK_THIS, TOK_THROW, TOK_TRY, TOK_VAR, TOK_VOID, TOK_WHILE, TOK_WITH, ··· 118 119 static jsval_t js_expr(struct js *js); 119 120 static jsval_t js_stmt(struct js *js); 120 121 static jsval_t js_assignment(struct js *js); 122 + static jsval_t js_arrow_func(struct js *js, jsoff_t params_start, jsoff_t params_end, bool is_async); 121 123 static jsval_t do_op(struct js *, uint8_t op, jsval_t l, jsval_t r); 122 124 static jsval_t do_instanceof(struct js *js, jsval_t l, jsval_t r); 123 125 static jsval_t resolveprop(struct js *js, jsval_t v); ··· 575 577 576 578 static uint8_t parsekeyword(const char *buf, size_t len) { 577 579 switch (buf[0]) { 580 + case 'a': if (streq("async", 5, buf, len)) return TOK_ASYNC; if (streq("await", 5, buf, len)) return TOK_AWAIT; break; 578 581 case 'b': if (streq("break", 5, buf, len)) return TOK_BREAK; break; 579 582 case 'c': if (streq("class", 5, buf, len)) return TOK_CLASS; if (streq("case", 4, buf, len)) return TOK_CASE; if (streq("catch", 5, buf, len)) return TOK_CATCH; if (streq("const", 5, buf, len)) return TOK_CONST; if (streq("continue", 8, buf, len)) return TOK_CONTINUE; break; 580 583 case 'd': if (streq("do", 2, buf, len)) return TOK_DO; if (streq("default", 7, buf, len)) return TOK_DEFAULT; if (streq("delete", 6, buf, len)) return TOK_DELETE; break; ··· 1086 1089 } 1087 1090 jsoff_t fnlen, fnoff = vstr(js, code_val, &fnlen); 1088 1091 const char *code_str = (const char *) (&js->mem[fnoff]); 1092 + bool is_async = false; 1093 + jsoff_t async_off = lkp(js, func_obj, "__async", 7); 1094 + if (async_off != 0) { 1095 + jsval_t async_val = resolveprop(js, mkval(T_PROP, async_off)); 1096 + is_async = vtype(async_val) == T_BOOL && vdata(async_val) == 1; 1097 + } 1089 1098 if (fnlen == 16 && memcmp(code_str, "__builtin_Object", 16) == 0) { 1090 1099 res = call_c(js, builtin_Object); 1091 1100 } else { 1092 1101 js->nogc = (jsoff_t) (fnoff - sizeof(jsoff_t)); 1093 1102 res = call_js(js, code_str, fnlen, closure_scope); 1103 + if (is_async && !is_err(res)) { 1104 + jsval_t promise_args[] = { res }; 1105 + res = builtin_Promise_resolve(js, promise_args, 1); 1106 + } 1094 1107 } 1095 1108 } 1096 1109 } else { ··· 1401 1414 return obj; 1402 1415 } 1403 1416 1404 - static jsval_t js_func_literal(struct js *js) { 1417 + static jsval_t js_func_literal(struct js *js, bool is_async) { 1405 1418 uint8_t flags = js->flags; 1406 1419 js->consumed = 1; 1407 1420 jsoff_t name_off = 0, name_len = 0; ··· 1439 1452 if (is_err(code_key)) return code_key; 1440 1453 jsval_t res2 = setprop(js, func_obj, code_key, str); 1441 1454 if (is_err(res2)) return res2; 1455 + 1456 + if (is_async) { 1457 + jsval_t async_key = js_mkstr(js, "__async", 7); 1458 + if (is_err(async_key)) return async_key; 1459 + jsval_t res_async = setprop(js, func_obj, async_key, js_mktrue()); 1460 + if (is_err(res_async)) return res_async; 1461 + } 1462 + 1442 1463 if (name_len > 0) { 1443 1464 jsval_t name_key = js_mkstr(js, "name", 4); 1444 1465 if (is_err(name_key)) return name_key; ··· 1493 1514 case TOK_TEMPLATE: return js_template_literal(js); 1494 1515 case TOK_LBRACE: return js_obj_literal(js); 1495 1516 case TOK_LBRACKET: return js_arr_literal(js); 1496 - case TOK_FUNC: return js_func_literal(js); 1517 + case TOK_FUNC: return js_func_literal(js, false); 1518 + case TOK_ASYNC: { 1519 + js->consumed = 1; 1520 + uint8_t next_tok = next(js); 1521 + if (next_tok == TOK_FUNC) { 1522 + return js_func_literal(js, true); 1523 + } else if (next_tok == TOK_LPAREN) { 1524 + jsoff_t paren_start = js->pos - 1; 1525 + js->consumed = 1; 1526 + jsoff_t saved_pos = js->pos; 1527 + uint8_t saved_tok = js->tok; 1528 + uint8_t saved_consumed = js->consumed; 1529 + uint8_t saved_flags = js->flags; 1530 + int paren_depth = 1; 1531 + js->flags |= F_NOEXEC; 1532 + while (paren_depth > 0 && next(js) != TOK_EOF) { 1533 + if (js->tok == TOK_LPAREN) paren_depth++; 1534 + else if (js->tok == TOK_RPAREN) paren_depth--; 1535 + js->consumed = 1; 1536 + } 1537 + jsoff_t paren_end = js->pos; 1538 + bool is_arrow = lookahead(js) == TOK_ARROW; 1539 + js->pos = saved_pos; 1540 + js->tok = saved_tok; 1541 + js->consumed = saved_consumed; 1542 + js->flags = saved_flags; 1543 + if (is_arrow) { 1544 + js->flags |= F_NOEXEC; 1545 + while (next(js) != TOK_RPAREN && next(js) != TOK_EOF) { 1546 + js->consumed = 1; 1547 + } 1548 + if (next(js) != TOK_RPAREN) return js_mkerr(js, ") expected"); 1549 + js->consumed = 1; 1550 + js->flags = saved_flags; 1551 + if (next(js) != TOK_ARROW) return js_mkerr(js, "=> expected"); 1552 + js->consumed = 1; 1553 + return js_arrow_func(js, paren_start, paren_end, true); 1554 + } 1555 + return js_mkerr(js, "async ( must be arrow function"); 1556 + } else if (next_tok == TOK_IDENTIFIER) { 1557 + jsoff_t id_start = js->toff; 1558 + jsoff_t id_len = js->tlen; 1559 + js->consumed = 1; 1560 + if (next(js) == TOK_ARROW) { 1561 + js->consumed = 1; 1562 + char param_buf[256]; 1563 + if (id_len + 3 > sizeof(param_buf)) return js_mkerr(js, "param too long"); 1564 + param_buf[0] = '('; 1565 + memcpy(param_buf + 1, &js->code[id_start], id_len); 1566 + param_buf[id_len + 1] = ')'; 1567 + param_buf[id_len + 2] = '\0'; 1568 + uint8_t flags = js->flags; 1569 + bool is_expr = next(js) != TOK_LBRACE; 1570 + jsoff_t body_start = is_expr ? js->toff : js->pos; 1571 + jsval_t body_result; 1572 + if (is_expr) { 1573 + js->flags |= F_NOEXEC; 1574 + body_result = js_assignment(js); 1575 + if (is_err(body_result)) { js->flags = flags; return body_result; } 1576 + } else { 1577 + js->flags |= F_NOEXEC; 1578 + js->consumed = 1; 1579 + body_result = js_block(js, false); 1580 + if (is_err(body_result)) { js->flags = flags; return body_result; } 1581 + if (next(js) == TOK_RBRACE) js->consumed = 1; 1582 + } 1583 + js->flags = flags; 1584 + jsoff_t body_end = js->pos; 1585 + size_t fn_size = id_len + (body_end - body_start) + 64; 1586 + char *fn_str = (char *) malloc(fn_size); 1587 + if (!fn_str) return js_mkerr(js, "oom"); 1588 + jsoff_t fn_pos = 0; 1589 + memcpy(fn_str + fn_pos, param_buf, id_len + 2); 1590 + fn_pos += id_len + 2; 1591 + fn_str[fn_pos++] = '{'; 1592 + if (is_expr) { 1593 + memcpy(fn_str + fn_pos, "return ", 7); 1594 + fn_pos += 7; 1595 + size_t body_len = body_end - body_start; 1596 + memcpy(fn_str + fn_pos, &js->code[body_start], body_len); 1597 + fn_pos += body_len; 1598 + } else { 1599 + size_t body_len = body_end - body_start - 2; 1600 + memcpy(fn_str + fn_pos, &js->code[body_start + 1], body_len); 1601 + fn_pos += body_len; 1602 + } 1603 + fn_str[fn_pos++] = '}'; 1604 + jsval_t str = js_mkstr(js, fn_str, fn_pos); 1605 + free(fn_str); 1606 + if (is_err(str)) return str; 1607 + jsval_t func_obj = mkobj(js, 0); 1608 + if (is_err(func_obj)) return func_obj; 1609 + jsval_t code_key = js_mkstr(js, "__code", 6); 1610 + if (is_err(code_key)) return code_key; 1611 + jsval_t res = setprop(js, func_obj, code_key, str); 1612 + if (is_err(res)) return res; 1613 + jsval_t async_key = js_mkstr(js, "__async", 7); 1614 + if (is_err(async_key)) return async_key; 1615 + jsval_t res_async = setprop(js, func_obj, async_key, js_mktrue()); 1616 + if (is_err(res_async)) return res_async; 1617 + if (!(flags & F_NOEXEC)) { 1618 + jsval_t scope_key = js_mkstr(js, "__scope", 7); 1619 + if (is_err(scope_key)) return scope_key; 1620 + jsval_t res2 = setprop(js, func_obj, scope_key, js->scope); 1621 + if (is_err(res2)) return res2; 1622 + } 1623 + return mkval(T_FUNC, (unsigned long) vdata(func_obj)); 1624 + } 1625 + return mkcoderef((jsoff_t) id_start, (jsoff_t) id_len); 1626 + } 1627 + return js_mkerr(js, "unexpected token after async"); 1628 + } 1497 1629 case TOK_NULL: return js_mknull(); 1498 1630 case TOK_UNDEF: return js_mkundef(); 1499 1631 case TOK_TRUE: return js_mktrue(); ··· 1507 1639 } 1508 1640 } 1509 1641 1510 - static jsval_t js_arrow_func(struct js *js, jsoff_t params_start, jsoff_t params_end) { 1642 + static jsval_t js_arrow_func(struct js *js, jsoff_t params_start, jsoff_t params_end, bool is_async) { 1511 1643 uint8_t flags = js->flags; 1512 1644 bool is_expr = next(js) != TOK_LBRACE; 1513 1645 jsoff_t body_start, body_end_actual; ··· 1586 1718 jsval_t res = setprop(js, func_obj, code_key, str); 1587 1719 if (is_err(res)) return res; 1588 1720 1721 + if (is_async) { 1722 + jsval_t async_key = js_mkstr(js, "__async", 7); 1723 + if (is_err(async_key)) return async_key; 1724 + jsval_t res_async = setprop(js, func_obj, async_key, js_mktrue()); 1725 + if (is_err(res_async)) return res_async; 1726 + } 1727 + 1589 1728 if (!(flags & F_NOEXEC)) { 1590 1729 jsval_t scope_key = js_mkstr(js, "__scope", 7); 1591 1730 if (is_err(scope_key)) return scope_key; ··· 1640 1779 if (next(js) != TOK_ARROW) return js_mkerr(js, "=> expected"); 1641 1780 js->consumed = 1; 1642 1781 1643 - return js_arrow_func(js, paren_start, paren_end); 1782 + return js_arrow_func(js, paren_start, paren_end, false); 1644 1783 } else { 1645 1784 jsval_t v = js_expr(js); 1646 1785 if (is_err(v)) return v; ··· 2074 2213 return js_mkundef(); 2075 2214 } 2076 2215 2216 + static jsval_t js_func_decl_async(struct js *js) { 2217 + uint8_t exe = !(js->flags & F_NOEXEC); 2218 + js->consumed = 1; 2219 + EXPECT(TOK_IDENTIFIER, ); 2220 + js->consumed = 0; 2221 + jsoff_t noff = js->toff, nlen = js->tlen; 2222 + char *name = (char *) &js->code[noff]; 2223 + js->consumed = 1; 2224 + EXPECT(TOK_LPAREN, ); 2225 + jsoff_t pos = js->pos - 1; 2226 + for (bool comma = false; next(js) != TOK_EOF; comma = true) { 2227 + if (!comma && next(js) == TOK_RPAREN) break; 2228 + EXPECT(TOK_IDENTIFIER, ); 2229 + if (next(js) == TOK_RPAREN) break; 2230 + EXPECT(TOK_COMMA, ); 2231 + } 2232 + EXPECT(TOK_RPAREN, ); 2233 + EXPECT(TOK_LBRACE, ); 2234 + js->consumed = 0; 2235 + uint8_t flags = js->flags; 2236 + js->flags |= F_NOEXEC; 2237 + jsval_t res = js_block(js, false); 2238 + if (is_err(res)) { 2239 + js->flags = flags; 2240 + return res; 2241 + } 2242 + js->flags = flags; 2243 + jsval_t str = js_mkstr(js, &js->code[pos], js->pos - pos); 2244 + jsval_t func_obj = mkobj(js, 0); 2245 + if (is_err(func_obj)) return func_obj; 2246 + jsval_t code_key = js_mkstr(js, "__code", 6); 2247 + if (is_err(code_key)) return code_key; 2248 + jsval_t res2 = setprop(js, func_obj, code_key, str); 2249 + if (is_err(res2)) return res2; 2250 + jsval_t async_key = js_mkstr(js, "__async", 7); 2251 + if (is_err(async_key)) return async_key; 2252 + jsval_t res_async = setprop(js, func_obj, async_key, js_mktrue()); 2253 + if (is_err(res_async)) return res_async; 2254 + jsval_t name_key = js_mkstr(js, "name", 4); 2255 + if (is_err(name_key)) return name_key; 2256 + jsval_t name_val = js_mkstr(js, name, nlen); 2257 + if (is_err(name_val)) return name_val; 2258 + jsval_t res3 = setprop(js, func_obj, name_key, name_val); 2259 + if (is_err(res3)) return res3; 2260 + if (exe) { 2261 + jsval_t scope_key = js_mkstr(js, "__scope", 7); 2262 + if (is_err(scope_key)) return scope_key; 2263 + jsval_t res4 = setprop(js, func_obj, scope_key, js->scope); 2264 + if (is_err(res4)) return res4; 2265 + } 2266 + jsval_t func = mkval(T_FUNC, (unsigned long) vdata(func_obj)); 2267 + if (exe) { 2268 + if (lkp(js, js->scope, name, nlen) > 0) 2269 + return js_mkerr(js, "'%.*s' already declared", (int) nlen, name); 2270 + jsval_t x = mkprop(js, js->scope, js_mkstr(js, name, nlen), func, false); 2271 + if (is_err(x)) return x; 2272 + } 2273 + 2274 + return js_mkundef(); 2275 + } 2276 + 2077 2277 static jsval_t js_block_or_stmt(struct js *js) { 2078 2278 if (next(js) == TOK_LBRACE) return js_block(js, !(js->flags & F_NOEXEC)); 2079 2279 jsval_t res = resolveprop(js, js_stmt(js)); ··· 2399 2599 case TOK_LET: res = js_let(js); break; 2400 2600 case TOK_CONST: res = js_const(js); break; 2401 2601 case TOK_FUNC: res = js_func_decl(js); break; 2602 + case TOK_ASYNC: js->consumed = 1; if (next(js) == TOK_FUNC) res = js_func_decl_async(js); else return js_mkerr(js, "async must be followed by function"); break; 2402 2603 case TOK_CLASS: res = js_class_decl(js); break; 2403 2604 case TOK_IF: res = js_if(js); break; 2404 2605 case TOK_LBRACE: res = js_block(js, !(js->flags & F_NOEXEC)); break; ··· 3074 3275 3075 3276 static size_t strpromise(struct js *js, jsval_t value, char *buf, size_t len) { 3076 3277 jsval_t prom_obj = mkval(T_OBJ, vdata(value)); 3077 - jsoff_t off = lkp(js, prom_obj, "__state", 7); 3278 + jsoff_t state_off = lkp(js, prom_obj, "__state", 7); 3078 3279 int state = 0; 3079 - if (off != 0) { 3080 - jsval_t val = resolveprop(js, mkval(T_PROP, off)); 3280 + if (state_off != 0) { 3281 + jsval_t val = resolveprop(js, mkval(T_PROP, state_off)); 3081 3282 if (vtype(val) == T_NUM) state = (int)tod(val); 3082 3283 } 3083 - const char *s = (state == 0) ? "<pending>" : (state == 1) ? "<fulfilled>" : "<rejected>"; 3084 - return (size_t)snprintf(buf, len, "Promise { %s }", s); 3284 + 3285 + if (state == 0) { 3286 + return (size_t)snprintf(buf, len, "Promise { <pending> }"); 3287 + } 3288 + 3289 + jsoff_t value_off = lkp(js, prom_obj, "__value", 7); 3290 + if (value_off == 0) { 3291 + const char *s = (state == 1) ? "<fulfilled>" : "<rejected>"; 3292 + return (size_t)snprintf(buf, len, "Promise { %s }", s); 3293 + } 3294 + 3295 + jsval_t prom_val = resolveprop(js, mkval(T_PROP, value_off)); 3296 + char value_buf[256]; 3297 + size_t value_len = tostr(js, prom_val, value_buf, sizeof(value_buf)); 3298 + if (value_len >= sizeof(value_buf)) value_len = sizeof(value_buf) - 1; 3299 + value_buf[value_len] = '\0'; 3300 + 3301 + if (state == 1) { 3302 + return (size_t)snprintf(buf, len, "Promise { %s }", value_buf); 3303 + } else { 3304 + return (size_t)snprintf(buf, len, "Promise { <rejected> %s }", value_buf); 3305 + } 3085 3306 } 3086 3307 3087 3308 static jsval_t mkpromise(struct js *js) { ··· 3093 3314 return mkval(T_PROMISE, vdata(obj)); 3094 3315 } 3095 3316 3317 + static jsval_t builtin_trigger_handler_wrapper(struct js *js, jsval_t *args, int nargs); 3318 + 3096 3319 static void trigger_handlers(struct js *js, jsval_t p) { 3320 + jsval_t wrapper_obj = mkobj(js, 0); 3321 + setprop(js, wrapper_obj, js_mkstr(js, "__native_func", 13), js_mkfun(builtin_trigger_handler_wrapper)); 3322 + setprop(js, wrapper_obj, js_mkstr(js, "promise", 7), p); 3323 + jsval_t wrapper_fn = mkval(T_FUNC, vdata(wrapper_obj)); 3324 + queue_microtask(js, wrapper_fn); 3325 + } 3326 + 3327 + static jsval_t builtin_trigger_handler_wrapper(struct js *js, jsval_t *args, int nargs) { 3328 + jsval_t me = js->current_func; 3329 + jsval_t p = js_get(js, me, "promise"); 3330 + if (vtype(p) != T_PROMISE) return js_mkundef(); 3331 + 3097 3332 jsval_t p_obj = mkval(T_OBJ, vdata(p)); 3098 3333 jsoff_t state_off = lkp(js, p_obj, "__state", 7); 3099 3334 int state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); ··· 3141 3376 } 3142 3377 } 3143 3378 setprop(js, p_obj, js_mkstr(js, "__handlers", 10), mkarr(js)); 3379 + return js_mkundef(); 3144 3380 } 3145 3381 3146 3382 static void resolve_promise(struct js *js, jsval_t p, jsval_t val) { ··· 3210 3446 return js_mkundef(); 3211 3447 } 3212 3448 3449 + static jsval_t builtin_promise_executor_wrapper(struct js *js, jsval_t *args, int nargs); 3450 + 3213 3451 static jsval_t builtin_Promise(struct js *js, jsval_t *args, int nargs) { 3214 3452 jsval_t p = mkpromise(js); 3215 3453 jsval_t res_obj = mkobj(js, 0); ··· 3221 3459 setprop(js, rej_obj, js_mkstr(js, "promise", 7), p); 3222 3460 jsval_t rej_fn = mkval(T_FUNC, vdata(rej_obj)); 3223 3461 if (nargs > 0) { 3224 - jsval_t exec_args[] = { res_fn, rej_fn }; 3225 - js_call(js, args[0], exec_args, 2); 3462 + jsval_t wrapper_obj = mkobj(js, 0); 3463 + setprop(js, wrapper_obj, js_mkstr(js, "__native_func", 13), js_mkfun(builtin_promise_executor_wrapper)); 3464 + setprop(js, wrapper_obj, js_mkstr(js, "executor", 8), args[0]); 3465 + setprop(js, wrapper_obj, js_mkstr(js, "resolve", 7), res_fn); 3466 + setprop(js, wrapper_obj, js_mkstr(js, "reject", 6), rej_fn); 3467 + jsval_t wrapper_fn = mkval(T_FUNC, vdata(wrapper_obj)); 3468 + queue_microtask(js, wrapper_fn); 3226 3469 } 3227 3470 return p; 3471 + } 3472 + 3473 + static jsval_t builtin_promise_executor_wrapper(struct js *js, jsval_t *args, int nargs) { 3474 + jsval_t me = js->current_func; 3475 + jsval_t executor = js_get(js, me, "executor"); 3476 + jsval_t res_fn = js_get(js, me, "resolve"); 3477 + jsval_t rej_fn = js_get(js, me, "reject"); 3478 + jsval_t exec_args[] = { res_fn, rej_fn }; 3479 + js_call(js, executor, exec_args, 2); 3480 + return js_mkundef(); 3228 3481 } 3229 3482 3230 3483 static jsval_t builtin_Promise_resolve(struct js *js, jsval_t *args, int nargs) {
+11 -8
src/main.c
··· 252 252 253 253 int result = execute_module(js, module_file); 254 254 255 - process_microtasks(js); 256 - 257 - while (has_pending_timers()) { 258 - int64_t next_timeout_ms = get_next_timer_timeout(); 255 + while (has_pending_microtasks() || has_pending_timers()) { 256 + process_microtasks(js); 259 257 260 - if (next_timeout_ms <= 0) { 261 - process_timers(js); 262 - } else { 263 - usleep(next_timeout_ms > 1000000 ? 1000000 : next_timeout_ms * 1000); 258 + if (has_pending_timers()) { 259 + int64_t next_timeout_ms = get_next_timer_timeout(); 260 + 261 + if (next_timeout_ms <= 0) { 262 + process_timers(js); 263 + continue; 264 + } else { 265 + usleep(next_timeout_ms > 1000000 ? 1000000 : next_timeout_ms * 1000); 266 + } 264 267 } 265 268 } 266 269
+10 -7
src/modules/timer.c
··· 120 120 return js_mkerr(js, "queueMicrotask requires 1 argument (callback)"); 121 121 } 122 122 123 - jsval_t callback = args[0]; 124 - 123 + queue_microtask(js, args[0]); 124 + return js_mkundef(); 125 + } 126 + 127 + void queue_microtask(struct js *js, jsval_t callback) { 125 128 microtask_entry_t *entry = malloc(sizeof(microtask_entry_t)); 126 - if (entry == NULL) { 127 - return js_mkerr(js, "failed to allocate microtask"); 128 - } 129 + if (entry == NULL) return; 129 130 130 131 entry->callback = callback; 131 132 entry->next = NULL; ··· 137 138 timer_state.microtasks_tail->next = entry; 138 139 timer_state.microtasks_tail = entry; 139 140 } 140 - 141 - return js_mkundef(); 142 141 } 143 142 144 143 void process_microtasks(struct js *js) { ··· 197 196 if (entry->active) return 1; 198 197 } 199 198 return 0; 199 + } 200 + 201 + int has_pending_microtasks(void) { 202 + return timer_state.microtasks != NULL ? 1 : 0; 200 203 } 201 204 202 205 int64_t get_next_timer_timeout(void) {
+175
tests/test_async.cjs
··· 1 + // Test async/await functionality 2 + Ant.println('=== Async/Await Tests ==='); 3 + 4 + // Test 1: Basic async function 5 + Ant.println('\nTest 1: Basic async function'); 6 + async function basicAsync() { 7 + return 42; 8 + } 9 + 10 + basicAsync().then(v => { 11 + Ant.println('Basic async returned: ' + v); 12 + }); 13 + 14 + // Test 2: Async arrow function 15 + Ant.println('\nTest 2: Async arrow function'); 16 + const arrowAsync = async () => { 17 + return 'arrow result'; 18 + }; 19 + 20 + arrowAsync().then(v => { 21 + Ant.println('Arrow async returned: ' + v); 22 + }); 23 + 24 + // Test 3: Async arrow function with single parameter 25 + Ant.println('\nTest 3: Async arrow with param'); 26 + const singleParamAsync = async x => { 27 + return x * 2; 28 + }; 29 + 30 + singleParamAsync(21).then(v => { 31 + Ant.println('Single param async returned: ' + v); 32 + }); 33 + 34 + // Test 4: Async arrow function with multiple parameters 35 + Ant.println('\nTest 4: Async arrow with multiple params'); 36 + const multiParamAsync = async (a, b) => { 37 + return a + b; 38 + }; 39 + 40 + multiParamAsync(10, 32).then(v => { 41 + Ant.println('Multi param async returned: ' + v); 42 + }); 43 + 44 + // Test 5: Async function with Promise 45 + Ant.println('\nTest 5: Async function returning Promise'); 46 + async function withPromise() { 47 + return Promise.resolve('promise from async'); 48 + } 49 + 50 + withPromise().then(v => { 51 + Ant.println('Async with promise: ' + v); 52 + }); 53 + 54 + // Test 6: Async function with setTimeout 55 + Ant.println('\nTest 6: Async function with setTimeout'); 56 + async function withTimeout() { 57 + Ant.setTimeout(() => { 58 + Ant.println('Timeout inside async executed'); 59 + }, 100); 60 + return 'timeout scheduled'; 61 + } 62 + 63 + withTimeout().then(v => { 64 + Ant.println('Async with timeout returned: ' + v); 65 + }); 66 + 67 + // Test 7: Async function with queueMicrotask 68 + Ant.println('\nTest 7: Async function with queueMicrotask'); 69 + async function withMicrotask() { 70 + Ant.queueMicrotask(() => { 71 + Ant.println('Microtask inside async executed'); 72 + }); 73 + return 'microtask queued'; 74 + } 75 + 76 + withMicrotask().then(v => { 77 + Ant.println('Async with microtask returned: ' + v); 78 + }); 79 + 80 + // Test 8: Async functions must return primitives for now (Promise chaining limitation) 81 + Ant.println('\nTest 8: Async returning values'); 82 + async function chain1() { 83 + return 5; 84 + } 85 + 86 + chain1().then(v => { 87 + Ant.println('Chained async result: ' + (v * 2)); 88 + }); 89 + 90 + // Test 9: Async function as callback 91 + Ant.println('\nTest 9: Async function as callback'); 92 + function executor(fn) { 93 + fn().then(v => { 94 + Ant.println('Callback async result: ' + v); 95 + }); 96 + } 97 + 98 + executor(async () => { 99 + return 'callback value'; 100 + }); 101 + 102 + // Test 10: Multiple promise chains 103 + Ant.println('\nTest 10: Multiple promise chains'); 104 + Promise.resolve(1) 105 + .then(v => v + 1) 106 + .then(v => v + 1) 107 + .then(v => v + 1) 108 + .then(v => { 109 + Ant.println('Multiple chains result: ' + v); 110 + }); 111 + 112 + // Test 11: Async function expression 113 + Ant.println('\nTest 11: Async function expression'); 114 + const asyncExpr = async function() { 115 + return 'expression'; 116 + }; 117 + 118 + asyncExpr().then(v => { 119 + Ant.println('Async expression result: ' + v); 120 + }); 121 + 122 + // Test 12: Async function with conditional 123 + Ant.println('\nTest 12: Async function with conditional'); 124 + async function conditional(flag) { 125 + if (flag) { 126 + return 'true branch'; 127 + } 128 + return 'false branch'; 129 + } 130 + 131 + conditional(true).then(v => { 132 + Ant.println('Conditional async (true): ' + v); 133 + }); 134 + 135 + conditional(false).then(v => { 136 + Ant.println('Conditional async (false): ' + v); 137 + }); 138 + 139 + // Test 13: Async with Promise.resolve 140 + Ant.println('\nTest 13: Async with Promise.resolve'); 141 + async function withResolve() { 142 + return Promise.resolve('resolved value'); 143 + } 144 + 145 + withResolve().then(v => { 146 + Ant.println('Async with resolve result: ' + v); 147 + }); 148 + 149 + // Test 14: Async with object method 150 + Ant.println('\nTest 14: Async object method'); 151 + const obj = { 152 + value: 100, 153 + asyncMethod: async function() { 154 + return this.value; 155 + } 156 + }; 157 + 158 + obj.asyncMethod().then(v => { 159 + Ant.println('Async object method: ' + v); 160 + }); 161 + 162 + // Test 15: Async arrow in object 163 + Ant.println('\nTest 15: Async arrow in object'); 164 + const obj2 = { 165 + value: 200, 166 + asyncArrow: async () => { 167 + return 'arrow in object'; 168 + } 169 + }; 170 + 171 + obj2.asyncArrow().then(v => { 172 + Ant.println('Async arrow in object: ' + v); 173 + }); 174 + 175 + Ant.println('\n=== Synchronous code finished ===');
+90
tests/test_promise_display.cjs
··· 1 + // Test Promise display with various value types 2 + Ant.println('=== Promise Display Test ==='); 3 + 4 + // Test 1: Number 5 + Ant.println('\nTest 1: Promise with number'); 6 + const p1 = Promise.resolve(42); 7 + Ant.println('Promise.resolve(42): ' + p1); 8 + 9 + // Test 2: String 10 + Ant.println('\nTest 2: Promise with string'); 11 + const p2 = Promise.resolve('hello'); 12 + Ant.println('Promise.resolve("hello"): ' + p2); 13 + 14 + // Test 3: Boolean (true) 15 + Ant.println('\nTest 3: Promise with boolean'); 16 + const p3 = Promise.resolve(true); 17 + Ant.println('Promise.resolve(true): ' + p3); 18 + 19 + // Test 4: Boolean (false) 20 + const p4 = Promise.resolve(false); 21 + Ant.println('Promise.resolve(false): ' + p4); 22 + 23 + // Test 5: Null 24 + Ant.println('\nTest 5: Promise with null'); 25 + const p5 = Promise.resolve(null); 26 + Ant.println('Promise.resolve(null): ' + p5); 27 + 28 + // Test 6: Undefined 29 + Ant.println('\nTest 6: Promise with undefined'); 30 + const p6 = Promise.resolve(undefined); 31 + Ant.println('Promise.resolve(undefined): ' + p6); 32 + 33 + // Test 7: Object 34 + Ant.println('\nTest 7: Promise with object'); 35 + const p7 = Promise.resolve({ x: 1, y: 2 }); 36 + Ant.println('Promise.resolve({x:1,y:2}): ' + p7); 37 + 38 + // Test 8: Array 39 + Ant.println('\nTest 8: Promise with array'); 40 + const p8 = Promise.resolve([1, 2, 3]); 41 + Ant.println('Promise.resolve([1,2,3]): ' + p8); 42 + 43 + // Test 9: Rejected promise 44 + Ant.println('\nTest 9: Rejected promise'); 45 + const p9 = Promise.reject('error message'); 46 + Ant.println('Promise.reject("error message"): ' + p9); 47 + 48 + // Test 10: Pending promise 49 + Ant.println('\nTest 10: Pending promise'); 50 + const p10 = new Promise((resolve) => { 51 + Ant.setTimeout(() => { 52 + resolve(100); 53 + Ant.println('After timeout, promise is: ' + p10); 54 + }, 100); 55 + }); 56 + Ant.println('Before timeout, promise is: ' + p10); 57 + 58 + // Test 11: Async function result 59 + Ant.println('\nTest 11: Async function result'); 60 + async function getValue() { 61 + return 999; 62 + } 63 + const p11 = getValue(); 64 + Ant.println('Async function returned: ' + p11); 65 + 66 + // Test 12: Async function with object 67 + Ant.println('\nTest 12: Async function with object'); 68 + async function getObject() { 69 + return { value: 42, name: 'test' }; 70 + } 71 + const p12 = getObject(); 72 + Ant.println('Async object function: ' + p12); 73 + 74 + // Test 13: Async function with array 75 + Ant.println('\nTest 13: Async function with array'); 76 + async function getArray() { 77 + return [10, 20, 30]; 78 + } 79 + const p13 = getArray(); 80 + Ant.println('Async array function: ' + p13); 81 + 82 + // Test 14: Promise chaining visibility 83 + Ant.println('\nTest 14: Promise in then handler'); 84 + Promise.resolve(5).then(v => { 85 + const inner = Promise.resolve(v * 2); 86 + Ant.println('Inner promise: ' + inner); 87 + return inner; 88 + }); 89 + 90 + Ant.println('\n=== Synchronous code finished ===');