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.

rewrite entire async engine

+2033 -524
examples/npm/express/ant.lockb

This is a binary file and will not be displayed.

+12
examples/npm/express/index.cjs
··· 1 + const express = require('express'); 2 + 3 + let app = express(); 4 + let port = 3000; 5 + 6 + app.get('/', (_req, res) => { 7 + res.send('Hello World!'); 8 + }); 9 + 10 + app.listen(port, () => { 11 + console.log(`Example app listening on port ${port}`); 12 + });
+13
examples/npm/express/package.json
··· 1 + { 2 + "name": "express", 3 + "version": "1.0.0", 4 + "type": "commonjs", 5 + "main": "index.cjs", 6 + "scripts": { 7 + "start": "ant index.cjs" 8 + }, 9 + "dependencies": { 10 + "express": "^5.2.1" 11 + }, 12 + "devDependencies": {} 13 + }
+5 -3
include/ant.h
··· 153 153 ant_value_t js_mkffi(unsigned int index); 154 154 int js_getffi(ant_value_t val); 155 155 156 - void js_resolve_promise(ant_t *js, ant_value_t promise, ant_value_t value); 157 - void js_reject_promise(ant_t *js, ant_value_t promise, ant_value_t value); 158 156 void js_check_unhandled_rejections(ant_t *js); 159 - void js_process_promise_handlers(ant_t *js, ant_value_t promise); 160 157 void js_setup_import_meta(ant_t *js, const char *filename); 158 + void js_process_promise_handlers(ant_t *js, ant_value_t promise); 159 + void js_mark_promise_trigger_dequeued(ant_t *js, ant_value_t promise); 160 + bool js_mark_promise_trigger_queued(ant_t *js, ant_value_t promise); 161 + void js_reject_promise(ant_t *js, ant_value_t promise, ant_value_t value); 162 + void js_resolve_promise(ant_t *js, ant_value_t promise, ant_value_t value); 161 163 162 164 typedef ant_value_t (*js_getter_fn)(ant_t *js, ant_value_t obj, const char *key, size_t key_len); 163 165 typedef ant_value_t (*js_keys_fn)(ant_t *js, ant_value_t obj);
+33 -1
include/http/http1_parser.h
··· 4 4 #include <stdbool.h> 5 5 #include <stddef.h> 6 6 #include <stdint.h> 7 + #include <llhttp.h> 8 + 7 9 #include "modules/http.h" 10 + #include "http/http1_writer.h" 8 11 9 12 typedef struct { 10 13 char *method; ··· 22 25 bool keep_alive; 23 26 } ant_http1_parsed_request_t; 24 27 28 + typedef struct { 29 + ant_http1_parsed_request_t req; 30 + ant_http1_buffer_t method; 31 + ant_http1_buffer_t target; 32 + ant_http1_buffer_t header_field; 33 + ant_http1_buffer_t header_value; 34 + ant_http1_buffer_t body; 35 + ant_http_header_t **header_tail; 36 + bool message_complete; 37 + } ant_http1_parser_ctx_t; 38 + 39 + typedef struct { 40 + llhttp_t parser; 41 + ant_http1_parser_ctx_t ctx; 42 + size_t fed_len; 43 + } ant_http1_conn_parser_t; 44 + 25 45 typedef enum { 26 46 ANT_HTTP1_PARSE_INCOMPLETE = 0, 27 47 ANT_HTTP1_PARSE_OK, 28 48 ANT_HTTP1_PARSE_ERROR, 29 49 } ant_http1_parse_result_t; 50 + 51 + 52 + void ant_http1_free_parsed_request(ant_http1_parsed_request_t *req); 53 + void ant_http1_conn_parser_init(ant_http1_conn_parser_t *cp); 54 + void ant_http1_conn_parser_reset(ant_http1_conn_parser_t *cp); 55 + void ant_http1_conn_parser_free(ant_http1_conn_parser_t *cp); 30 56 31 57 ant_http1_parse_result_t ant_http1_parse_request( 32 58 const char *data, ··· 36 62 const char **error_code 37 63 ); 38 64 39 - void ant_http1_free_parsed_request(ant_http1_parsed_request_t *req); 65 + ant_http1_parse_result_t ant_http1_conn_parser_execute( 66 + ant_http1_conn_parser_t *cp, 67 + const char *data, 68 + size_t len, 69 + ant_http1_parsed_request_t *out, 70 + size_t *consumed_out 71 + ); 40 72 41 73 #endif
+5
include/internal.h
··· 4 4 #include "ant.h" 5 5 #include "object.h" 6 6 #include "pool.h" 7 + #include "sugar.h" 7 8 #include "arena.h" 8 9 #include "descriptors.h" 9 10 #include "esm/loader.h" ··· 208 209 #ifdef ANT_JIT 209 210 uint32_t jit_active_depth; 210 211 #endif 212 + 213 + uint32_t vm_exec_depth; 214 + bool microtasks_draining; 215 + struct coroutine *active_async_coro; 211 216 212 217 struct { 213 218 ant_value_t *items;
+1
include/modules/headers.h
··· 37 37 ant_value_t headers_create_from_init(ant_t *js, ant_value_t init); 38 38 ant_value_t headers_get_value(ant_t *js, ant_value_t hdrs, const char *name); 39 39 ant_value_t headers_append_value(ant_t *js, ant_value_t hdrs, ant_value_t name_v, ant_value_t value_v); 40 + ant_value_t headers_append_literal(ant_t *js, ant_value_t hdrs, const char *name, const char *value); 40 41 41 42 headers_guard_t headers_get_guard(ant_value_t hdrs); 42 43
+18 -4
include/modules/request.h
··· 33 33 34 34 request_data_t *request_get_data(ant_value_t obj); 35 35 ant_value_t request_get_headers(ant_value_t obj); 36 - ant_value_t request_get_signal(ant_value_t obj); 36 + ant_value_t request_get_signal(ant_t *js, ant_value_t obj); 37 + 38 + ant_value_t request_create_from_input_init( 39 + ant_t *js, 40 + ant_value_t input, 41 + ant_value_t init 42 + ); 37 43 38 44 ant_value_t request_create( 39 45 ant_t *js, ··· 45 51 const char *body_type 46 52 ); 47 53 48 - ant_value_t request_create_from_input_init( 54 + ant_value_t request_create_server( 49 55 ant_t *js, 50 - ant_value_t input, 51 - ant_value_t init 56 + const char *method, 57 + const char *target, 58 + bool absolute_target, 59 + const char *host, 60 + const char *server_hostname, 61 + int server_port, 62 + ant_value_t headers, 63 + const uint8_t *body, 64 + size_t body_len, 65 + const char *body_type 52 66 ); 53 67 54 68 #endif
+4 -1
include/modules/timer.h
··· 7 7 void process_microtasks(ant_t *js); 8 8 void process_immediates(ant_t *js); 9 9 void queue_microtask(ant_t *js, ant_value_t callback); 10 - void queue_promise_trigger(ant_value_t promise); 10 + void queue_promise_trigger(ant_t *js, ant_value_t promise); 11 + 12 + bool js_maybe_drain_microtasks(ant_t *js); 13 + bool js_maybe_drain_microtasks_after_async_settle(ant_t *js); 11 14 12 15 int has_pending_timers(void); 13 16 int has_pending_microtasks(void);
+11
include/object.h
··· 2 2 #define ANT_OBJECT_H 3 3 4 4 #include "types.h" 5 + #include "sugar.h" 5 6 #include "shapes.h" 6 7 7 8 #include <utarray.h> ··· 15 16 bool (*deleter)(ant_t *, ant_value_t, const char *, size_t); 16 17 } ant_exotic_ops_t; 17 18 19 + typedef struct promise_handler { 20 + ant_value_t onFulfilled; 21 + ant_value_t onRejected; 22 + ant_value_t nextPromise; 23 + struct coroutine *await_coro; 24 + } promise_handler_t; 25 + 18 26 typedef struct { 19 27 ant_value_t value; 20 28 ant_value_t trigger_parent; 29 + promise_handler_t inline_handler; 21 30 UT_array *handlers; 22 31 uint32_t promise_id; 32 + uint16_t handler_count; 23 33 uint8_t state; 34 + bool trigger_queued; 24 35 bool has_rejection_handler; 25 36 bool processing; 26 37 bool unhandled_reported;
+34
include/output.h
··· 1 + #ifndef ANT_OUTPUT_H 2 + #define ANT_OUTPUT_H 3 + 4 + #include <stdarg.h> 5 + #include <stdbool.h> 6 + #include <stddef.h> 7 + #include <stdio.h> 8 + 9 + typedef struct { 10 + char *data; 11 + size_t len; 12 + size_t cap; 13 + bool failed; 14 + } ant_output_buffer_t; 15 + 16 + typedef struct { 17 + FILE *stream; 18 + ant_output_buffer_t buffer; 19 + } ant_output_stream_t; 20 + 21 + ant_output_stream_t *ant_output_stream(FILE *stream); 22 + void ant_output_stream_begin(ant_output_stream_t *out); 23 + 24 + bool ant_output_stream_reserve(ant_output_stream_t *out, size_t extra); 25 + bool ant_output_stream_append(ant_output_stream_t *out, const void *data, size_t len); 26 + bool ant_output_stream_append_cstr(ant_output_stream_t *out, const char *str); 27 + bool ant_output_stream_putc(ant_output_stream_t *out, char ch); 28 + bool ant_output_stream_appendfv(ant_output_stream_t *out, const char *fmt, va_list ap); 29 + 30 + __attribute__((format(printf, 2, 3))) 31 + bool ant_output_stream_appendf(ant_output_stream_t *out, const char *fmt, ...); 32 + bool ant_output_stream_flush(ant_output_stream_t *out); 33 + 34 + #endif
+47 -11
include/silver/engine.h
··· 6 6 #include "runtime.h" 7 7 #include "errors.h" 8 8 #include "gc/objects.h" 9 + #include "modules/timer.h" 9 10 10 11 #include <stdbool.h> 11 12 #include <stdint.h> 12 13 #include <stdlib.h> 13 14 #include <string.h> 15 + #include <stdio.h> 14 16 15 17 typedef enum { 16 18 #define OP_DEF(name, size, n_pop, n_push, f) OP_##name, ··· 149 151 bool is_strict; 150 152 bool is_arrow; 151 153 bool is_async; 154 + bool has_await; 152 155 bool is_generator; 153 156 bool is_method; 154 157 bool is_tla; ··· 326 329 sv_handler_t handler_stack[SV_HANDLER_MAX]; 327 330 sv_upvalue_t *open_upvalues; 328 331 int handler_depth; 332 + 333 + // TODO: move to nested struct 334 + bool suspended; 335 + bool suspended_resume_pending; 336 + bool suspended_resume_is_error; 337 + 338 + int suspended_entry_fp; 339 + int suspended_saved_fp; 340 + ant_value_t suspended_resume_value; 329 341 330 342 #ifdef ANT_JIT 331 343 struct { ··· 436 448 ant_value_t *out_this, bool is_construct_call 437 449 ); 438 450 451 + static inline void sv_vm_maybe_checkpoint_microtasks(ant_t *js) { 452 + if (!js || js->microtasks_draining || js->vm_exec_depth != 0) return; 453 + js_maybe_drain_microtasks(js); 454 + } 455 + 439 456 typedef struct { 440 457 ant_value_t this_val; 441 458 ant_value_t super_val; ··· 816 833 if (!is_construct_call) js->new_target = js_mkundef(); 817 834 if (out_this) *out_this = this_val; 818 835 819 - if (is_construct_call && vtype(func) == T_OBJ && is_proxy(func)) 820 - return js_proxy_construct(js, func, args, argc, sv_vm_get_new_target(vm, js)); 836 + if (is_construct_call && vtype(func) == T_OBJ && is_proxy(func)) { 837 + ant_value_t result = js_proxy_construct(js, func, args, argc, sv_vm_get_new_target(vm, js)); 838 + sv_vm_maybe_checkpoint_microtasks(js); 839 + return result; 840 + } 841 + 821 842 if (is_construct_call && !js_is_constructor(js, func)) 822 843 return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor"); 823 844 824 - if (!is_construct_call && vtype(func) == T_OBJ && is_proxy(func)) 825 - return js_proxy_apply(js, func, this_val, args, argc); 845 + if (!is_construct_call && vtype(func) == T_OBJ && is_proxy(func)) { 846 + ant_value_t result = js_proxy_apply(js, func, this_val, args, argc); 847 + sv_vm_maybe_checkpoint_microtasks(js); 848 + return result; 849 + } 826 850 827 851 if (vtype(func) == T_CFUNC) { 828 852 ant_value_t cfunc_this = sv_is_nullish_this(this_val) ? js->global : this_val; 829 - return sv_call_cfunc(js, func, cfunc_this, args, argc); 853 + ant_value_t result = sv_call_cfunc(js, func, cfunc_this, args, argc); 854 + sv_vm_maybe_checkpoint_microtasks(js); 855 + return result; 830 856 } 831 857 832 - if (vtype(func) != T_FUNC) 833 - return sv_call_native(js, func, this_val, args, argc); 858 + if (vtype(func) != T_FUNC) { 859 + ant_value_t result = sv_call_native(js, func, this_val, args, argc); 860 + sv_vm_maybe_checkpoint_microtasks(js); 861 + return result; 862 + } 834 863 835 864 sv_closure_t *closure = js_func_closure(func); 836 865 ··· 845 874 if (is_construct_call) ctx.this_val = this_val; 846 875 if (out_this) *out_this = ctx.this_val; 847 876 848 - if (closure->call_flags & SV_CALL_IS_DEFAULT_CTOR) 849 - return sv_call_default_ctor(vm, js, closure, &ctx, out_this); 877 + if (closure->call_flags & SV_CALL_IS_DEFAULT_CTOR) { 878 + ant_value_t result = sv_call_default_ctor(vm, js, closure, &ctx, out_this); 879 + sv_vm_maybe_checkpoint_microtasks(js); 880 + return result; 881 + } 850 882 851 - if (closure->func != NULL) 852 - return sv_call_resolve_closure(vm, js, closure, func, &ctx, out_this); 883 + if (closure->func != NULL) { 884 + ant_value_t result = sv_call_resolve_closure(vm, js, closure, func, &ctx, out_this); 885 + sv_vm_maybe_checkpoint_microtasks(js); 886 + return result; 887 + } 853 888 854 889 ant_value_t result = sv_call_native(js, func, ctx.this_val, ctx.args, ctx.argc); 855 890 sv_call_cleanup(js, &ctx); 891 + sv_vm_maybe_checkpoint_microtasks(js); 856 892 857 893 return result; 858 894 }
+2
include/silver/vm.h
··· 38 38 ant_value_t this_val 39 39 ); 40 40 41 + ant_value_t sv_resume_suspended(sv_vm_t *vm); 42 + 41 43 #endif
+18
include/sugar.h
··· 3 3 4 4 #include "types.h" 5 5 6 + #include <stdbool.h> 7 + #include <stdint.h> 6 8 #include <stddef.h> 7 9 #include <stdlib.h> 8 10 #include <minicoro.h> ··· 49 51 ant_value_t awaited_promise; 50 52 ant_value_t async_promise; 51 53 54 + struct coroutine *active_parent; 52 55 struct coroutine *prev; 53 56 struct coroutine *next; 54 57 ··· 64 67 bool is_done; 65 68 bool mco_started; 66 69 bool is_ready; 70 + bool did_suspend; 67 71 } coroutine_t; 68 72 69 73 typedef struct { ··· 71 75 coroutine_t *tail; 72 76 } coroutine_queue_t; 73 77 78 + typedef enum { 79 + JS_AWAIT_PENDING = 0, 80 + JS_AWAIT_FULFILLED, 81 + JS_AWAIT_REJECTED, 82 + } js_await_state_t; 83 + 84 + typedef struct { 85 + js_await_state_t state; 86 + ant_value_t value; 87 + } js_await_result_t; 88 + 74 89 extern coroutine_queue_t pending_coroutines; 75 90 extern uint32_t coros_this_tick; 76 91 ··· 81 96 ant_value_t start_async_in_coroutine(ant_t *js, const char *code, size_t code_len, ant_value_t closure_scope, ant_value_t *args, int nargs); 82 97 ant_value_t resume_coroutine_wrapper(ant_t *js, ant_value_t *args, int nargs); 83 98 ant_value_t reject_coroutine_wrapper(ant_t *js, ant_value_t *args, int nargs); 99 + 100 + js_await_result_t js_promise_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro); 101 + void settle_and_resume_coroutine(ant_t *js, coroutine_t *coro, ant_value_t value, bool is_error); 84 102 85 103 bool has_ready_coroutines(void); 86 104 bool has_pending_coroutines(void);
+209 -46
src/ant.c
··· 10 10 #include "tokens.h" 11 11 #include "common.h" 12 12 #include "utils.h" 13 + #include "sugar.h" 13 14 #include "base64.h" 14 15 #include "runtime.h" 15 16 #include "internal.h" ··· 150 151 return true; 151 152 } 152 153 153 - typedef struct promise_handler { 154 - ant_value_t onFulfilled; 155 - ant_value_t onRejected; 156 - ant_value_t nextPromise; 157 - } promise_handler_t; 158 - 159 154 static const UT_icd promise_handler_icd = { 160 155 .sz = sizeof(promise_handler_t), 161 156 .init = NULL, ··· 168 163 169 164 static ant_promise_state_t *get_promise_data(ant_t *js, ant_value_t promise, bool create); 170 165 static ant_proxy_state_t *get_proxy_data(ant_value_t obj); 166 + 167 + static inline uint32_t promise_handler_count(const ant_promise_state_t *pd) { 168 + return pd ? (uint32_t)pd->handler_count : 0; 169 + } 170 + 171 + static inline bool promise_has_handlers(const ant_promise_state_t *pd) { 172 + return promise_handler_count(pd) != 0; 173 + } 174 + 175 + static bool promise_handler_append(ant_promise_state_t *pd, const promise_handler_t *handler) { 176 + if (!pd || !handler) return false; 177 + 178 + if (pd->handler_count == 0) { 179 + pd->inline_handler = *handler; 180 + pd->handler_count = 1; 181 + return true; 182 + } 183 + 184 + if (pd->handler_count == 1) { 185 + if (!pd->handlers) utarray_new(pd->handlers, &promise_handler_icd); 186 + if (!pd->handlers) return false; 187 + utarray_push_back(pd->handlers, &pd->inline_handler); 188 + utarray_push_back(pd->handlers, handler); 189 + pd->handler_count = 2; 190 + return true; 191 + } 192 + 193 + if (!pd->handlers) utarray_new(pd->handlers, &promise_handler_icd); 194 + if (!pd->handlers) return false; 195 + 196 + utarray_push_back(pd->handlers, handler); 197 + pd->handler_count++; 198 + 199 + return true; 200 + } 201 + 202 + static inline promise_handler_t *promise_handler_at(ant_promise_state_t *pd, uint32_t index) { 203 + if (!pd || index >= pd->handler_count) return NULL; 204 + if (pd->handler_count == 1) return &pd->inline_handler; 205 + if (!pd->handlers) return NULL; 206 + return (promise_handler_t *)utarray_eltptr(pd->handlers, (unsigned int)index); 207 + } 208 + 209 + static inline void promise_handlers_clear(ant_promise_state_t *pd) { 210 + if (!pd) return; 211 + pd->handler_count = 0; 212 + pd->inline_handler = (promise_handler_t){ 0 }; 213 + if (pd->handlers) utarray_clear(pd->handlers); 214 + } 171 215 172 216 typedef struct { 173 217 bool has_getter; ··· 437 481 [T_NUM] = "number", [T_BIGINT] = "bigint", [T_STR] = "string", 438 482 [T_SYMBOL] = "symbol", [T_OBJ] = "object", [T_ARR] = "object", 439 483 [T_FUNC] = "function", [T_CFUNC] = "function", [T_CLOSURE] = "closure", 440 - [T_PROMISE] = "promise", [T_GENERATOR] = "generator", 484 + [T_PROMISE] = "object", [T_GENERATOR] = "generator", 441 485 [T_ERR] = "err", [T_TYPEDARRAY] = "typedarray", 442 486 [T_FFI] = "ffi", [T_NTARG] = "ntarg" 443 487 }; ··· 9740 9784 if (!entry) return NULL; 9741 9785 entry->promise_id = next_promise_id++; 9742 9786 entry->trigger_parent = js_mkundef(); 9787 + entry->inline_handler = (promise_handler_t){ 0 }; 9788 + entry->handlers = NULL; 9789 + entry->handler_count = 0; 9743 9790 entry->state = 0; 9744 9791 entry->value = js_mkundef(); 9792 + entry->trigger_queued = false; 9745 9793 entry->has_rejection_handler = false; 9746 9794 entry->processing = false; 9747 9795 entry->unhandled_reported = false; 9748 - utarray_new(entry->handlers, &promise_handler_icd); 9749 9796 obj->promise_state = entry; 9750 9797 obj->type_tag = T_PROMISE; 9751 9798 return entry; ··· 9754 9801 static uint32_t get_promise_id(ant_t *js, ant_value_t p) { 9755 9802 ant_promise_state_t *pd = get_promise_data(js, p, false); 9756 9803 return pd ? pd->promise_id : 0; 9804 + } 9805 + 9806 + bool js_mark_promise_trigger_queued(ant_t *js, ant_value_t promise) { 9807 + ant_promise_state_t *pd = get_promise_data(js, promise, false); 9808 + if (!pd) return true; 9809 + if (pd->trigger_queued) return false; 9810 + pd->trigger_queued = true; 9811 + return true; 9812 + } 9813 + 9814 + void js_mark_promise_trigger_dequeued(ant_t *js, ant_value_t promise) { 9815 + ant_promise_state_t *pd = get_promise_data(js, promise, false); 9816 + if (!pd) return; 9817 + pd->trigger_queued = false; 9757 9818 } 9758 9819 9759 9820 static ant_value_t make_data_cfunc( ··· 9808 9869 return result; 9809 9870 } 9810 9871 9811 - static inline void trigger_handlers(ant_t *js, ant_value_t p) { 9812 - queue_promise_trigger(p); 9872 + js_await_result_t js_promise_await_coroutine(ant_t *js, ant_value_t promise, coroutine_t *coro) { 9873 + js_await_result_t result = { 9874 + .state = JS_AWAIT_PENDING, 9875 + .value = js_mkundef(), 9876 + }; 9877 + 9878 + if (vtype(promise) != T_PROMISE || !coro) return result; 9879 + 9880 + ant_promise_state_t *pd = get_promise_data(js, promise, false); 9881 + if (!pd) { 9882 + result.state = JS_AWAIT_FULFILLED; 9883 + return result; 9884 + } 9885 + 9886 + if (pd->state == 1) { 9887 + result.state = JS_AWAIT_FULFILLED; 9888 + result.value = pd->value; 9889 + return result; 9890 + } 9891 + 9892 + if (pd->state == 2) { 9893 + if (pd->unhandled_reported) js_fire_rejection_handled(js, promise, pd->value); 9894 + pd->has_rejection_handler = true; 9895 + pd->unhandled_reported = false; 9896 + result.state = JS_AWAIT_REJECTED; 9897 + result.value = pd->value; 9898 + return result; 9899 + } 9900 + 9901 + promise_handler_t h = { js_mkundef(), js_mkundef(), js_mkundef(), coro }; 9902 + if (!promise_handler_append(pd, &h)) { 9903 + result.state = JS_AWAIT_REJECTED; 9904 + result.value = js_mkerr(js, "out of memory"); 9905 + return result; 9906 + } 9907 + 9908 + if (pd->unhandled_reported) js_fire_rejection_handled(js, promise, pd->value); 9909 + pd->has_rejection_handler = true; 9910 + pd->unhandled_reported = false; 9911 + 9912 + gc_root_pending_promise(js_obj_ptr(js_as_obj(promise))); 9913 + return result; 9813 9914 } 9814 9915 9815 9916 void js_process_promise_handlers(ant_t *js, ant_value_t promise) { ··· 9820 9921 int state = pd->state; 9821 9922 ant_value_t val = pd->value; 9822 9923 9823 - unsigned int len = utarray_len(pd->handlers); 9824 - if (len == 0) { return; } 9924 + uint32_t len = promise_handler_count(pd); 9925 + if (len == 0) return; 9825 9926 gc_root_pending_promise(pobj); 9826 9927 pd->processing = true; 9827 9928 9828 - for (unsigned int i = 0; i < len; i++) { 9829 - promise_handler_t *h = (promise_handler_t *)utarray_eltptr(pd->handlers, i); 9929 + for (uint32_t i = 0; i < len; i++) { 9930 + promise_handler_t *h = promise_handler_at(pd, i); 9931 + if (!h) continue; 9932 + if (h->await_coro) { 9933 + settle_and_resume_coroutine(js, h->await_coro, val, state != 1); 9934 + continue; 9935 + } 9936 + 9830 9937 ant_value_t handler = (state == 1) ? h->onFulfilled : h->onRejected; 9831 9938 9832 9939 if (vtype(handler) == T_FUNC || vtype(handler) == T_CFUNC) { ··· 9854 9961 } 9855 9962 9856 9963 pd->processing = false; 9857 - utarray_clear(pd->handlers); 9964 + promise_handlers_clear(pd); 9858 9965 gc_unroot_pending_promise(js_obj_ptr(promise)); 9859 9966 } 9860 9967 ··· 9878 9985 return; 9879 9986 } 9880 9987 9881 - ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal); 9882 - GC_ROOT_PIN(js, res_fn); 9883 - if (is_err(res_fn)) { 9884 - js_reject_promise(js, p, res_fn); 9988 + ant_promise_state_t *src_pd = get_promise_data(js, val, false); 9989 + if (!src_pd) { 9990 + pd->state = 1; 9991 + pd->value = val; 9992 + gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), val); 9885 9993 GC_ROOT_RESTORE(js, root_mark); 9886 9994 return; 9887 9995 } 9888 9996 9889 - ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal); 9890 - GC_ROOT_PIN(js, rej_fn); 9891 - if (is_err(rej_fn)) { 9892 - js_reject_promise(js, p, rej_fn); 9997 + pd->trigger_parent = val; 9998 + 9999 + promise_handler_t h = { js_mkundef(), js_mkundef(), p, NULL }; 10000 + if (!promise_handler_append(src_pd, &h)) { 10001 + pd->state = 2; 10002 + pd->value = js_mkerr(js, "out of memory"); 10003 + gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), pd->value); 9893 10004 GC_ROOT_RESTORE(js, root_mark); 9894 10005 return; 9895 10006 } 10007 + 10008 + gc_write_barrier(js, js_obj_ptr(js_as_obj(val)), p); 10009 + if (src_pd->unhandled_reported) js_fire_rejection_handled(js, val, src_pd->value); 10010 + 10011 + src_pd->has_rejection_handler = true; 10012 + src_pd->unhandled_reported = false; 9896 10013 9897 - ant_value_t then_prop = js_get(js, val, "then"); 9898 - GC_ROOT_PIN(js, then_prop); 9899 - 9900 - if (vtype(then_prop) == T_FUNC || vtype(then_prop) == T_CFUNC) { 9901 - ant_value_t call_args[] = { res_fn, rej_fn }; 9902 - (void)sv_vm_call(js->vm, js, then_prop, val, call_args, 2, NULL, false); 9903 - GC_ROOT_RESTORE(js, root_mark); 9904 - return; 9905 - } 10014 + if (src_pd->state == 0) gc_root_pending_promise(js_obj_ptr(js_as_obj(val))); 10015 + else queue_promise_trigger(js, val); 10016 + 10017 + GC_ROOT_RESTORE(js, root_mark); 10018 + 10019 + return; 9906 10020 } 9907 10021 9908 10022 pd->state = 1; 9909 10023 pd->value = val; 10024 + 9910 10025 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), val); 9911 - trigger_handlers(js, p); 10026 + if (promise_has_handlers(pd)) queue_promise_trigger(js, p); 9912 10027 GC_ROOT_RESTORE(js, root_mark); 9913 10028 } 9914 10029 ··· 9929 10044 if (js->pending_rejections.len < js->pending_rejections.cap) 9930 10045 js->pending_rejections.items[js->pending_rejections.len++] = p; 9931 10046 9932 - trigger_handlers(js, p); 10047 + queue_promise_trigger(js, p); 9933 10048 } 9934 10049 9935 10050 static ant_value_t builtin_resolve_internal(ant_t *js, ant_value_t *args, int nargs) { ··· 10104 10219 10105 10220 ant_promise_state_t *pd = get_promise_data(js, p, false); 10106 10221 if (pd) { 10107 - promise_handler_t h = { onFulfilled, onRejected, nextP }; 10108 - utarray_push_back(pd->handlers, &h); 10222 + promise_handler_t h = { onFulfilled, onRejected, nextP, NULL }; 10223 + if (!promise_handler_append(pd, &h)) { 10224 + GC_ROOT_RESTORE(js, root_mark); 10225 + return js_mkerr(js, "out of memory"); 10226 + } 10109 10227 10110 10228 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), nextP); 10111 10229 gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), onFulfilled); ··· 10121 10239 gc_root_pending_promise(js_obj_ptr(p)); 10122 10240 } 10123 10241 10124 - if (pd && pd->state != 0) trigger_handlers(js, p); 10242 + if (pd && pd->state != 0) queue_promise_trigger(js, p); 10125 10243 GC_ROOT_RESTORE(js, root_mark); 10244 + 10126 10245 return nextP; 10127 10246 } 10128 10247 ··· 10169 10288 } 10170 10289 GC_ROOT_PIN(js, result); 10171 10290 10172 - if (vtype(result) == T_PROMISE || (vtype(result) == T_OBJ && vtype(js_get(js, result, "then")) == T_FUNC)) { 10291 + if (vtype(result) == T_PROMISE) { 10173 10292 ant_value_t thunk_fn = make_data_cfunc(js, value, finally_value_thunk); 10174 10293 GC_ROOT_PIN(js, thunk_fn); 10294 + 10175 10295 if (is_err(thunk_fn)) { 10176 10296 GC_ROOT_RESTORE(js, root_mark); 10177 10297 return thunk_fn; 10178 10298 } 10179 10299 10180 10300 ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject); 10181 - ant_value_t then_fn = js_get(js, result, "then"); 10182 - GC_ROOT_PIN(js, then_fn); 10301 + ant_value_t ret = js_promise_then(js, result, thunk_fn, identity_rej_fn); 10302 + GC_ROOT_RESTORE(js, root_mark); 10303 + 10304 + return ret; 10305 + } 10306 + 10307 + if (vtype(result) == T_OBJ) { 10308 + ant_value_t then_fn = js_get(js, result, "then"); 10309 + GC_ROOT_PIN(js, then_fn); 10310 + 10311 + if (vtype(then_fn) == T_FUNC || vtype(then_fn) == T_CFUNC) { 10312 + ant_value_t thunk_fn = make_data_cfunc(js, value, finally_value_thunk); 10313 + GC_ROOT_PIN(js, thunk_fn); 10314 + 10315 + if (is_err(thunk_fn)) { 10316 + GC_ROOT_RESTORE(js, root_mark); 10317 + return thunk_fn; 10318 + } 10319 + 10320 + ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject); 10183 10321 ant_value_t call_args[] = { thunk_fn, identity_rej_fn }; 10184 10322 ant_value_t ret = sv_vm_call(js->vm, js, then_fn, result, call_args, 2, NULL, false); 10185 10323 GC_ROOT_RESTORE(js, root_mark); 10324 + 10186 10325 return ret; 10187 - } 10326 + }} 10188 10327 10189 10328 GC_ROOT_RESTORE(js, root_mark); 10190 10329 return value; ··· 10192 10331 10193 10332 static ant_value_t finally_rejected_wrapper(ant_t *js, ant_value_t *args, int nargs) { 10194 10333 GC_ROOT_SAVE(root_mark, js); 10334 + 10195 10335 ant_value_t me = js->current_func; 10196 10336 ant_value_t callback = get_slot(me, SLOT_DATA); 10197 10337 ant_value_t reason = nargs > 0 ? args[0] : js_mkundef(); 10338 + 10198 10339 GC_ROOT_PIN(js, callback); 10199 10340 GC_ROOT_PIN(js, reason); 10200 10341 ··· 10208 10349 } 10209 10350 GC_ROOT_PIN(js, result); 10210 10351 10211 - if (vtype(result) == T_PROMISE || (vtype(result) == T_OBJ && vtype(js_get(js, result, "then")) == T_FUNC)) { 10352 + if (vtype(result) == T_PROMISE) { 10353 + ant_value_t thrower_fn = make_data_cfunc(js, reason, finally_thrower); 10354 + GC_ROOT_PIN(js, thrower_fn); 10355 + if (is_err(thrower_fn)) { 10356 + GC_ROOT_RESTORE(js, root_mark); 10357 + return thrower_fn; 10358 + } 10359 + ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject); 10360 + ant_value_t ret = js_promise_then(js, result, thrower_fn, identity_rej_fn); 10361 + GC_ROOT_RESTORE(js, root_mark); 10362 + return ret; 10363 + } 10364 + 10365 + if (vtype(result) == T_OBJ) { 10366 + ant_value_t then_prop = js_get(js, result, "then"); 10367 + GC_ROOT_PIN(js, then_prop); 10368 + 10369 + if (vtype(then_prop) == T_FUNC || vtype(then_prop) == T_CFUNC) { 10212 10370 ant_value_t thrower_fn = make_data_cfunc(js, reason, finally_thrower); 10213 10371 GC_ROOT_PIN(js, thrower_fn); 10372 + 10214 10373 if (is_err(thrower_fn)) { 10215 10374 GC_ROOT_RESTORE(js, root_mark); 10216 10375 return thrower_fn; 10217 10376 } 10377 + 10218 10378 ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject); 10219 - ant_value_t then_prop = js_get(js, result, "then"); 10220 - GC_ROOT_PIN(js, then_prop); 10221 10379 ant_value_t call_args[] = { thrower_fn, identity_rej_fn }; 10222 - 10223 10380 ant_value_t ret = sv_vm_call(js->vm, js, then_prop, result, call_args, 2, NULL, false); 10224 10381 GC_ROOT_RESTORE(js, root_mark); 10382 + 10225 10383 return ret; 10226 - } 10384 + }} 10227 10385 10228 10386 ant_value_t rejected = js_mkpromise(js); 10229 10387 GC_ROOT_PIN(js, rejected); 10230 10388 js_reject_promise(js, rejected, reason); 10231 10389 GC_ROOT_RESTORE(js, root_mark); 10390 + 10232 10391 return rejected; 10233 10392 } 10234 10393 ··· 10254 10413 ant_value_t args_then[] = { fulfilled_fn, rejected_fn }; 10255 10414 ant_value_t ret = builtin_promise_then(js, args_then, 2); 10256 10415 GC_ROOT_RESTORE(js, root_mark); 10416 + 10257 10417 return ret; 10258 10418 } 10259 10419 10260 10420 static ant_value_t builtin_Promise_try(ant_t *js, ant_value_t *args, int nargs) { 10261 10421 if (nargs == 0) return builtin_Promise_resolve(js, args, 0); 10422 + 10262 10423 ant_value_t fn = args[0]; 10263 10424 ant_value_t *call_args = nargs > 1 ? &args[1] : NULL; 10264 10425 int call_nargs = nargs > 1 ? nargs - 1 : 0; 10265 10426 ant_value_t res = sv_vm_call(js->vm, js, fn, js_mkundef(), call_args, call_nargs, NULL, false); 10427 + 10266 10428 if (is_err(res)) { 10267 10429 ant_value_t reject_val = js->thrown_value; 10268 10430 if (vtype(reject_val) == T_UNDEF) reject_val = res; ··· 10272 10434 ant_value_t rej_args[] = { reject_val }; 10273 10435 return builtin_Promise_reject(js, rej_args, 1); 10274 10436 } 10437 + 10275 10438 ant_value_t res_args[] = { res }; 10276 10439 return builtin_Promise_resolve(js, res_args, 1); 10277 10440 }
+50 -36
src/gc/objects.c
··· 121 121 js->remember_set[js->remember_set_len++] = obj; 122 122 } 123 123 124 - typedef struct { 125 - ant_value_t onFulfilled; 126 - ant_value_t onRejected; 127 - ant_value_t nextPromise; 128 - } promise_handler_t; 129 - 130 124 #define GC_MARK_STACK_INIT 4096 131 125 void gc_mark_value(ant_t *js, ant_value_t v); 126 + 127 + static inline void gc_mark_promise_handler(ant_t *js, const promise_handler_t *h) { 128 + if (!h) return; 129 + gc_mark_value(js, h->onFulfilled); 130 + gc_mark_value(js, h->onRejected); 131 + gc_mark_value(js, h->nextPromise); 132 + } 133 + 134 + static inline void gc_mark_promise_handlers(ant_t *js, ant_promise_state_t *pd) { 135 + if (!pd) return; 136 + 137 + if (pd->handler_count == 1) { 138 + gc_mark_promise_handler(js, &pd->inline_handler); 139 + return; 140 + } 141 + 142 + if (pd->handler_count <= 1 || !pd->handlers) return; 143 + 144 + promise_handler_t *h = NULL; 145 + while ((h = (promise_handler_t *)utarray_next(pd->handlers, h))) 146 + gc_mark_promise_handler(js, h); 147 + } 132 148 133 149 static ant_object_t **gc_mark_stack = NULL; 134 150 static size_t gc_mark_sp = 0; ··· 250 266 } 251 267 252 268 if (obj->shape) { 253 - uint32_t count = ant_shape_count(obj->shape); 254 - for (uint32_t i = 0; i < count && i < obj->prop_count; i++) 255 - gc_mark_value(js, ant_object_prop_get_unchecked(obj, i)); 256 - for (uint32_t i = 0; i < count; i++) { 257 - const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, i); 258 - if (prop && prop->has_getter) gc_mark_value(js, prop->getter); 259 - if (prop && prop->has_setter) gc_mark_value(js, prop->setter); 260 - } 261 - } 269 + uint32_t count = ant_shape_count(obj->shape); 270 + for (uint32_t i = 0; i < count && i < obj->prop_count; i++) 271 + gc_mark_value(js, ant_object_prop_get_unchecked(obj, i)); 272 + for (uint32_t i = 0; i < count; i++) { 273 + const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, i); 274 + if (prop && prop->has_getter) gc_mark_value(js, prop->getter); 275 + if (prop && prop->has_setter) gc_mark_value(js, prop->setter); 276 + }} 262 277 263 278 if (obj->extra_slots) { 264 279 ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; ··· 270 285 for (uint32_t i = 0; i < n; i++) gc_mark_value(js, obj->u.array.data[i]); 271 286 } 272 287 273 - if (obj->promise_state) { 274 - gc_mark_value(js, obj->promise_state->value); 275 - gc_mark_value(js, obj->promise_state->trigger_parent); 276 - if (obj->promise_state->handlers) { 277 - promise_handler_t *h = NULL; 278 - while ((h = (promise_handler_t *)utarray_next(obj->promise_state->handlers, h))) { 279 - gc_mark_value(js, h->onFulfilled); 280 - gc_mark_value(js, h->onRejected); 281 - gc_mark_value(js, h->nextPromise); 282 - }} 288 + ant_promise_state_t *pd = obj->promise_state; 289 + if (pd) { 290 + gc_mark_value(js, pd->value); 291 + gc_mark_value(js, pd->trigger_parent); 292 + gc_mark_promise_handlers(js, pd); 283 293 } 284 294 285 295 if (obj->proxy_state) { ··· 404 414 } 405 415 } 406 416 417 + static void gc_mark_coroutine(ant_t *js, coroutine_t *c) { 418 + if (!c) return; 419 + gc_scan_vm_stack(js, c->sv_vm); 420 + gc_mark_value(js, c->this_val); 421 + gc_mark_value(js, c->async_func); 422 + gc_mark_value(js, c->async_promise); 423 + gc_mark_value(js, c->awaited_promise); 424 + gc_mark_value(js, c->result); 425 + gc_mark_value(js, c->yield_value); 426 + gc_mark_value(js, c->super_val); 427 + gc_mark_value(js, c->new_target); 428 + } 429 + 407 430 static void gc_mark_roots(ant_t *js) { 408 431 gc_scan_vm_stack(js, js->vm); 409 432 410 - for (coroutine_t *c = pending_coroutines.head; c; c = c->next) { 411 - gc_scan_vm_stack(js, c->sv_vm); 412 - gc_mark_value(js, c->this_val); 413 - gc_mark_value(js, c->async_func); 414 - gc_mark_value(js, c->async_promise); 415 - gc_mark_value(js, c->awaited_promise); 416 - gc_mark_value(js, c->result); 417 - gc_mark_value(js, c->yield_value); 418 - gc_mark_value(js, c->super_val); 419 - gc_mark_value(js, c->new_target); 420 - } 433 + for (coroutine_t *c = pending_coroutines.head; c; c = c->next) gc_mark_coroutine(js, c); 434 + for (coroutine_t *c = js->active_async_coro; c; c = c->active_parent) gc_mark_coroutine(js, c); 421 435 422 436 gc_mark_value(js, js->global); 423 437 gc_mark_value(js, js->object);
+106 -22
src/http/http1_parser.c
··· 8 8 #include "http/http1_parser.h" 9 9 #include "http/http1_writer.h" 10 10 11 - typedef struct { 12 - ant_http1_parsed_request_t req; 13 - ant_http1_buffer_t method; 14 - ant_http1_buffer_t target; 15 - ant_http1_buffer_t header_field; 16 - ant_http1_buffer_t header_value; 17 - ant_http1_buffer_t body; 18 - ant_http_header_t **header_tail; 19 - bool message_complete; 20 - } parser_ctx_t; 11 + typedef ant_http1_parser_ctx_t parser_ctx_t; 21 12 22 13 static void parser_ctx_free(parser_ctx_t *ctx) { 23 14 if (!ctx) return; ··· 108 99 return HPE_PAUSED; 109 100 } 110 101 102 + static llhttp_settings_t g_request_settings = { 103 + .on_method = parser_on_method, 104 + .on_url = parser_on_url, 105 + .on_header_field = parser_on_header_field, 106 + .on_header_value = parser_on_header_value, 107 + .on_headers_complete = parser_on_headers_complete, 108 + .on_body = parser_on_body, 109 + .on_message_complete = parser_on_message_complete, 110 + }; 111 + 111 112 ant_http1_parse_result_t ant_http1_parse_request( 112 113 const char *data, 113 114 size_t len, ··· 116 117 const char **error_code 117 118 ) { 118 119 llhttp_t parser; 119 - llhttp_settings_t settings; 120 120 llhttp_errno_t err = HPE_OK; 121 121 parser_ctx_t ctx = {0}; 122 122 123 123 if (error_reason) *error_reason = NULL; 124 124 if (error_code) *error_code = NULL; 125 - 125 + 126 126 memset(out, 0, sizeof(*out)); 127 127 ctx.header_tail = &ctx.req.headers; 128 128 ··· 132 132 ant_http1_buffer_init(&ctx.header_value); 133 133 ant_http1_buffer_init(&ctx.body); 134 134 135 - llhttp_settings_init(&settings); 136 - settings.on_method = parser_on_method; 137 - settings.on_url = parser_on_url; 138 - settings.on_header_field = parser_on_header_field; 139 - settings.on_header_value = parser_on_header_value; 140 - settings.on_headers_complete = parser_on_headers_complete; 141 - settings.on_body = parser_on_body; 142 - settings.on_message_complete = parser_on_message_complete; 143 - 144 - llhttp_init(&parser, HTTP_REQUEST, &settings); 135 + llhttp_init(&parser, HTTP_REQUEST, &g_request_settings); 145 136 parser.data = &ctx; 146 137 147 138 err = llhttp_execute(&parser, data, len); ··· 192 183 ant_http_headers_free(req->headers); 193 184 memset(req, 0, sizeof(*req)); 194 185 } 186 + 187 + void ant_http1_conn_parser_init(ant_http1_conn_parser_t *cp) { 188 + if (!cp) return; 189 + 190 + memset(cp, 0, sizeof(*cp)); 191 + cp->ctx.header_tail = &cp->ctx.req.headers; 192 + llhttp_init(&cp->parser, HTTP_REQUEST, &g_request_settings); 193 + cp->parser.data = &cp->ctx; 194 + } 195 + 196 + void ant_http1_conn_parser_reset(ant_http1_conn_parser_t *cp) { 197 + if (!cp) return; 198 + 199 + ant_http1_buffer_free(&cp->ctx.method); 200 + ant_http1_buffer_free(&cp->ctx.target); 201 + ant_http1_buffer_free(&cp->ctx.header_field); 202 + ant_http1_buffer_free(&cp->ctx.header_value); 203 + ant_http1_buffer_free(&cp->ctx.body); 204 + 205 + memset(&cp->ctx, 0, sizeof(cp->ctx)); 206 + cp->ctx.header_tail = &cp->ctx.req.headers; 207 + cp->fed_len = 0; 208 + llhttp_reset(&cp->parser); 209 + cp->parser.data = &cp->ctx; 210 + } 211 + 212 + void ant_http1_conn_parser_free(ant_http1_conn_parser_t *cp) { 213 + if (!cp) return; 214 + 215 + ant_http1_buffer_free(&cp->ctx.method); 216 + ant_http1_buffer_free(&cp->ctx.target); 217 + ant_http1_buffer_free(&cp->ctx.header_field); 218 + ant_http1_buffer_free(&cp->ctx.header_value); 219 + ant_http1_buffer_free(&cp->ctx.body); 220 + ant_http1_free_parsed_request(&cp->ctx.req); 221 + } 222 + 223 + ant_http1_parse_result_t ant_http1_conn_parser_execute( 224 + ant_http1_conn_parser_t *cp, 225 + const char *data, 226 + size_t len, 227 + ant_http1_parsed_request_t *out, 228 + size_t *consumed_out 229 + ) { 230 + const char *new_data = NULL; 231 + size_t new_len = 0; 232 + size_t old_fed = 0; 233 + 234 + const char *errpos = NULL; 235 + llhttp_errno_t err = HPE_OK; 236 + 237 + if (!cp || !data || !out) return ANT_HTTP1_PARSE_ERROR; 238 + 239 + memset(out, 0, sizeof(*out)); 240 + if (consumed_out) *consumed_out = 0; 241 + if (cp->fed_len > len) return ANT_HTTP1_PARSE_ERROR; 242 + 243 + new_data = data + cp->fed_len; 244 + new_len = len - cp->fed_len; 245 + old_fed = cp->fed_len; 246 + if (new_len == 0) return ANT_HTTP1_PARSE_INCOMPLETE; 247 + 248 + err = llhttp_execute(&cp->parser, new_data, new_len); 249 + errpos = llhttp_get_error_pos(&cp->parser); 250 + if (errpos && consumed_out) 251 + *consumed_out = (size_t)(errpos - new_data) + old_fed; 252 + cp->fed_len = len; 253 + 254 + if (err != HPE_OK && err != HPE_PAUSED) 255 + return ANT_HTTP1_PARSE_ERROR; 256 + 257 + if (!cp->ctx.message_complete) 258 + return ANT_HTTP1_PARSE_INCOMPLETE; 259 + 260 + cp->ctx.req.method = ant_http1_buffer_take(&cp->ctx.method, NULL); 261 + cp->ctx.req.target = ant_http1_buffer_take(&cp->ctx.target, NULL); 262 + cp->ctx.req.body = (uint8_t *)ant_http1_buffer_take(&cp->ctx.body, &cp->ctx.req.body_len); 263 + 264 + if (!cp->ctx.req.method || !cp->ctx.req.target) 265 + return ANT_HTTP1_PARSE_ERROR; 266 + 267 + cp->ctx.req.absolute_target = 268 + strncmp(cp->ctx.req.target, "http://", 7) == 0 || 269 + strncmp(cp->ctx.req.target, "https://", 8) == 0; 270 + 271 + cp->ctx.req.keep_alive = llhttp_should_keep_alive(&cp->parser) == 1; 272 + cp->ctx.req.http_major = cp->parser.http_major; 273 + cp->ctx.req.http_minor = cp->parser.http_minor; 274 + 275 + *out = cp->ctx.req; 276 + memset(&cp->ctx.req, 0, sizeof(cp->ctx.req)); 277 + return ANT_HTTP1_PARSE_OK; 278 + }
+9 -6
src/modules/fetch.c
··· 61 61 } 62 62 63 63 static void destroy_fetch_request(fetch_request_t *req) { 64 + ant_value_t signal = 0; 65 + 64 66 if (!req) return; 67 + signal = request_get_signal(req->js, req->request_obj); 65 68 66 - if (abort_signal_is_signal(request_get_signal(req->request_obj)) && is_callable(req->abort_listener)) 67 - abort_signal_remove_listener(req->js, request_get_signal(req->request_obj), req->abort_listener); 69 + if (abort_signal_is_signal(signal) && is_callable(req->abort_listener)) 70 + abort_signal_remove_listener(req->js, signal, req->abort_listener); 68 71 69 72 remove_pending_request(req); 70 73 free(req); ··· 348 351 349 352 static ant_value_t fetch_transport_reason(fetch_request_t *req, ant_http_result_t result, const char *error_message) { 350 353 if (result == ANT_HTTP_RESULT_ABORTED && req->aborted) { 351 - ant_value_t signal = request_get_signal(req->request_obj); 354 + ant_value_t signal = request_get_signal(req->js, req->request_obj); 352 355 return abort_signal_get_reason(signal); 353 356 } 354 357 ··· 632 635 633 636 if (!req || req->aborted) return js_mkundef(); 634 637 req->aborted = true; 635 - signal = request_get_signal(req->request_obj); 638 + signal = request_get_signal(js, req->request_obj); 636 639 reason = abort_signal_get_reason(signal); 637 640 638 641 if (req->http_req) ant_http_request_cancel(req->http_req); ··· 679 682 req->refs = 1; 680 683 utarray_push_back(pending_requests, &req); 681 684 682 - signal = request_get_signal(request_obj); 685 + signal = request_get_signal(js, request_obj); 683 686 if (abort_signal_is_signal(signal)) { 684 687 if (abort_signal_is_aborted(signal)) { 685 688 fetch_reject(req, abort_signal_get_reason(signal)); 686 689 fetch_request_release(req); 687 690 return promise; 688 691 } 689 - 692 + 690 693 req->abort_listener = js_heavy_mkfun(js, fetch_abort_listener, ANT_PTR(req)); 691 694 abort_signal_add_listener(js, signal, req->abort_listener); 692 695 }
+38 -15
src/modules/headers.c
··· 370 370 free(v); 371 371 } 372 372 373 - static ant_value_t headers_append_pair(ant_t *js, hdr_list_t *l, ant_value_t name_v, ant_value_t value_v) { 374 - if (vtype(name_v) != T_STR) { 375 - name_v = js_tostring_val(js, name_v); 376 - if (is_err(name_v)) return name_v; 377 - } 378 - 379 - if (vtype(value_v) != T_STR) { 380 - value_v = js_tostring_val(js, value_v); 381 - if (is_err(value_v)) return value_v; 382 - } 383 - 384 - const char *name = js_getstr(js, name_v, NULL); 385 - const char *value = js_getstr(js, value_v, NULL); 386 - 373 + static ant_value_t headers_append_name_value(ant_t *js, hdr_list_t *l, const char *name, const char *value) { 387 374 if (!is_valid_name(name)) 388 375 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid header name: %s", name ? name : ""); 389 376 ··· 402 389 return js_mkundef(); 403 390 } 404 391 392 + static ant_value_t headers_append_pair(ant_t *js, hdr_list_t *l, ant_value_t name_v, ant_value_t value_v) { 393 + const char *name = NULL; 394 + const char *value = NULL; 395 + 396 + if (vtype(name_v) != T_STR) { 397 + name_v = js_tostring_val(js, name_v); 398 + if (is_err(name_v)) return name_v; 399 + } 400 + 401 + if (vtype(value_v) != T_STR) { 402 + value_v = js_tostring_val(js, value_v); 403 + if (is_err(value_v)) return value_v; 404 + } 405 + 406 + name = js_getstr(js, name_v, NULL); 407 + value = js_getstr(js, value_v, NULL); 408 + return headers_append_name_value(js, l, name, value); 409 + } 410 + 405 411 ant_value_t headers_append_value(ant_t *js, ant_value_t hdrs, ant_value_t name_v, ant_value_t value_v) { 406 412 hdr_list_t *l = get_list(hdrs); 413 + ant_value_t r = 0; 414 + 407 415 if (!l) return js_mkerr(js, "Invalid Headers object"); 408 - ant_value_t r = headers_append_pair(js, l, name_v, value_v); 416 + r = headers_append_pair(js, l, name_v, value_v); 417 + 409 418 if (is_err(r)) return r; 410 419 list_apply_guard(l, get_guard(hdrs)); 420 + 421 + return js_mkundef(); 422 + } 423 + 424 + ant_value_t headers_append_literal(ant_t *js, ant_value_t hdrs, const char *name, const char *value) { 425 + hdr_list_t *l = get_list(hdrs); 426 + ant_value_t r = 0; 427 + 428 + if (!l) return js_mkerr(js, "Invalid Headers object"); 429 + r = headers_append_name_value(js, l, name, value); 430 + 431 + if (is_err(r)) return r; 432 + list_apply_guard(l, get_guard(hdrs)); 433 + 411 434 return js_mkundef(); 412 435 } 413 436
+186 -105
src/modules/io.c
··· 10 10 11 11 #include "common.h" 12 12 #include "errors.h" 13 + #include "output.h" 13 14 #include "internal.h" 14 15 #include "runtime.h" 15 16 #include "silver/engine.h" ··· 33 34 return c >= '0' && c <= '9'; 34 35 } 35 36 36 - static void io_print(const char *str, FILE *stream) { 37 + static bool io_print_to_output(const char *str, ant_output_stream_t *out) { 37 38 if (!io_no_color) { 38 - fputs(str, stream); 39 - return; 39 + return ant_output_stream_append_cstr(out, str); 40 40 } 41 41 42 42 static void *states[] = {&&normal, &&esc, &&csi, &&done}; ··· 48 48 c = *p++; 49 49 if (!c) goto *states[3]; 50 50 if (c == '\x1b') goto *states[1]; 51 - fputc(c, stream); 51 + if (!ant_output_stream_putc(out, c)) return false; 52 52 goto *states[0]; 53 53 } 54 54 ··· 56 56 c = *p++; 57 57 if (!c) goto *states[3]; 58 58 if (c == '[') goto *states[2]; 59 - fputc('\x1b', stream); 60 - fputc(c, stream); 59 + if (!ant_output_stream_putc(out, '\x1b')) return false; 60 + if (!ant_output_stream_putc(out, c)) return false; 61 61 goto *states[0]; 62 62 } 63 63 ··· 65 65 c = *p++; 66 66 if (!c) goto *states[3]; 67 67 if ((c >= '0' && c <= '9') || c == ';') goto *states[2]; 68 - if (c != 'm') fputc(c, stream); 68 + if (c != 'm' && !ant_output_stream_putc(out, c)) return false; 69 69 goto *states[0]; 70 70 } 71 71 72 - done: return; 72 + done: return true; 73 73 } 74 74 75 75 enum char_class { ··· 143 143 144 144 #define KEYWORD(kw, color) \ 145 145 if (memcmp(p, kw, sizeof(kw) - 1) == 0 && !isalnum((unsigned char)p[sizeof(kw) - 1]) && p[sizeof(kw) - 1] != '_') { \ 146 - fputs(color, stream); fputs(kw, stream); fputs(C_RESET, stream); \ 146 + ant_output_stream_append_cstr(out, color); ant_output_stream_append_cstr(out, kw); ant_output_stream_append_cstr(out, C_RESET); \ 147 147 p += sizeof(kw) - 1; goto next; \ 148 148 } 149 149 150 150 #define EMIT_UNTIL(end_char, color) \ 151 - fputs(color, stream); \ 152 - while (*p && *p != end_char) fputc(*p++, stream); \ 153 - if (*p == end_char) fputc(*p++, stream); \ 154 - fputs(C_RESET, stream); goto next; 151 + ant_output_stream_append_cstr(out, color); \ 152 + while (*p && *p != end_char) ant_output_stream_putc(out, *p++); \ 153 + if (*p == end_char) ant_output_stream_putc(out, *p++); \ 154 + ant_output_stream_append_cstr(out, C_RESET); goto next; 155 155 156 156 #define EMIT_TYPE(tag, len, color) \ 157 157 if (!(is_key && brace_depth > 0) && memcmp(p, tag, len) == 0) { \ 158 - fputs(color, stream); fputs(tag, stream); fputs(C_RESET, stream); \ 158 + ant_output_stream_append_cstr(out, color); ant_output_stream_append_cstr(out, tag); ant_output_stream_append_cstr(out, C_RESET); \ 159 159 p += len; goto next; \ 160 160 } 161 161 162 - void print_value_colored(const char *str, FILE *stream) { 163 - if (io_no_color) { io_print(str, stream); return; } 162 + static void print_value_colored_to_output(const char *str, ant_output_stream_t *out) { 163 + if (io_no_color) { io_print_to_output(str, out); return; } 164 164 165 165 static void *dispatch[] = { 166 166 [CC_NUL] = &&done, [CC_QUOTE] = &&quote, [CC_ESCAPE] = &&other, ··· 186 186 187 187 quote: 188 188 string_char = *p; 189 - fputs((is_key && brace_depth > 0) ? JSON_KEY : JSON_STRING, stream); 190 - fputc(*p++, stream); 189 + ant_output_stream_append_cstr(out, (is_key && brace_depth > 0) ? JSON_KEY : JSON_STRING); 190 + ant_output_stream_putc(out, *p++); 191 191 while (*p) { 192 - if (*p == '\\' && p[1]) { fputc(*p++, stream); fputc(*p++, stream); continue; } 193 - if (*p == string_char) { fputc(*p++, stream); break; } 194 - fputc(*p++, stream); 192 + if (*p == '\\' && p[1]) { ant_output_stream_putc(out, *p++); ant_output_stream_putc(out, *p++); continue; } 193 + if (*p == string_char) { ant_output_stream_putc(out, *p++); break; } 194 + ant_output_stream_putc(out, *p++); 195 195 } 196 - fputs(C_RESET, stream); 196 + ant_output_stream_append_cstr(out, C_RESET); 197 197 goto next; 198 198 199 199 lbrace: 200 - fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); 200 + ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); 201 201 brace_depth++; is_key = true; goto next; 202 202 203 203 rbrace: 204 - fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); 204 + ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); 205 205 brace_depth--; is_key = false; goto next; 206 206 207 207 lbrack: ··· 218 218 case 'U': if (memcmp(p + 2, "int8Contents]", 13) == 0) { EMIT_UNTIL(']', JSON_FUNC) } break; 219 219 case 'P': if (memcmp(p + 2, "romise]", 7) == 0) { EMIT_UNTIL(']', JSON_TAG) } break; 220 220 } 221 - fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); 221 + ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); 222 222 array_depth++; is_key = false; goto next; 223 223 224 224 rbrack: 225 - fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); 225 + ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); 226 226 array_depth--; is_key = false; goto next; 227 227 228 228 colon: 229 - fputc(*p++, stream); is_key = false; goto next; 229 + ant_output_stream_putc(out, *p++); is_key = false; goto next; 230 230 231 231 separator: 232 - fputc(*p++, stream); 232 + ant_output_stream_putc(out, *p++); 233 233 is_key = (brace_depth > 0 && array_depth == 0); 234 234 goto next; 235 235 236 - number: 237 - { 236 + number: { 238 237 int iso_len = io_iso_utc_token_len(p); 239 238 if (iso_len > 0) { 240 - fputs(C_MAGENTA, stream); 241 - for (int k = 0; k < iso_len; k++) fputc(*p++, stream); 242 - fputs(C_RESET, stream); 239 + ant_output_stream_append_cstr(out, C_MAGENTA); 240 + for (int k = 0; k < iso_len; k++) ant_output_stream_putc(out, *p++); 241 + ant_output_stream_append_cstr(out, C_RESET); 243 242 goto next; 244 243 } 245 244 } 246 - fputs(JSON_NUMBER, stream); 245 + ant_output_stream_append_cstr(out, JSON_NUMBER); 247 246 while ((*p >= '0' && *p <= '9') || *p == '.' || *p == 'e' || *p == 'E' || *p == '+' || *p == '-') 248 - fputc(*p++, stream); 249 - fputs(C_RESET, stream); 247 + ant_output_stream_putc(out, *p++); 248 + ant_output_stream_append_cstr(out, C_RESET); 250 249 goto next; 251 250 252 - minus: 253 - { 251 + minus: { 254 252 int iso_len = io_iso_utc_token_len(p); 255 253 if (iso_len > 0) { 256 - fputs(C_MAGENTA, stream); 257 - for (int k = 0; k < iso_len; k++) fputc(*p++, stream); 258 - fputs(C_RESET, stream); 254 + ant_output_stream_append_cstr(out, C_MAGENTA); 255 + for (int k = 0; k < iso_len; k++) ant_output_stream_putc(out, *p++); 256 + ant_output_stream_append_cstr(out, C_RESET); 259 257 goto next; 260 258 } 261 259 } 262 260 if (memcmp(p + 1, "Infinity", 8) == 0 && !isalnum((unsigned char)p[9]) && p[9] != '_') { 263 - fputs(JSON_NUMBER, stream); fputs("-Infinity", stream); fputs(C_RESET, stream); 261 + ant_output_stream_append_cstr(out, JSON_NUMBER); ant_output_stream_append_cstr(out, "-Infinity"); ant_output_stream_append_cstr(out, C_RESET); 264 262 p += 9; goto next; 265 263 } 266 264 if (p[1] >= '0' && p[1] <= '9') { 267 - fputs(JSON_NUMBER, stream); fputc(*p++, stream); 265 + ant_output_stream_append_cstr(out, JSON_NUMBER); ant_output_stream_putc(out, *p++); 268 266 while ((*p >= '0' && *p <= '9') || *p == '.' || *p == 'e' || *p == 'E' || *p == '+' || *p == '-') 269 - fputc(*p++, stream); 270 - fputs(C_RESET, stream); 267 + ant_output_stream_putc(out, *p++); 268 + ant_output_stream_append_cstr(out, C_RESET); 271 269 goto next; 272 270 } 273 - fputc(*p++, stream); goto next; 271 + ant_output_stream_putc(out, *p++); goto next; 274 272 275 273 lt: 276 274 if (memcmp(p, "<ref", 4) == 0) { EMIT_UNTIL('>', JSON_REF) } ··· 278 276 if (memcmp(p, "<rej", 4) == 0) { is_key = false; EMIT_UNTIL('>', C_CYAN) } 279 277 280 278 if (p[1] == '>' || (isxdigit((unsigned char)p[1]) && isxdigit((unsigned char)p[2]))) { 281 - fputs(JSON_BRACE, stream); fputc(*p++, stream); 282 - fputs(JSON_WHITE, stream); 283 - while (*p && *p != '>') fputc(*p++, stream); 284 - fputs(C_RESET, stream); 285 - if (*p == '>') { fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); } 279 + ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); 280 + ant_output_stream_append_cstr(out, JSON_WHITE); 281 + while (*p && *p != '>') ant_output_stream_putc(out, *p++); 282 + ant_output_stream_append_cstr(out, C_RESET); 283 + if (*p == '>') { ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); } 286 284 goto next; 287 285 } 288 286 289 - fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); 287 + ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); 290 288 goto next; 291 289 292 290 gt: 293 - fputs(JSON_BRACE, stream); fputc(*p++, stream); fputs(C_RESET, stream); 291 + ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); 294 292 goto next; 295 293 296 294 alpha: ··· 309 307 310 308 ident: 311 309 if (is_key && brace_depth > 0) { 312 - fputs(JSON_KEY, stream); 313 - while (isalnum((unsigned char)*p) || *p == '_' || *p == '$') fputc(*p++, stream); 314 - fputs(C_RESET, stream); 310 + ant_output_stream_append_cstr(out, JSON_KEY); 311 + while (isalnum((unsigned char)*p) || *p == '_' || *p == '$') ant_output_stream_putc(out, *p++); 312 + ant_output_stream_append_cstr(out, C_RESET); 315 313 goto next; 316 314 } 317 - fputc(*p++, stream); goto next; 315 + ant_output_stream_putc(out, *p++); goto next; 318 316 319 317 other: 320 318 if (*p == '+') { 321 319 int iso_len = io_iso_utc_token_len(p); 322 320 if (iso_len > 0) { 323 - fputs(C_MAGENTA, stream); 324 - for (int k = 0; k < iso_len; k++) fputc(*p++, stream); 325 - fputs(C_RESET, stream); 321 + ant_output_stream_append_cstr(out, C_MAGENTA); 322 + for (int k = 0; k < iso_len; k++) ant_output_stream_putc(out, *p++); 323 + ant_output_stream_append_cstr(out, C_RESET); 326 324 goto next; 327 325 } 328 326 } 329 - fputc(*p++, stream); goto next; 327 + ant_output_stream_putc(out, *p++); goto next; 330 328 } 331 329 332 330 #undef KEYWORD 333 331 #undef EMIT_UNTIL 332 + #undef EMIT_TYPE 333 + 334 + void print_value_colored(const char *str, FILE *stream) { 335 + ant_output_stream_t *out = ant_output_stream(stream); 336 + ant_output_stream_begin(out); 337 + print_value_colored_to_output(str, out); 338 + ant_output_stream_flush(out); 339 + } 334 340 335 341 void print_repl_value(ant_t *js, ant_value_t val, FILE *stream) { 342 + ant_output_stream_t *out = ant_output_stream(stream); 343 + 336 344 if (vtype(val) == T_STR) { 337 345 char *str = js_getstr(js, val, NULL); 338 - fprintf(stream, "%s'%s'%s\n", C(JSON_STRING), str ? str : "", C(C_RESET)); 346 + ant_output_stream_begin(out); 347 + ant_output_stream_append_cstr(out, C(JSON_STRING)); 348 + ant_output_stream_putc(out, '\''); 349 + ant_output_stream_append_cstr(out, str ? str : ""); 350 + ant_output_stream_putc(out, '\''); 351 + ant_output_stream_append_cstr(out, C(C_RESET)); 352 + ant_output_stream_putc(out, '\n'); 353 + ant_output_stream_flush(out); 339 354 return; 340 355 } 341 356 342 357 if (vtype(val) == T_OBJ && vtype(js_get_slot(val, SLOT_ERR_TYPE)) != T_UNDEF) { 343 - const char *stack = get_str_prop(js, val, "stack", 5, NULL); 344 - if (stack) { io_print(stack, stream); fputc('\n', stream); return; } 345 - } 358 + const char *stack = get_str_prop(js, val, "stack", 5, NULL); 359 + 360 + if (stack) { 361 + ant_output_stream_begin(out); 362 + io_print_to_output(stack, out); 363 + ant_output_stream_putc(out, '\n'); 364 + ant_output_stream_flush(out); 365 + return; 366 + }} 346 367 347 368 char cbuf[512]; 348 369 js_cstr_t cstr = js_to_cstr(js, val, cbuf, sizeof(cbuf)); 349 370 350 - if (vtype(val) == T_ERR) fprintf(stderr, "%s\n", cstr.ptr); else { 351 - print_value_colored(cstr.ptr, stream); 352 - fputc('\n', stream); 371 + if (vtype(val) == T_ERR) { 372 + ant_output_stream_t *err_out = ant_output_stream(stderr); 373 + ant_output_stream_begin(err_out); 374 + ant_output_stream_append_cstr(err_out, cstr.ptr); 375 + ant_output_stream_putc(err_out, '\n'); 376 + ant_output_stream_flush(err_out); 377 + } else { 378 + ant_output_stream_begin(out); 379 + print_value_colored_to_output(cstr.ptr, out); 380 + ant_output_stream_putc(out, '\n'); 381 + ant_output_stream_flush(out); 353 382 } 354 383 355 384 if (cstr.needs_free) free((void *)cstr.ptr); 356 385 } 357 386 358 387 ant_value_t console_print(ant_t *js, ant_value_t *args, int nargs, const char *color, FILE *stream) { 359 - if (color && !io_no_color) fputs(color, stream); 388 + ant_output_stream_t *out = ant_output_stream(stream); 389 + ant_output_stream_begin(out); 390 + if (color && !io_no_color) ant_output_stream_append_cstr(out, color); 360 391 361 392 for (int i = 0; i < nargs; i++) { 362 - if (i) fputc(' ', stream); 363 - 393 + if (i) ant_output_stream_putc(out, ' '); 364 394 if (vtype(args[i]) == T_OBJ) { 365 395 const char *stack = get_str_prop(js, args[i], "stack", 5, NULL); 366 - if (stack) { io_print(stack, stream); continue; } 396 + if (stack) { io_print_to_output(stack, out); continue; } 367 397 } 368 - 398 + 369 399 char cbuf[512]; 370 400 js_cstr_t cstr = js_to_cstr(js, args[i], cbuf, sizeof(cbuf)); 371 - 372 - if (vtype(args[i]) == T_STR) io_print(cstr.ptr, stream); else { 373 - if (color && !io_no_color) fputs(C_RESET, stream); 374 - print_value_colored(cstr.ptr, stream); 375 - if (color && !io_no_color) fputs(color, stream); 401 + 402 + if (vtype(args[i]) == T_STR) io_print_to_output(cstr.ptr, out); else { 403 + if (color && !io_no_color) ant_output_stream_append_cstr(out, C_RESET); 404 + print_value_colored_to_output(cstr.ptr, out); 405 + if (color && !io_no_color) ant_output_stream_append_cstr(out, color); 376 406 } 377 407 378 408 if (cstr.needs_free) free((void *)cstr.ptr); 379 409 } 380 410 381 - if (color && !io_no_color) fputs(C_RESET, stream); 382 - fputc('\n', stream); 411 + if (color && !io_no_color) ant_output_stream_append_cstr(out, C_RESET); 412 + ant_output_stream_putc(out, '\n'); 413 + ant_output_stream_flush(out); 383 414 384 415 return js_mkundef(); 385 416 } 386 417 418 + static void console_write_args_to_output(ant_t *js, ant_output_stream_t *out, ant_value_t *args, int nargs) { 419 + for (int i = 0; i < nargs; i++) { 420 + if (i) ant_output_stream_putc(out, ' '); 421 + 422 + if (vtype(args[i]) == T_OBJ) { 423 + const char *stack = get_str_prop(js, args[i], "stack", 5, NULL); 424 + if (stack) { 425 + io_print_to_output(stack, out); 426 + continue; 427 + }} 428 + 429 + char cbuf[512]; 430 + js_cstr_t cstr = js_to_cstr(js, args[i], cbuf, sizeof(cbuf)); 431 + 432 + if (vtype(args[i]) == T_STR) io_print_to_output(cstr.ptr, out); 433 + else print_value_colored_to_output(cstr.ptr, out); 434 + 435 + if (cstr.needs_free) free((void *)cstr.ptr); 436 + }} 437 + 387 438 static ant_value_t js_console_log(ant_t *js, ant_value_t *args, int nargs) { 388 439 return console_print(js, args, nargs, NULL, stdout); 389 440 } ··· 397 448 } 398 449 399 450 static ant_value_t js_console_assert(ant_t *js, ant_value_t *args, int nargs) { 451 + ant_output_stream_t *out = ant_output_stream(stderr); 452 + 400 453 if (nargs < 1) return js_mkundef(); 401 454 402 455 bool is_truthy = js_truthy(js, args[0]); 403 456 if (is_truthy) return js_mkundef(); 404 457 405 - fputs("Assertion failed", stderr); 458 + ant_output_stream_begin(out); 459 + ant_output_stream_append_cstr(out, "Assertion failed"); 460 + 406 461 if (nargs > 1) { 407 - fputs(": ", stderr); 408 - console_print(js, args + 1, nargs - 1, NULL, stderr); 462 + ant_output_stream_append_cstr(out, ": "); 463 + console_write_args_to_output(js, out, args + 1, nargs - 1); 464 + ant_output_stream_putc(out, '\n'); 465 + ant_output_stream_flush(out); 409 466 return js_mkundef(); 410 467 } 411 468 412 - fputc('\n', stderr); 469 + ant_output_stream_putc(out, '\n'); 470 + ant_output_stream_flush(out); 471 + 413 472 return js_mkundef(); 414 473 } 415 474 416 475 static ant_value_t js_console_trace(ant_t *js, ant_value_t *args, int nargs) { 417 - fputs("Trace", stderr); 476 + ant_output_stream_t *out = ant_output_stream(stderr); 477 + 478 + ant_output_stream_begin(out); 479 + ant_output_stream_append_cstr(out, "Trace"); 480 + 418 481 if (nargs > 0) { 419 - fputs(": ", stderr); 420 - console_print(js, args, nargs, NULL, stderr); 421 - } else fputc('\n', stderr); 482 + ant_output_stream_append_cstr(out, ": "); 483 + console_write_args_to_output(js, out, args, nargs); 484 + ant_output_stream_putc(out, '\n'); 485 + } else ant_output_stream_putc(out, '\n'); 422 486 487 + ant_output_stream_flush(out); 423 488 js_print_stack_trace_vm(js, stderr); 489 + 424 490 return js_mkundef(); 425 491 } 426 492 ··· 434 500 435 501 static ant_value_t js_console_clear(ant_t *js, ant_value_t *args, int nargs) { 436 502 if (!io_no_color) { 437 - fprintf(stdout, "\033[2J\033[H"); 438 - fflush(stdout); 503 + ant_output_stream_t *out = ant_output_stream(stdout); 504 + ant_output_stream_begin(out); 505 + ant_output_stream_append_cstr(out, "\033[2J\033[H"); 506 + ant_output_stream_flush(out); 439 507 } 440 508 return js_mkundef(); 441 509 } ··· 450 518 } 451 519 452 520 for (int i = 0; i < console_timer_count; i++) { 453 - if (strcmp(console_timers[i].label, label) == 0) { 454 - fprintf(stderr, "Timer '%s' already exists\n", label); 455 - return js_mkundef(); 456 - } 457 - } 521 + if (strcmp(console_timers[i].label, label) == 0) { 522 + ant_output_stream_t *out = ant_output_stream(stderr); 523 + ant_output_stream_begin(out); 524 + ant_output_stream_appendf(out, "Timer '%s' already exists\n", label); 525 + ant_output_stream_flush(out); 526 + return js_mkundef(); 527 + }} 458 528 459 529 if (console_timer_count < 64) { 460 530 console_timers[console_timer_count].label = strdup(label); ··· 472 542 } 473 543 474 544 for (int i = 0; i < console_timer_count; i++) { 475 - if (strcmp(console_timers[i].label, label) == 0) { 476 - double elapsed = ((double)uv_hrtime() / 1e6) - console_timers[i].start_time; 477 - fprintf(stdout, "%s: %.3fms\n", label, elapsed); 478 - free(console_timers[i].label); 479 - for (int j = i; j < console_timer_count - 1; j++) { 480 - console_timers[j] = console_timers[j + 1]; 481 - } 482 - console_timer_count--; 483 - return js_mkundef(); 545 + if (strcmp(console_timers[i].label, label) == 0) { 546 + double elapsed = ((double)uv_hrtime() / 1e6) - console_timers[i].start_time; 547 + ant_output_stream_t *out = ant_output_stream(stdout); 548 + 549 + ant_output_stream_begin(out); 550 + ant_output_stream_appendf(out, "%s: %.3fms\n", label, elapsed); 551 + ant_output_stream_flush(out); 552 + free(console_timers[i].label); 553 + 554 + for (int j = i; j < console_timer_count - 1; j++) { 555 + console_timers[j] = console_timers[j + 1]; 484 556 } 557 + 558 + console_timer_count--; 559 + return js_mkundef(); 560 + }} 561 + 562 + { 563 + ant_output_stream_t *out = ant_output_stream(stderr); 564 + ant_output_stream_begin(out); 565 + ant_output_stream_appendf(out, "Timer '%s' does not exist\n", label); 566 + ant_output_stream_flush(out); 485 567 } 486 568 487 - fprintf(stderr, "Timer '%s' does not exist\n", label); 488 569 return js_mkundef(); 489 570 } 490 571
+11 -24
src/modules/navigator.c
··· 184 184 } 185 185 186 186 if (vtype(result) == T_PROMISE) { 187 - ant_value_t then_fn = js_get(js, result, "then"); 188 - int fn_type = vtype(then_fn); 189 - 190 - if (fn_type == T_FUNC || fn_type == T_CFUNC) { 191 - ant_value_t name_str = js_mkstr(js, name, strlen(name)); 192 - 193 - ant_value_t on_resolve = make_lock_handler(js, js_mkfun(lock_then_handler), name_str, outer_promise); 194 - ant_value_t on_reject = make_lock_handler(js, js_mkfun(lock_catch_handler), name_str, outer_promise); 195 - 196 - ant_value_t then_args[2] = { on_resolve, on_reject }; 197 - sv_vm_call(js->vm, js, then_fn, result, then_args, 2, NULL, false); 198 - return; 199 - } 187 + ant_value_t name_str = js_mkstr(js, name, strlen(name)); 188 + ant_value_t on_resolve = make_lock_handler(js, js_mkfun(lock_then_handler), name_str, outer_promise); 189 + ant_value_t on_reject = make_lock_handler(js, js_mkfun(lock_catch_handler), name_str, outer_promise); 190 + js_promise_then(js, result, on_resolve, on_reject); 191 + return; 200 192 } 201 193 202 194 release_lock(name); ··· 283 275 ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), &null_val, 1, NULL, false); 284 276 285 277 if (vtype(result) == T_PROMISE) { 286 - ant_value_t then_fn = js_get(js, result, "then"); 287 - int fn_type = vtype(then_fn); 288 - if (fn_type == T_FUNC || fn_type == T_CFUNC) { 289 - ant_value_t on_resolve = make_lock_handler( 290 - js, js_mkfun(lock_then_handler), 291 - js_mkstr(js, "", 0), promise 292 - ); 293 - sv_vm_call(js->vm, js, then_fn, result, &on_resolve, 1, NULL, false); 294 - return promise; 295 - } 278 + ant_value_t on_resolve = make_lock_handler( 279 + js, js_mkfun(lock_then_handler), 280 + js_mkstr(js, "", 0), promise 281 + ); 282 + js_promise_then(js, result, on_resolve, js_mkundef()); 283 + return promise; 296 284 } 297 285 js_resolve_promise(js, promise, result); 298 286 return promise; ··· 398 386 mark(js, req->promise); 399 387 } 400 388 } 401 -
+17 -8
src/modules/process.c
··· 29 29 30 30 #include "ant.h" 31 31 #include "errors.h" 32 + #include "output.h" 32 33 #include "utils.h" 33 34 #include "internal.h" 34 35 #include "descriptors.h" ··· 800 801 if (nargs < 1) return js_false; 801 802 size_t len = 0; 802 803 char *data = js_getstr(js, args[0], &len); 804 + 805 + ant_output_stream_t *out = NULL; 803 806 if (!data) return js_false; 804 - fwrite(data, 1, len, stdout); 805 - fflush(stdout); 806 - return js_true; 807 + 808 + out = ant_output_stream(stdout); 809 + ant_output_stream_begin(out); 810 + 811 + if (!ant_output_stream_append(out, data, len)) return js_false; 812 + return ant_output_stream_flush(out) ? js_true : js_false; 807 813 } 808 814 809 815 static ant_value_t js_stdout_on(ant_t *js, ant_value_t *args, int nargs) { ··· 900 906 return js_mknum(cols); 901 907 } 902 908 903 - 904 - 905 909 static ant_value_t js_stderr_write(ant_t *js, ant_value_t *args, int nargs) { 906 910 if (nargs < 1) return js_false; 907 911 size_t len = 0; 908 912 char *data = js_getstr(js, args[0], &len); 913 + 914 + ant_output_stream_t *out = NULL; 909 915 if (!data) return js_false; 910 - fwrite(data, 1, len, stderr); 911 - fflush(stderr); 912 - return js_true; 916 + 917 + out = ant_output_stream(stderr); 918 + ant_output_stream_begin(out); 919 + 920 + if (!ant_output_stream_append(out, data, len)) return js_false; 921 + return ant_output_stream_flush(out) ? js_true : js_false; 913 922 } 914 923 915 924 static ant_value_t js_stderr_on(ant_t *js, ant_value_t *args, int nargs) {
+137 -24
src/modules/request.c
··· 41 41 return js_get_slot(obj, SLOT_REQUEST_HEADERS); 42 42 } 43 43 44 - ant_value_t request_get_signal(ant_value_t obj) { 45 - return js_get_slot(obj, SLOT_REQUEST_SIGNAL); 44 + ant_value_t request_get_signal(ant_t *js, ant_value_t obj) { 45 + ant_value_t signal = js_get_slot(obj, SLOT_REQUEST_SIGNAL); 46 + 47 + if (vtype(signal) != T_UNDEF) return signal; 48 + signal = abort_signal_create_dependent(js, js_mkundef()); 49 + if (is_err(signal)) return signal; 50 + js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, signal); 51 + return signal; 46 52 } 47 53 48 54 static void data_free(request_data_t *d) { ··· 80 86 return d; 81 87 } 82 88 89 + static request_data_t *data_new_server(const char *method) { 90 + request_data_t *d = calloc(1, sizeof(request_data_t)); 91 + if (!d) return NULL; 92 + 93 + d->method = strdup(method ? method : "GET"); 94 + d->referrer = strdup("client"); 95 + d->referrer_policy = strdup(""); 96 + d->mode = strdup("same-origin"); 97 + d->credentials = strdup("same-origin"); 98 + d->cache = strdup("default"); 99 + d->redirect = strdup("follow"); 100 + d->integrity = strdup(""); 101 + 102 + if (!d->method 103 + || !d->referrer 104 + || !d->referrer_policy 105 + || !d->mode 106 + || !d->credentials 107 + || !d->cache 108 + || !d->redirect 109 + || !d->integrity 110 + ) { data_free(d); return NULL; } 111 + 112 + return d; 113 + } 114 + 115 + static ant_value_t request_create_object(ant_t *js, request_data_t *req, ant_value_t headers_obj, bool create_signal) { 116 + ant_value_t obj = js_mkobj(js); 117 + ant_value_t hdrs = is_object_type(headers_obj) 118 + ? headers_obj 119 + : headers_create_empty(js); 120 + 121 + js_set_proto_init(obj, g_request_proto); 122 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 123 + js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 124 + 125 + headers_set_guard(hdrs, 126 + strcmp(req->mode, "no-cors") == 0 127 + ? HEADERS_GUARD_REQUEST_NO_CORS 128 + : HEADERS_GUARD_REQUEST 129 + ); 130 + 131 + headers_apply_guard(hdrs); 132 + js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, hdrs); 133 + js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, create_signal 134 + ? abort_signal_create_dependent(js, js_mkundef()) 135 + : js_mkundef() 136 + ); 137 + 138 + return obj; 139 + } 140 + 141 + static char *request_build_server_base_url(const char *host, const char *server_hostname, int server_port) { 142 + const char *authority = (host && host[0]) ? host : server_hostname; 143 + size_t authority_len = authority ? strlen(authority) : 0; 144 + bool include_port = (!host || !host[0]) && server_port > 0 && server_port != 80; 145 + size_t cap = sizeof("http://") - 1 + authority_len + 1 + 16 + 1; 146 + 147 + char *base = malloc(cap); 148 + int written = 0; 149 + 150 + if (!base) return NULL; 151 + if (include_port) written = snprintf(base, cap, "http://%s:%d/", authority ? authority : "", server_port); 152 + else written = snprintf(base, cap, "http://%s/", authority ? authority : ""); 153 + 154 + if (written < 0 || (size_t)written >= cap) { 155 + free(base); 156 + return NULL; 157 + } 158 + 159 + return base; 160 + } 161 + 162 + static int request_parse_server_url( 163 + const char *target, 164 + bool absolute_target, 165 + const char *host, 166 + const char *server_hostname, 167 + int server_port, 168 + url_state_t *out 169 + ) { 170 + char *base = NULL; 171 + int rc = 0; 172 + 173 + if (absolute_target) 174 + return parse_url_to_state(target, NULL, out); 175 + 176 + base = request_build_server_base_url(host, server_hostname, server_port); 177 + if (!base) return -1; 178 + 179 + rc = parse_url_to_state(target, base, out); 180 + free(base); 181 + 182 + return rc; 183 + } 184 + 83 185 static request_data_t *data_dup(const request_data_t *src) { 84 186 request_data_t *d = calloc(1, sizeof(request_data_t)); 85 187 if (!d) return NULL; ··· 713 815 REQ_GETTER_END 714 816 715 817 REQ_GETTER_START(signal) 716 - return js_get_slot(this, SLOT_REQUEST_SIGNAL); 818 + return request_get_signal(js, this); 717 819 REQ_GETTER_END 718 820 719 821 REQ_GETTER_START(duplex) ··· 767 869 if (!nd) return js_mkerr(js, "out of memory"); 768 870 769 871 ant_value_t src_headers = js_get_slot(this, SLOT_REQUEST_HEADERS); 770 - ant_value_t src_signal = js_get_slot(this, SLOT_REQUEST_SIGNAL); 872 + ant_value_t src_signal = request_get_signal(js, this); 771 873 772 874 ant_value_t new_headers = headers_create_empty(js); 773 875 if (is_err(new_headers)) { data_free(nd); return new_headers; } ··· 860 962 req = data_dup(src); 861 963 if (!req) return js_mkerr(js, "out of memory"); 862 964 req->body_used = false; 863 - *out_input_signal = js_get_slot(input, SLOT_REQUEST_SIGNAL); 965 + *out_input_signal = request_get_signal(js, input); 864 966 } 865 967 866 968 *out_req = req; ··· 1283 1385 req->body_type = body_type ? strdup(body_type) : NULL; 1284 1386 } 1285 1387 req->body_is_stream = false; 1388 + return request_create_object(js, req, headers_obj, true); 1389 + } 1286 1390 1287 - ant_value_t obj = js_mkobj(js); 1288 - js_set_proto_init(obj, g_request_proto); 1289 - js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_REQUEST)); 1290 - js_set_slot(obj, SLOT_DATA, ANT_PTR(req)); 1391 + ant_value_t request_create_server( 1392 + ant_t *js, 1393 + const char *method, 1394 + const char *target, 1395 + bool absolute_target, 1396 + const char *host, 1397 + const char *server_hostname, 1398 + int server_port, 1399 + ant_value_t headers_obj, 1400 + const uint8_t *body, 1401 + size_t body_len, 1402 + const char *body_type 1403 + ) { 1404 + request_data_t *req = data_new_server(method); 1405 + if (!req) return js_mkerr(js, "out of memory"); 1291 1406 1292 - ant_value_t hdrs = is_object_type(headers_obj) 1293 - ? headers_obj 1294 - : headers_create_empty(js); 1295 - 1296 - headers_set_guard(hdrs, 1297 - strcmp(req->mode, "no-cors") == 0 1298 - ? HEADERS_GUARD_REQUEST_NO_CORS 1299 - : HEADERS_GUARD_REQUEST 1300 - ); 1301 - 1302 - headers_apply_guard(hdrs); 1303 - js_set_slot_wb(js, obj, SLOT_REQUEST_HEADERS, hdrs); 1407 + if (target && request_parse_server_url(target, absolute_target, host, server_hostname, server_port, &req->url) != 0) 1408 + url_state_clear(&req->url); 1409 + 1410 + if (body) req->has_body = true; 1304 1411 1305 - ant_value_t sig = abort_signal_create_dependent(js, js_mkundef()); 1306 - js_set_slot_wb(js, obj, SLOT_REQUEST_SIGNAL, sig); 1412 + if (body && body_len > 0) { 1413 + req->body_data = malloc(body_len); 1414 + if (!req->body_data) { data_free(req); return js_mkerr(js, "out of memory"); } 1415 + memcpy(req->body_data, body, body_len); 1416 + req->body_size = body_len; 1417 + req->body_type = body_type ? strdup(body_type) : NULL; 1418 + } 1419 + req->body_is_stream = false; 1307 1420 1308 - return obj; 1421 + return request_create_object(js, req, headers_obj, false); 1309 1422 } 1310 1423 1311 1424 void init_request_module(void) {
+229 -75
src/modules/server.c
··· 27 27 #include "modules/response.h" 28 28 #include "modules/server.h" 29 29 30 - typedef struct server_runtime_s server_runtime_t; 31 - typedef struct server_request_s server_request_t; 30 + typedef struct server_runtime_s server_runtime_t; 31 + typedef struct server_request_s server_request_t; 32 + typedef struct server_conn_state_s server_conn_state_t; 32 33 33 34 static server_runtime_t *g_server = NULL; 34 35 ··· 41 42 SERVER_WRITE_NONE = 0, 42 43 SERVER_WRITE_CLOSE_CLIENT, 43 44 SERVER_WRITE_STREAM_READ, 45 + SERVER_WRITE_KEEP_ALIVE, 44 46 } server_write_action_t; 45 47 46 48 typedef struct { ··· 51 53 52 54 struct server_request_s { 53 55 server_runtime_t *server; 56 + server_conn_state_t *conn_state; 54 57 ant_conn_t *conn; 58 + 55 59 ant_value_t request_obj; 56 60 ant_value_t response_obj; 57 61 ant_value_t response_promise; 58 62 ant_value_t response_reader; 59 63 ant_value_t response_read_promise; 64 + 65 + struct server_request_s *next; 66 + size_t consumed_len; 67 + 60 68 int refs; 69 + bool keep_alive; 61 70 bool response_started; 62 - struct server_request_s *next; 71 + }; 72 + 73 + struct server_conn_state_s { 74 + server_runtime_t *server; 75 + ant_conn_t *conn; 76 + 77 + ant_http1_conn_parser_t parser; 78 + uv_timer_t drain_timer; 79 + server_request_t request; 80 + server_request_t *active_req; 81 + 82 + bool drain_timer_closed; 83 + bool drain_scheduled; 63 84 }; 64 85 65 86 struct server_runtime_s { 66 87 ant_t *js; 88 + 67 89 ant_value_t export_obj; 68 90 ant_value_t fetch_fn; 69 91 ant_value_t server_ctx; 70 92 uv_loop_t *loop; 93 + 71 94 ant_listener_t listener; 72 95 uv_signal_t sigint_handle; 73 96 uv_signal_t sigterm_handle; 74 97 stop_waiter_t *stop_waiters; 75 98 server_request_t *requests; 99 + 76 100 char *hostname; 101 + uint64_t request_timeout_ms; 102 + uint64_t idle_timeout_ms; 103 + 77 104 int port; 78 - int idle_timeout_secs; 79 105 bool sigint_closed; 80 106 bool sigterm_closed; 81 107 bool stopping; ··· 136 162 }} 137 163 } 138 164 165 + static void server_request_reset(server_request_t *req) { 166 + server_runtime_t *server = NULL; 167 + server_conn_state_t *conn_state = NULL; 168 + 169 + if (!req) return; 170 + server = req->server; 171 + conn_state = req->conn_state; 172 + 173 + *req = (server_request_t){ 174 + .server = server, 175 + .conn_state = conn_state, 176 + .conn = conn_state ? conn_state->conn : NULL, 177 + .request_obj = js_mkundef(), 178 + .response_obj = js_mkundef(), 179 + .response_promise = js_mkundef(), 180 + .response_reader = js_mkundef(), 181 + .response_read_promise = js_mkundef(), 182 + }; 183 + } 184 + 185 + static void server_conn_state_maybe_free(server_conn_state_t *cs) { 186 + if (!cs) return; 187 + if (cs->conn) return; 188 + if (cs->active_req) return; 189 + if (cs->request.refs > 0) return; 190 + if (!cs->drain_timer_closed) return; 191 + free(cs); 192 + } 139 193 140 194 static void server_request_release(server_request_t *req) { 141 195 if (!req) return; 142 196 if (--req->refs > 0) return; 143 197 144 198 server_remove_request(req->server, req); 145 - free(req); 199 + server_request_reset(req); 200 + server_conn_state_maybe_free(req->conn_state); 146 201 } 147 202 148 203 static server_request_t *server_find_request(server_runtime_t *server, ant_value_t request_obj) { ··· 206 261 server_begin_stop(server, false); 207 262 } 208 263 209 - static char *server_build_request_url(server_runtime_t *server, const ant_http1_parsed_request_t *req) { 210 - ant_http1_buffer_t url; 211 - ant_http1_buffer_init(&url); 212 - 213 - if (req->absolute_target) return strdup(req->target); 214 - if (!ant_http1_buffer_append_cstr(&url, "http://")) goto oom; 215 - if (req->host && req->host[0]) { 216 - if (!ant_http1_buffer_append_cstr(&url, req->host)) goto oom; 217 - } else { 218 - if (!ant_http1_buffer_append_cstr(&url, server->hostname)) goto oom; 219 - if (server->port != 80 && server->port > 0) { 220 - if (!ant_http1_buffer_appendf(&url, ":%d", server->port)) goto oom; 221 - } 222 - } 223 - 224 - if (!ant_http1_buffer_append_cstr(&url, req->target)) goto oom; 225 - return ant_http1_buffer_take(&url, NULL); 226 - 227 - oom: 228 - ant_http1_buffer_free(&url); 229 - return NULL; 230 - } 231 - 232 264 static ant_value_t server_headers_from_parsed(ant_t *js, const ant_http1_parsed_request_t *parsed) { 233 265 ant_value_t headers = headers_create_empty(js); 234 266 const ant_http_header_t *hdr = NULL; 235 267 236 268 if (is_err(headers)) return headers; 237 269 for (hdr = parsed->headers; hdr; hdr = hdr->next) { 238 - ant_value_t step = headers_append_value( 239 - js, 240 - headers, 241 - js_mkstr(js, hdr->name, strlen(hdr->name)), 242 - js_mkstr(js, hdr->value, strlen(hdr->value)) 243 - ); 270 + ant_value_t step = headers_append_literal(js, headers, hdr->name, hdr->value); 244 271 if (is_err(step)) return step; 245 272 } 246 273 return headers; ··· 277 304 } 278 305 279 306 static void server_start_stream_read(server_request_t *req); 307 + static void server_on_read(ant_conn_t *conn, ssize_t nread, void *user_data); 280 308 static void server_write_cb(ant_conn_t *conn, int status, void *user_data); 281 309 310 + static void server_on_deferred_drain(uv_timer_t *handle) { 311 + server_conn_state_t *cs = handle ? (server_conn_state_t *)handle->data : NULL; 312 + 313 + if (!cs) return; 314 + cs->drain_scheduled = false; 315 + 316 + if (!cs->conn || cs->active_req) return; 317 + if (ant_conn_is_closing(cs->conn)) return; 318 + if (ant_conn_buffer_len(cs->conn) == 0) return; 319 + 320 + server_on_read(cs->conn, (ssize_t)ant_conn_buffer_len(cs->conn), cs->server); 321 + } 322 + 323 + static void server_on_drain_timer_close(uv_handle_t *handle) { 324 + server_conn_state_t *cs = handle ? (server_conn_state_t *)handle->data : NULL; 325 + 326 + if (!cs) return; 327 + cs->drain_timer_closed = true; 328 + cs->drain_scheduled = false; 329 + server_conn_state_maybe_free(cs); 330 + } 331 + 332 + static void server_schedule_drain(server_conn_state_t *cs) { 333 + if (!cs || !cs->conn) return; 334 + if (cs->drain_scheduled) return; 335 + 336 + cs->drain_scheduled = true; 337 + if (uv_timer_start(&cs->drain_timer, server_on_deferred_drain, 0, 0) != 0) 338 + cs->drain_scheduled = false; 339 + } 340 + 282 341 static bool server_queue_write(ant_conn_t *conn, server_request_t *req, char *data, size_t len, server_write_action_t action) { 283 342 server_write_req_t *wr = calloc(1, sizeof(*wr)); 284 343 int rc = 0; ··· 378 437 status_text = (resp->status_text && resp->status_text[0]) ? resp->status_text : ant_http1_default_status_text(resp->status); 379 438 380 439 ant_http1_buffer_init(&buf); 381 - if (!ant_http1_write_response_head(&buf, resp->status, status_text, headers, body_is_stream, resp->body_size, false)) { 440 + if (!ant_http1_write_response_head(&buf, resp->status, status_text, headers, body_is_stream, resp->body_size, req->keep_alive)) { 382 441 ant_http1_buffer_free(&buf); 383 442 ant_conn_close(req->conn); 384 443 return; ··· 391 450 } 392 451 393 452 out = ant_http1_buffer_take(&buf, &out_len); 394 - if (!server_queue_write(req->conn, req, out, out_len, body_is_stream ? SERVER_WRITE_STREAM_READ : SERVER_WRITE_CLOSE_CLIENT)) 453 + if (body_is_stream) { 454 + if (!server_queue_write(req->conn, req, out, out_len, SERVER_WRITE_STREAM_READ)) return; 455 + if (head_only) ant_conn_close(req->conn); 395 456 return; 457 + } 396 458 397 - if (body_is_stream && head_only) ant_conn_close(req->conn); 459 + server_queue_write( 460 + req->conn, 461 + req, out, out_len, 462 + req->keep_alive ? SERVER_WRITE_KEEP_ALIVE : SERVER_WRITE_CLOSE_CLIENT 463 + ); 398 464 } 399 465 400 466 static ant_value_t server_on_response_reject(ant_t *js, ant_value_t *args, int nargs) { ··· 504 570 server_request_release(req); 505 571 return js_mkundef(); 506 572 } 573 + 507 574 out = ant_http1_buffer_take(&buf, &out_len); 508 - server_queue_write(req->conn, req, out, out_len, SERVER_WRITE_CLOSE_CLIENT); 575 + server_queue_write( 576 + req->conn, 577 + req, out, out_len, 578 + req->keep_alive ? SERVER_WRITE_KEEP_ALIVE : SERVER_WRITE_CLOSE_CLIENT 579 + ); 580 + 509 581 server_request_release(req); 510 582 return js_mkundef(); 511 583 } ··· 575 647 static void server_write_cb(ant_conn_t *conn, int status, void *user_data) { 576 648 server_write_req_t *wr = (server_write_req_t *)user_data; 577 649 server_request_t *req = wr->request; 650 + server_conn_state_t *cs = conn ? (server_conn_state_t *)ant_conn_get_user_data(conn) : NULL; 578 651 579 652 if (status < 0 && conn && !ant_conn_is_closing(conn)) 580 653 ant_conn_close(conn); ··· 588 661 } 589 662 server_start_stream_read(req); 590 663 break; 664 + 665 + case SERVER_WRITE_KEEP_ALIVE: 666 + if (cs && req) { 667 + ant_conn_consume(conn, req->consumed_len); 668 + ant_http1_conn_parser_reset(&cs->parser); 669 + cs->active_req = NULL; 670 + ant_conn_set_timeout_ms(conn, cs->server->idle_timeout_ms); 671 + server_request_release(req); 672 + if (ant_conn_buffer_len(conn) > 0) server_schedule_drain(cs); 673 + } 674 + break; 591 675 592 676 case SERVER_WRITE_CLOSE_CLIENT: 593 677 ant_conn_close(conn); ··· 654 738 return promise; 655 739 } 656 740 657 - static void server_process_client_request(ant_conn_t *conn, ant_http1_parsed_request_t *parsed) { 658 - server_runtime_t *server = (server_runtime_t *)ant_listener_get_user_data(ant_conn_listener(conn)); 741 + static void server_process_client_request( 742 + ant_conn_t *conn, 743 + ant_http1_parsed_request_t *parsed, 744 + size_t consumed_len 745 + ) { 746 + server_conn_state_t *cs = (server_conn_state_t *)ant_conn_get_user_data(conn); 747 + server_runtime_t *server = cs ? cs->server : NULL; 748 + server_request_t *req = NULL; 659 749 660 - ant_t *js = server->js; 750 + ant_t *js = NULL; 661 751 ant_value_t headers = 0; 662 752 ant_value_t request_obj = 0; 663 753 ant_value_t result = 0; 664 - 665 - char *url = NULL; 666 - server_request_t *req = NULL; 754 + bool keep_alive = false; 667 755 668 - url = server_build_request_url(server, parsed); 669 - if (!url) { 756 + if (!server || !cs) { 670 757 ant_http1_free_parsed_request(parsed); 671 - server_send_internal_error(conn, NULL); 758 + ant_conn_close(conn); 672 759 return; 673 760 } 674 761 762 + js = server->js; 763 + req = &cs->request; 764 + keep_alive = parsed->keep_alive; 675 765 headers = server_headers_from_parsed(js, parsed); 766 + 676 767 if (is_err(headers)) { 677 - free(url); 678 768 ant_http1_free_parsed_request(parsed); 679 769 server_send_internal_error(conn, NULL); 680 770 return; 681 771 } 682 772 683 - request_obj = request_create(js, parsed->method, url, headers, parsed->body, parsed->body_len, parsed->content_type); 684 - free(url); 773 + request_obj = request_create_server( 774 + js, 775 + parsed->method, 776 + parsed->target, 777 + parsed->absolute_target, 778 + parsed->host, 779 + server->hostname, 780 + server->port, 781 + headers, 782 + parsed->body, 783 + parsed->body_len, 784 + parsed->content_type 785 + ); 685 786 ant_http1_free_parsed_request(parsed); 686 787 687 788 if (is_err(request_obj)) { ··· 689 790 return; 690 791 } 691 792 692 - req = calloc(1, sizeof(*req)); 693 - if (!req) { 694 - server_send_internal_error(conn, NULL); 695 - return; 696 - } 697 - 793 + server_request_reset(req); 698 794 req->server = server; 795 + req->conn_state = cs; 699 796 req->conn = conn; 700 797 req->request_obj = request_obj; 701 - req->response_obj = js_mkundef(); 702 - req->response_promise = js_mkundef(); 703 - req->response_reader = js_mkundef(); 704 - req->response_read_promise = js_mkundef(); 798 + req->consumed_len = consumed_len; 799 + req->keep_alive = keep_alive; 705 800 req->refs = 1; 706 801 req->next = server->requests; 707 802 server->requests = req; 708 - ant_conn_set_user_data(conn, req); 803 + cs->active_req = req; 804 + ant_conn_set_timeout_ms(conn, server->request_timeout_ms); 709 805 710 806 result = server_call_fetch(server, request_obj); 711 807 server_handle_fetch_result(req, result); 712 808 } 713 809 714 810 static void server_on_read(ant_conn_t *conn, ssize_t nread, void *user_data) { 811 + server_conn_state_t *cs = (server_conn_state_t *)ant_conn_get_user_data(conn); 715 812 ant_http1_parsed_request_t parsed = {0}; 716 813 ant_http1_parse_result_t parse_result = ANT_HTTP1_PARSE_INCOMPLETE; 717 - if (!conn) return; 814 + size_t consumed = 0; 815 + 816 + if (!conn || !cs) return; 817 + if (cs->active_req) return; 818 + if (ant_conn_buffer_len(conn) == 0) return; 718 819 719 - parse_result = ant_http1_parse_request(ant_conn_buffer(conn), ant_conn_buffer_len(conn), &parsed, NULL, NULL); 820 + ant_conn_set_timeout_ms(conn, cs->server->request_timeout_ms); 821 + parse_result = ant_http1_conn_parser_execute( 822 + &cs->parser, 823 + ant_conn_buffer(conn), 824 + ant_conn_buffer_len(conn), 825 + &parsed, 826 + &consumed 827 + ); 828 + 720 829 if (parse_result == ANT_HTTP1_PARSE_ERROR) { 721 830 ant_http1_free_parsed_request(&parsed); 722 831 server_send_text_response(conn, 400, "Bad Request", "Bad Request"); 723 832 return; 724 833 } 725 834 726 - if (parse_result == ANT_HTTP1_PARSE_OK) { 727 - ant_conn_pause_read(conn); 728 - server_process_client_request(conn, &parsed); 729 - } 835 + if (parse_result != ANT_HTTP1_PARSE_OK) return; 836 + server_process_client_request(conn, &parsed, consumed); 730 837 } 731 838 732 839 static void server_on_end(ant_conn_t *conn, void *user_data) { ··· 735 842 736 843 static void server_on_conn_close(ant_conn_t *conn, void *user_data) { 737 844 server_runtime_t *server = (server_runtime_t *)user_data; 738 - server_request_t *req = (server_request_t *)ant_conn_get_user_data(conn); 845 + server_conn_state_t *cs = (server_conn_state_t *)ant_conn_get_user_data(conn); 739 846 740 - if (req) { 741 - req->conn = NULL; 847 + if (cs) { 742 848 ant_conn_set_user_data(conn, NULL); 743 - server_request_release(req); 849 + cs->conn = NULL; 850 + ant_http1_conn_parser_free(&cs->parser); 851 + if (!uv_is_closing((uv_handle_t *)&cs->drain_timer)) 852 + uv_close((uv_handle_t *)&cs->drain_timer, server_on_drain_timer_close); 853 + if (cs->active_req) { 854 + cs->active_req->conn = NULL; 855 + cs->active_req = NULL; 856 + server_request_release(&cs->request); 857 + cs = NULL; 858 + } 859 + if (cs) server_conn_state_maybe_free(cs); 744 860 } 745 861 746 862 server_maybe_finish_stop(server); ··· 750 866 server_maybe_finish_stop((server_runtime_t *)user_data); 751 867 } 752 868 869 + static void server_on_accept(ant_listener_t *listener, ant_conn_t *conn, void *user_data) { 870 + server_runtime_t *server = (server_runtime_t *)user_data; 871 + server_conn_state_t *cs = NULL; 872 + 873 + (void)listener; 874 + if (!conn || !server) return; 875 + 876 + cs = calloc(1, sizeof(*cs)); 877 + if (!cs) { 878 + ant_conn_close(conn); 879 + return; 880 + } 881 + 882 + cs->server = server; 883 + cs->conn = conn; 884 + cs->drain_timer_closed = true; 885 + cs->request.server = server; 886 + cs->request.conn_state = cs; 887 + server_request_reset(&cs->request); 888 + 889 + if (uv_timer_init(server->loop, &cs->drain_timer) != 0) { 890 + free(cs); 891 + ant_conn_close(conn); 892 + return; 893 + } 894 + 895 + cs->drain_timer.data = cs; 896 + cs->drain_timer_closed = false; 897 + ant_http1_conn_parser_init(&cs->parser); 898 + ant_conn_set_user_data(conn, cs); 899 + ant_conn_set_no_delay(conn, true); 900 + } 901 + 753 902 static bool server_export_has_fetch_handler(ant_t *js, ant_value_t default_export, bool *looks_like_config) { 754 903 ant_value_t fetch = 0; 755 904 ··· 826 975 .fetch_fn = js_get(js, default_export, "fetch"), 827 976 .hostname = strdup("0.0.0.0"), 828 977 .port = 3000, 829 - .idle_timeout_secs = 10, 978 + .request_timeout_ms = 5000, 979 + .idle_timeout_ms = 5000, 830 980 .loop = uv_default_loop(), 831 981 }; 832 982 ··· 885 1035 } 886 1036 887 1037 if (vtype(timeout_v) != T_UNDEF && vtype(timeout_v) != T_NULL) { 1038 + double timeout = 0; 888 1039 if (vtype(timeout_v) != T_NUM) { 889 1040 free(server->hostname); 890 1041 free(server); 891 1042 return js_mkerr_typed(js, JS_ERR_TYPE, "server idleTimeout must be a number"); 892 1043 } 893 - server->idle_timeout_secs = (int)js_getnum(timeout_v); 894 - if (server->idle_timeout_secs < 0) { 1044 + timeout = js_getnum(timeout_v); 1045 + if (timeout < 0) { 895 1046 free(server->hostname); 896 1047 free(server); 897 1048 return js_mkerr_typed(js, JS_ERR_RANGE, "server idleTimeout must be >= 0"); 898 1049 } 1050 + server->request_timeout_ms = (uint64_t)(timeout * 1000.0); 1051 + server->idle_timeout_ms = server->request_timeout_ms; 899 1052 } 900 1053 901 1054 uv_signal_init(server->loop, &server->sigint_handle); ··· 903 1056 server->sigint_handle.data = server; 904 1057 server->sigterm_handle.data = server; 905 1058 1059 + callbacks.on_accept = server_on_accept; 906 1060 callbacks.on_read = server_on_read; 907 1061 callbacks.on_end = server_on_end; 908 1062 callbacks.on_conn_close = server_on_conn_close; ··· 911 1065 rc = ant_listener_listen_tcp( 912 1066 &server->listener, server->loop, 913 1067 server->hostname, server->port, 914 - 128, (uint64_t)server->idle_timeout_secs * 1000ULL, &callbacks, server 1068 + 128, server->idle_timeout_ms, &callbacks, server 915 1069 ); 916 1070 917 1071 if (rc != 0) {
+43 -6
src/modules/timer.c
··· 299 299 } 300 300 } 301 301 302 - void queue_promise_trigger(ant_value_t promise) { 302 + void queue_promise_trigger(ant_t *js, ant_value_t promise) { 303 + if (!js_mark_promise_trigger_queued(js, promise)) return; 304 + 303 305 microtask_entry_t *entry = ant_calloc(sizeof(microtask_entry_t)); 304 - if (entry == NULL) return; 306 + if (entry == NULL) { 307 + js_mark_promise_trigger_dequeued(js, promise); 308 + return; 309 + } 305 310 306 311 entry->callback = js_mkundef(); 307 312 entry->promise = promise; ··· 316 321 } 317 322 } 318 323 319 - void process_microtasks(ant_t *js) { 324 + static void process_microtasks_internal(ant_t *js, bool check_unhandled_rejections) { 325 + if (!js || js->microtasks_draining) return; 326 + js->microtasks_draining = true; 327 + 328 + microtask_entry_t *batch_stop = timer_state.microtasks_tail; 329 + 320 330 while (timer_state.microtasks != NULL) { 321 331 microtask_entry_t *entry = timer_state.microtasks; 322 332 timer_state.microtasks = entry->next; ··· 324 334 if (timer_state.microtasks == NULL) { 325 335 timer_state.microtasks_tail = NULL; 326 336 } 327 - 337 + 328 338 if (vtype(entry->promise) == T_PROMISE) { 329 339 GC_ROOT_SAVE(root_mark, js); 330 340 ant_value_t promise = entry->promise; 331 341 GC_ROOT_PIN(js, promise); 342 + js_mark_promise_trigger_dequeued(js, promise); 332 343 js_process_promise_handlers(js, promise); 333 344 GC_ROOT_RESTORE(js, root_mark); 334 345 } else { ··· 340 351 GC_ROOT_RESTORE(js, root_mark); 341 352 } 342 353 354 + bool reached_batch_end = (entry == batch_stop); 343 355 free(entry); 356 + if (reached_batch_end) break; 344 357 } 345 - 346 - js_check_unhandled_rejections(js); 358 + 359 + if (check_unhandled_rejections) js_check_unhandled_rejections(js); 360 + js->microtasks_draining = false; 361 + } 362 + 363 + void process_microtasks(ant_t *js) { 364 + process_microtasks_internal(js, true); 365 + } 366 + 367 + bool js_maybe_drain_microtasks(ant_t *js) { 368 + if (!js) return false; 369 + if (js->microtasks_draining) return false; 370 + if (js->vm_exec_depth != 0) return false; 371 + if (!has_pending_microtasks()) return false; 372 + process_microtasks_internal(js, true); 373 + return true; 374 + } 375 + 376 + bool js_maybe_drain_microtasks_after_async_settle(ant_t *js) { 377 + if (!js) return false; 378 + 379 + if (js->microtasks_draining) return false; 380 + if (!has_pending_microtasks()) return false; 381 + 382 + process_microtasks_internal(js, false); 383 + return true; 347 384 } 348 385 349 386 void process_immediates(ant_t *js) {
+1 -2
src/net/connection.c
··· 110 110 111 111 static void ant_conn_restart_timer(ant_conn_t *conn) { 112 112 if (!conn || conn->closing) return; 113 - uv_timer_stop(&conn->timer); 114 - if (conn->timeout_ms == 0) return; 113 + if (conn->timeout_ms == 0) { uv_timer_stop(&conn->timer); return; } 115 114 uv_timer_start(&conn->timer, ant_conn_timeout_cb, conn->timeout_ms, 0); 116 115 } 117 116
+124
src/output.c
··· 1 + #include <compat.h> // IWYU pragma: keep 2 + 3 + #include <stdarg.h> 4 + #include <stdio.h> 5 + #include <stdlib.h> 6 + #include <string.h> 7 + 8 + #include "output.h" 9 + 10 + #define ANT_OUTPUT_INITIAL_CAP 65536 11 + 12 + static ant_output_stream_t g_stdout_writer = { .stream = NULL }; 13 + static ant_output_stream_t g_stderr_writer = { .stream = NULL }; 14 + 15 + ant_output_stream_t *ant_output_stream(FILE *stream) { 16 + ant_output_stream_t *out = (stream == stderr) ? &g_stderr_writer : &g_stdout_writer; 17 + out->stream = stream; 18 + return out; 19 + } 20 + 21 + void ant_output_stream_begin(ant_output_stream_t *out) { 22 + if (!out) return; 23 + out->buffer.len = 0; 24 + out->buffer.failed = false; 25 + } 26 + 27 + bool ant_output_stream_reserve(ant_output_stream_t *out, size_t extra) { 28 + size_t needed = 0; 29 + size_t next_cap = 0; 30 + char *next = NULL; 31 + 32 + if (!out || out->buffer.failed) return false; 33 + 34 + needed = out->buffer.len + extra; 35 + if (needed <= out->buffer.cap) return true; 36 + 37 + next_cap = out->buffer.cap ? out->buffer.cap * 2 : ANT_OUTPUT_INITIAL_CAP; 38 + while (next_cap < needed) next_cap *= 2; 39 + 40 + next = realloc(out->buffer.data, next_cap); 41 + if (!next) { 42 + out->buffer.failed = true; 43 + return false; 44 + } 45 + 46 + out->buffer.data = next; 47 + out->buffer.cap = next_cap; 48 + return true; 49 + } 50 + 51 + bool ant_output_stream_append(ant_output_stream_t *out, const void *data, size_t len) { 52 + if (!ant_output_stream_reserve(out, len)) return false; 53 + if (len > 0) memcpy(out->buffer.data + out->buffer.len, data, len); 54 + out->buffer.len += len; 55 + return true; 56 + } 57 + 58 + bool ant_output_stream_append_cstr(ant_output_stream_t *out, const char *str) { 59 + return ant_output_stream_append(out, str, strlen(str)); 60 + } 61 + 62 + bool ant_output_stream_putc(ant_output_stream_t *out, char ch) { 63 + return ant_output_stream_append(out, &ch, 1); 64 + } 65 + 66 + #pragma GCC diagnostic push 67 + #pragma GCC diagnostic ignored "-Wformat-nonliteral" 68 + 69 + bool ant_output_stream_appendfv(ant_output_stream_t *out, const char *fmt, va_list ap) { 70 + char stack[256]; 71 + va_list ap_copy; 72 + int written = 0; 73 + 74 + if (!out || out->buffer.failed) return false; 75 + 76 + va_copy(ap_copy, ap); 77 + written = vsnprintf(stack, sizeof(stack), fmt, ap_copy); 78 + va_end(ap_copy); 79 + 80 + if (written < 0) { 81 + out->buffer.failed = true; 82 + return false; 83 + } 84 + 85 + if ((size_t)written < sizeof(stack)) 86 + return ant_output_stream_append(out, stack, (size_t)written); 87 + 88 + if (!ant_output_stream_reserve(out, (size_t)written + 1)) return false; 89 + vsnprintf(out->buffer.data + out->buffer.len, out->buffer.cap - out->buffer.len, fmt, ap); 90 + out->buffer.len += (size_t)written; 91 + 92 + return true; 93 + } 94 + 95 + bool ant_output_stream_appendf(ant_output_stream_t *out, const char *fmt, ...) { 96 + va_list ap; 97 + bool ok = false; 98 + 99 + va_start(ap, fmt); 100 + ok = ant_output_stream_appendfv(out, fmt, ap); 101 + va_end(ap); 102 + return ok; 103 + } 104 + 105 + #pragma GCC diagnostic pop 106 + 107 + bool ant_output_stream_flush(ant_output_stream_t *out) { 108 + size_t len = 0; 109 + size_t wrote = 0; 110 + 111 + if (!out || !out->stream) return false; 112 + if (out->buffer.failed) { 113 + out->buffer.len = 0; 114 + out->buffer.failed = false; 115 + return false; 116 + } 117 + if (out->buffer.len == 0) return fflush(out->stream) == 0; 118 + 119 + len = out->buffer.len; 120 + wrote = fwrite(out->buffer.data, 1, len, out->stream); 121 + out->buffer.len = 0; 122 + 123 + return wrote == len && fflush(out->stream) == 0; 124 + }
+25
src/silver/compiler.c
··· 90 90 int param_count; 91 91 92 92 bool is_arrow; 93 + bool is_async; 93 94 bool is_strict; 94 95 sv_compile_mode_t mode; 95 96 ··· 2631 2632 return; 2632 2633 } 2633 2634 2635 + if (expr->type == N_AWAIT && c->is_async && c->try_depth == 0 && !c->is_tla) { 2636 + compile_expr(c, expr->right); 2637 + emit_close_upvals(c); 2638 + emit_op(c, OP_RETURN); 2639 + return; 2640 + } 2641 + 2634 2642 if (is_tail_callable(c, expr)) { 2635 2643 compile_tail_call(c, expr); 2636 2644 return; ··· 4053 4061 comp.enclosing = enclosing; 4054 4062 comp.scope_depth = 0; 4055 4063 comp.is_arrow = !!(node->flags & FN_ARROW); 4064 + comp.is_async = !!(node->flags & FN_ASYNC); 4056 4065 comp.is_strict = enclosing->is_strict; 4057 4066 comp.mode = mode; 4058 4067 comp.strict_args_local = -1; ··· 4428 4437 func->is_arrow = comp.is_arrow; 4429 4438 4430 4439 func->is_async = !!(node->flags & FN_ASYNC); 4440 + func->has_await = false; 4431 4441 func->is_generator = !!(node->flags & FN_GENERATOR); 4432 4442 func->is_method = !!(node->flags & FN_METHOD); 4433 4443 func->is_tla = comp.is_tla; ··· 4443 4453 memcpy(name, enclosing->inferred_name, enclosing->inferred_name_len); 4444 4454 name[enclosing->inferred_name_len] = '\0'; 4445 4455 func->name = name; 4456 + } 4457 + 4458 + if (func->is_async) { 4459 + const uint8_t *ip = func->code; 4460 + const uint8_t *end = func->code + func->code_len; 4461 + while (ip < end) { 4462 + sv_op_t op = (sv_op_t)*ip; 4463 + if (op == OP_AWAIT || op == OP_FOR_AWAIT_OF || op == OP_AWAIT_ITER_NEXT) { 4464 + func->has_await = true; 4465 + break; 4466 + } 4467 + int sz = sv_op_size[op]; 4468 + if (sz <= 0) break; 4469 + ip += sz; 4470 + } 4446 4471 } 4447 4472 4448 4473 free(comp.code);
+80 -20
src/silver/engine.c
··· 244 244 ant_value_t result = sv_execute_frame(vm, func, this_val, super_val, args, argc); 245 245 if (out_this) *out_this = vm->frames[vm->fp].this; 246 246 247 - vm->fp = saved_fp; 247 + if (!vm->suspended) vm->fp = saved_fp; 248 248 249 249 return result; 250 250 } ··· 268 268 269 269 ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant_value_t super_val, ant_value_t *args, int argc) { 270 270 ant_t *js = vm->js; 271 - uint8_t *ip = func->code; 272 - int entry_fp = vm->fp; 271 + bool resuming = vm->suspended && vm->suspended_resume_pending; 272 + uint8_t *ip = resuming ? NULL : func->code; 273 + 274 + int entry_fp = resuming ? vm->suspended_entry_fp : vm->fp; 273 275 ant_value_t vm_result = js_mkundef(); 276 + ant_value_t suspended_resume_value = js_mkundef(); 277 + 278 + bool suspended_resume_is_error = false; 279 + js->vm_exec_depth++; 274 280 275 281 // TODO: shorthand? 276 282 sv_frame_t *frame = &vm->frames[vm->fp]; 277 - frame->ip = ip; 278 - frame->func = func; 279 - frame->this = sv_normalize_this_for_frame(js, func, this); 280 - frame->new_target = js->new_target; 281 - frame->super_val = super_val; 282 - frame->prev_sp = vm->sp; 283 - frame->handler_base = vm->handler_depth; 284 - frame->handler_top = vm->handler_depth; 285 - frame->argc = argc; 286 - frame->completion.kind = SV_COMPLETION_NONE; 287 - frame->completion.value = js_mkundef(); 288 - frame->with_obj = js_mkundef(); 283 + if (!resuming) { 284 + frame->ip = ip; 285 + frame->func = func; 286 + frame->this = sv_normalize_this_for_frame(js, func, this); 287 + frame->new_target = js->new_target; 288 + frame->super_val = super_val; 289 + frame->prev_sp = vm->sp; 290 + frame->handler_base = vm->handler_depth; 291 + frame->handler_top = vm->handler_depth; 292 + frame->argc = argc; 293 + frame->completion.kind = SV_COMPLETION_NONE; 294 + frame->completion.value = js_mkundef(); 295 + frame->with_obj = js_mkundef(); 296 + } else { 297 + func = frame->func; 298 + ip = frame->ip; 299 + suspended_resume_value = vm->suspended_resume_value; 300 + suspended_resume_is_error = vm->suspended_resume_is_error; 301 + vm->suspended = false; 302 + vm->suspended_resume_pending = false; 303 + vm->suspended_resume_is_error = false; 304 + vm->suspended_resume_value = js_mkundef(); 305 + } 289 306 290 307 ant_value_t *entry_bp = NULL; 291 308 ant_value_t *entry_lp = NULL; 309 + 310 + if (!resuming) { 292 311 ant_value_t stage_err = sv_stage_frame_args(vm, js, func, args, argc, &entry_bp, &entry_lp); 293 312 if (is_err(stage_err)) { 294 313 vm_result = stage_err; 295 314 goto sv_leave; 296 - } 315 + }} 316 + 297 317 #ifdef ANT_JIT 298 - if (vm->jit_resume.active) { 318 + if (!resuming && vm->jit_resume.active) { 299 319 ip = func->code + vm->jit_resume.ip_offset; 300 320 frame->ip = ip; 301 321 int64_t rl = ··· 312 332 }} 313 333 } 314 334 #endif 315 - frame->bp = entry_bp; 316 - frame->lp = entry_lp; 335 + 336 + if (!resuming) { 337 + frame->bp = entry_bp; 338 + frame->lp = entry_lp; 339 + } 317 340 318 341 ant_value_t *bp = frame->bp; 319 342 ant_value_t *lp = frame->lp; 320 343 321 344 #ifdef ANT_JIT 322 - if (vm->jit_resume.active) { 345 + if (!resuming && vm->jit_resume.active) { 323 346 for (int64_t i = 0; i < vm->jit_resume.vstack_sp; i++) 324 347 vm->stack[vm->sp++] = vm->jit_resume.vstack[i]; 325 348 ··· 414 437 #else 415 438 #define JIT_OSR_BACK_EDGE() ((void)0) 416 439 #endif 440 + if (resuming) { 441 + if (suspended_resume_is_error) { 442 + sv_err = js_throw(js, suspended_resume_value); 443 + goto sv_throw; 444 + } 445 + vm->stack[vm->sp++] = suspended_resume_value; 446 + } 417 447 DISPATCH(); 418 448 419 449 L_CONST: { sv_op_const(vm, func, ip); NEXT(5); } ··· 1208 1238 L_AWAIT: { 1209 1239 ant_value_t await_val = vm->stack[--vm->sp]; 1210 1240 ant_value_t result = sv_await_value(js, await_val); 1241 + if (vm->suspended) { 1242 + vm->suspended_entry_fp = entry_fp; 1243 + vm->suspended_saved_fp = entry_fp - 1; 1244 + frame->ip = ip + 1; 1245 + goto sv_leave; 1246 + } 1211 1247 if (is_err(result)) { sv_err = result; goto sv_throw; } 1212 1248 vm->stack[vm->sp++] = result; 1213 1249 NEXT(1); ··· 1284 1320 } 1285 1321 1286 1322 sv_leave: 1323 + if (vm->suspended) { 1324 + if (js->vm_exec_depth > 0) js->vm_exec_depth--; 1325 + return vm_result; 1326 + } 1287 1327 for (int f = vm->fp; f >= entry_fp; f--) { 1288 1328 ant_value_t *drop_bp = vm->frames[f].bp; 1289 1329 if (drop_bp) sv_close_upvalues_from_slot(vm, drop_bp); ··· 1291 1331 vm->fp = entry_fp; 1292 1332 vm->sp = vm->frames[entry_fp].prev_sp; 1293 1333 vm->handler_depth = vm->frames[entry_fp].handler_base; 1334 + if (js->vm_exec_depth > 0) js->vm_exec_depth--; 1294 1335 1295 1336 return vm_result; 1296 1337 ··· 1303 1344 (void)bp; 1304 1345 (void)sv_op_size; 1305 1346 } 1347 + 1348 + ant_value_t sv_resume_suspended(sv_vm_t *vm) { 1349 + if (!vm || !vm->suspended || !vm->suspended_resume_pending || vm->fp < 0) 1350 + return mkval(T_ERR, 0); 1351 + 1352 + int saved_fp = vm->suspended_saved_fp; 1353 + sv_frame_t *frame = &vm->frames[vm->fp]; 1354 + ant_value_t result = sv_execute_frame( 1355 + vm, frame->func, frame->this, frame->super_val, NULL, frame->argc 1356 + ); 1357 + 1358 + if (!vm->suspended) { 1359 + vm->fp = saved_fp; 1360 + vm->suspended_entry_fp = 0; 1361 + vm->suspended_saved_fp = -1; 1362 + } 1363 + 1364 + return result; 1365 + }
+244 -28
src/silver/ops/async.h
··· 2 2 #define SV_ASYNC_H 3 3 4 4 #include "ant.h" 5 + #include "gc/roots.h" 5 6 #include "sugar.h" 6 7 #include "silver/engine.h" 7 8 8 9 #include <minicoro.h> 9 - 10 10 typedef struct { 11 11 ant_t *js; 12 12 coroutine_t *coro; ··· 27 27 sv_vm_t *vm; 28 28 } sv_async_ctx_t; 29 29 30 + static inline bool sv_async_func_supports_lazy_start(sv_func_t *func) { 31 + if (!func || !func->has_await || func->is_generator) return false; 32 + 33 + for (int off = 0; off < func->code_len;) { 34 + sv_op_t op = (sv_op_t)func->code[off]; 35 + if ( 36 + op == OP_AWAIT_ITER_NEXT || 37 + op == OP_YIELD || 38 + op == OP_YIELD_STAR || 39 + op == OP_INITIAL_YIELD 40 + ) return false; 41 + 42 + int size = sv_op_size[op]; 43 + if (size <= 0) return false; 44 + 45 + off += size; 46 + } 47 + 48 + return true; 49 + } 30 50 static void sv_mco_async_entry(mco_coro *mco) { 31 51 sv_async_ctx_t *ctx = (sv_async_ctx_t *)mco_get_user_data(mco); 32 52 ant_t *js = ctx->js; ··· 52 72 js->thrown_exists = false; 53 73 js->thrown_value = js_mkundef(); 54 74 js_reject_promise(js, promise, reject_value); 55 - } else js_resolve_promise(js, promise, result); 75 + js_maybe_drain_microtasks_after_async_settle(js); 76 + } else { 77 + js_resolve_promise(js, promise, result); 78 + js_maybe_drain_microtasks_after_async_settle(js); 79 + } 56 80 } 57 81 58 82 typedef struct { ··· 78 102 js->thrown_exists = false; 79 103 js->thrown_value = js_mkundef(); 80 104 js_reject_promise(js, promise, reject_value); 81 - } else js_resolve_promise(js, promise, result); 105 + js_maybe_drain_microtasks_after_async_settle(js); 106 + } else { 107 + js_resolve_promise(js, promise, result); 108 + js_maybe_drain_microtasks_after_async_settle(js); 109 + } 82 110 } 83 111 84 112 static inline ant_value_t sv_start_tla(ant_t *js, sv_func_t *func, ant_value_t this_val) { ··· 89 117 } 90 118 91 119 ant_value_t promise = js_mkpromise(js); 120 + 121 + if (func && (!func->has_await || sv_async_func_supports_lazy_start(func))) { 122 + GC_ROOT_SAVE(root_mark, js); 123 + GC_ROOT_PIN(js, this_val); 124 + 125 + sv_vm_t *async_vm = sv_vm_create(js, SV_VM_ASYNC); 126 + if (!async_vm) { 127 + GC_ROOT_RESTORE(js, root_mark); 128 + return js_mkerr(js, "out of memory for TLA VM"); 129 + } 130 + 131 + coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 132 + if (!coro) { 133 + sv_vm_destroy(async_vm); 134 + GC_ROOT_RESTORE(js, root_mark); 135 + return js_mkerr(js, "out of memory for TLA coroutine"); 136 + } 137 + 138 + GC_ROOT_PIN(js, promise); 139 + *coro = (coroutine_t){ 140 + .js = js, 141 + .type = CORO_ASYNC_AWAIT, 142 + .this_val = this_val, 143 + .super_val = js_mkundef(), 144 + .new_target = js_mkundef(), 145 + .awaited_promise = js_mkundef(), 146 + .result = js_mkundef(), 147 + .async_func = js_mkundef(), 148 + .args = NULL, 149 + .nargs = 0, 150 + .active_parent = NULL, 151 + .is_settled = false, 152 + .is_error = false, 153 + .is_done = false, 154 + .resume_point = 0, 155 + .yield_value = js_mkundef(), 156 + .async_promise = promise, 157 + .next = NULL, 158 + .mco = NULL, 159 + .mco_started = false, 160 + .is_ready = false, 161 + .did_suspend = false, 162 + .sv_vm = async_vm, 163 + }; 164 + 165 + coro->active_parent = js->active_async_coro; 166 + js->active_async_coro = coro; 167 + 168 + ant_value_t result = sv_execute_entry(async_vm, func, this_val, NULL, 0); 169 + js->active_async_coro = coro->active_parent; 170 + coro->active_parent = NULL; 171 + 172 + if (async_vm->suspended) { 173 + enqueue_coroutine(coro); 174 + GC_ROOT_RESTORE(js, root_mark); 175 + return promise; 176 + } 177 + 178 + if (is_err(result)) { 179 + ant_value_t reject_value = js->thrown_value; 180 + if (vtype(reject_value) == T_UNDEF) reject_value = result; 181 + js->thrown_exists = false; 182 + js->thrown_value = js_mkundef(); 183 + js_reject_promise(js, promise, reject_value); 184 + } else js_resolve_promise(js, promise, result); 185 + 186 + free_coroutine(coro); 187 + GC_ROOT_RESTORE(js, root_mark); 188 + 189 + return promise; 190 + } 191 + 92 192 sv_tla_ctx_t *ctx = (sv_tla_ctx_t *)CORO_MALLOC(sizeof(sv_tla_ctx_t)); 93 193 if (!ctx) return js_mkerr(js, "out of memory for TLA context"); 94 194 ··· 136 236 .async_func = js_mkundef(), 137 237 .args = NULL, 138 238 .nargs = 0, 239 + .active_parent = NULL, 139 240 .is_settled = false, 140 241 .is_error = false, 141 242 .is_done = false, ··· 146 247 .mco = mco, 147 248 .mco_started = false, 148 249 .is_ready = true, 250 + .did_suspend = false, 149 251 .sv_vm = async_vm, 150 252 }; 151 253 ··· 179 281 "Maximum async operations per tick exceeded"); 180 282 } 181 283 284 + if (caller_vm && closure && closure->func && !closure->func->has_await) { 285 + GC_ROOT_SAVE(root_mark, js); 286 + GC_ROOT_PIN(js, callee_func); 287 + GC_ROOT_PIN(js, super_val); 288 + GC_ROOT_PIN(js, this_val); 289 + 290 + if (args) { 291 + for (int i = 0; i < argc; i++) GC_ROOT_PIN(js, args[i]); 292 + } 293 + 294 + ant_value_t promise = js_mkpromise(js); 295 + GC_ROOT_PIN(js, promise); 296 + 297 + ant_value_t result = sv_execute_closure_entry( 298 + caller_vm, closure, callee_func, super_val, this_val, args, argc, NULL 299 + ); 300 + 301 + if (is_err(result)) { 302 + ant_value_t reject_value = js->thrown_value; 303 + if (vtype(reject_value) == T_UNDEF) reject_value = result; 304 + js->thrown_exists = false; 305 + js->thrown_value = js_mkundef(); 306 + js_reject_promise(js, promise, reject_value); 307 + } else js_resolve_promise(js, promise, result); 308 + 309 + GC_ROOT_RESTORE(js, root_mark); 310 + return promise; 311 + } 312 + 313 + if (closure && closure->func && sv_async_func_supports_lazy_start(closure->func)) { 314 + GC_ROOT_SAVE(root_mark, js); 315 + GC_ROOT_PIN(js, callee_func); 316 + GC_ROOT_PIN(js, super_val); 317 + GC_ROOT_PIN(js, this_val); 318 + 319 + if (args) { 320 + for (int i = 0; i < argc; i++) GC_ROOT_PIN(js, args[i]); 321 + } 322 + 323 + ant_value_t promise = js_mkpromise(js); 324 + sv_vm_t *async_vm = sv_vm_create(js, SV_VM_ASYNC); 325 + if (!async_vm) { 326 + GC_ROOT_RESTORE(js, root_mark); 327 + return js_mkerr(js, "out of memory for async VM"); 328 + } 329 + 330 + coroutine_t *coro = (coroutine_t *)CORO_MALLOC(sizeof(coroutine_t)); 331 + if (!coro) { 332 + sv_vm_destroy(async_vm); 333 + GC_ROOT_RESTORE(js, root_mark); 334 + return js_mkerr(js, "out of memory for coroutine"); 335 + } 336 + 337 + GC_ROOT_PIN(js, promise); 338 + *coro = (coroutine_t){ 339 + .js = js, 340 + .type = CORO_ASYNC_AWAIT, 341 + .this_val = this_val, 342 + .super_val = super_val, 343 + .new_target = js->new_target, 344 + .awaited_promise = js_mkundef(), 345 + .result = js_mkundef(), 346 + .async_func = callee_func, 347 + .args = NULL, 348 + .nargs = argc, 349 + .active_parent = NULL, 350 + .is_settled = false, 351 + .is_error = false, 352 + .is_done = false, 353 + .resume_point = 0, 354 + .yield_value = js_mkundef(), 355 + .async_promise = promise, 356 + .next = NULL, 357 + .mco = NULL, 358 + .mco_started = false, 359 + .is_ready = false, 360 + .did_suspend = false, 361 + .sv_vm = async_vm, 362 + }; 363 + 364 + coro->active_parent = js->active_async_coro; 365 + js->active_async_coro = coro; 366 + 367 + ant_value_t result = sv_execute_closure_entry( 368 + async_vm, closure, callee_func, super_val, this_val, args, argc, NULL 369 + ); 370 + 371 + js->active_async_coro = coro->active_parent; 372 + coro->active_parent = NULL; 373 + 374 + if (async_vm->suspended) { 375 + enqueue_coroutine(coro); 376 + GC_ROOT_RESTORE(js, root_mark); 377 + return promise; 378 + } 379 + 380 + if (is_err(result)) { 381 + ant_value_t reject_value = js->thrown_value; 382 + if (vtype(reject_value) == T_UNDEF) reject_value = result; 383 + js->thrown_exists = false; 384 + js->thrown_value = js_mkundef(); 385 + js_reject_promise(js, promise, reject_value); 386 + } else js_resolve_promise(js, promise, result); 387 + 388 + free_coroutine(coro); 389 + GC_ROOT_RESTORE(js, root_mark); 390 + 391 + return promise; 392 + } 393 + 182 394 ant_value_t promise = js_mkpromise(js); 183 395 sv_async_ctx_t *ctx = (sv_async_ctx_t *)CORO_MALLOC(sizeof(sv_async_ctx_t)); 184 396 if (!ctx) return js_mkerr(js, "out of memory for async context"); ··· 242 454 .async_func = callee_func, 243 455 .args = ctx->args, 244 456 .nargs = argc, 457 + .active_parent = NULL, 245 458 .is_settled = false, 246 459 .is_error = false, 247 460 .is_done = false, ··· 252 465 .mco = mco, 253 466 .mco_started = false, 254 467 .is_ready = true, 468 + .did_suspend = false, 255 469 .sv_vm = async_vm, 256 470 }; 257 471 258 472 ctx->coro = coro; 259 473 enqueue_coroutine(coro); 260 474 MCO_RESUME_SAVE(js, mco, res); 475 + mco_state start_status = mco_status(mco); 261 476 262 - if (res != MCO_SUCCESS && mco_status(mco) != MCO_DEAD) { 477 + if (res != MCO_SUCCESS && start_status != MCO_DEAD) { 263 478 remove_coroutine(coro); 264 479 free_coroutine(coro); 265 480 return js_mkerr(js, "failed to start async coroutine"); 266 481 } 267 482 268 483 coro->mco_started = true; 269 - if (mco_status(mco) == MCO_DEAD) { 484 + if (start_status == MCO_DEAD) { 270 485 remove_coroutine(coro); 271 486 free_coroutine(coro); 272 487 } ··· 279 494 280 495 mco_coro *current_mco = mco_running(); 281 496 if (!current_mco) 282 - return js_mkerr(js, "await can only be used inside async functions"); 497 + current_mco = NULL; 283 498 284 499 coroutine_t *coro = NULL; 285 - sv_coro_header_t *hdr = (sv_coro_header_t *)mco_get_user_data(current_mco); 286 - if (hdr) coro = hdr->coro; 500 + if (current_mco) { 501 + sv_coro_header_t *hdr = (sv_coro_header_t *)mco_get_user_data(current_mco); 502 + if (hdr) coro = hdr->coro; 503 + } else if (js->active_async_coro) coro = js->active_async_coro; 287 504 288 505 if (!coro) 289 - return js_mkerr(js, "invalid async context"); 506 + return js_mkerr(js, "await can only be used inside async functions"); 290 507 291 508 coro->awaited_promise = value; 292 509 coro->is_settled = false; 293 510 coro->is_ready = false; 294 - 295 - ant_value_t resume_obj = mkobj(js, 0); 296 - js_set_slot(resume_obj, SLOT_CORO, tov((double)(uintptr_t)coro)); 297 - js_set_slot(resume_obj, SLOT_CFUNC, js_mkfun(resume_coroutine_wrapper)); 298 - 299 - ant_value_t reject_obj = mkobj(js, 0); 300 - js_set_slot(reject_obj, SLOT_CORO, tov((double)(uintptr_t)coro)); 301 - js_set_slot(reject_obj, SLOT_CFUNC, js_mkfun(reject_coroutine_wrapper)); 302 - 303 - ant_value_t then_fn = js_getprop_fallback(js, value, "then"); 304 - if (vtype(then_fn) == T_FUNC || vtype(then_fn) == T_CFUNC) { 305 - ant_value_t then_args[] = { 306 - js_obj_to_func(resume_obj), 307 - js_obj_to_func(reject_obj) 308 - }; 309 - sv_vm_call( 310 - js->vm, js, then_fn, value, 311 - then_args, 2, NULL, false 312 - ); 511 + js_await_result_t await_result = js_promise_await_coroutine(js, value, coro); 512 + 513 + if (await_result.state == JS_AWAIT_FULFILLED) { 514 + coro->is_settled = false; 515 + coro->awaited_promise = js_mkundef(); 516 + return await_result.value; 517 + } 518 + 519 + if (await_result.state == JS_AWAIT_REJECTED) { 520 + coro->is_settled = false; 521 + coro->awaited_promise = js_mkundef(); 522 + return js_throw(js, await_result.value); 313 523 } 314 524 525 + coro->did_suspend = true; 526 + if (!current_mco) { 527 + if (coro->sv_vm) coro->sv_vm->suspended = true; 528 + return js_mkundef(); 529 + } 530 + 315 531 mco_result mco_res = mco_yield(current_mco); 316 532 if (mco_res != MCO_SUCCESS) 317 533 return js_mkerr(js, "failed to yield coroutine");
+2 -3
src/silver/ops/exceptions.h
··· 150 150 } 151 151 152 152 ant_value_t caught = err; 153 - if (vtype(err) == T_ERR && js->thrown_exists && 154 - vtype(js->thrown_value) != T_UNDEF) { 153 + if (vtype(err) == T_ERR && js->thrown_exists) { 155 154 caught = js->thrown_value; 156 155 js->thrown_value = js_mkundef(); 157 156 js->thrown_exists = false; 158 157 } 159 - 158 + 160 159 vm->sp = h->saved_sp; 161 160 vm->fp = f; 162 161 vm->frames[f].completion.kind = SV_COMPLETION_THROW;
+1 -5
src/streams/pipes.c
··· 23 23 js_resolve_promise(js, promise, value); 24 24 } 25 25 26 - ant_value_t then_fn = js_get(js, promise, "then"); 27 - if (!is_callable(then_fn)) return; 28 - 29 - ant_value_t then_args[2] = { on_resolve, on_reject }; 30 - sv_vm_call(js->vm, js, then_fn, promise, then_args, 2, NULL, false); 26 + js_promise_then(js, promise, on_resolve, on_reject); 31 27 } 32 28 33 29 typedef struct {
+7 -36
src/streams/readable.c
··· 307 307 } else if (vtype(result) == T_PROMISE) { 308 308 ant_value_t res_fn = js_heavy_mkfun(js, rs_cancel_resolve, p); 309 309 ant_value_t rej_fn = js_heavy_mkfun(js, rs_cancel_reject, p); 310 - ant_value_t then_fn = js_get(js, result, "then"); 311 - if (is_callable(then_fn)) { 312 - ant_value_t then_args[2] = { res_fn, rej_fn }; 313 - sv_vm_call(js->vm, js, then_fn, result, then_args, 2, NULL, false); 314 - } 310 + js_promise_then(js, result, res_fn, rej_fn); 315 311 } else js_resolve_promise(js, p, js_mkundef()); 316 312 317 313 return p; ··· 360 356 if (vtype(result) == T_PROMISE) { 361 357 ant_value_t resolve_fn = js_heavy_mkfun(js, rs_pull_resolve_handler, controller_obj); 362 358 ant_value_t reject_fn = js_heavy_mkfun(js, rs_pull_reject_handler, controller_obj); 363 - ant_value_t then_fn = js_get(js, result, "then"); 364 - if (is_callable(then_fn)) { 365 - ant_value_t then_args[2] = { resolve_fn, reject_fn }; 366 - sv_vm_call(js->vm, js, then_fn, result, then_args, 2, NULL, false); 367 - } 359 + js_promise_then(js, result, resolve_fn, reject_fn); 368 360 } else if (is_err(result)) { 369 361 if (stream->state == RS_STATE_READABLE) { 370 362 ant_value_t thrown = js->thrown_value; ··· 375 367 js_resolve_promise(js, resolved, js_mkundef()); 376 368 ant_value_t resolve_fn = js_heavy_mkfun(js, rs_pull_resolve_handler, controller_obj); 377 369 ant_value_t reject_fn = js_heavy_mkfun(js, rs_pull_reject_handler, controller_obj); 378 - ant_value_t then_fn = js_get(js, resolved, "then"); 379 - if (is_callable(then_fn)) { 380 - ant_value_t then_args[2] = { resolve_fn, reject_fn }; 381 - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); 382 - } 370 + js_promise_then(js, resolved, resolve_fn, reject_fn); 383 371 } 384 372 } else ctrl->pulling = false; 385 373 } ··· 885 873 if (vtype(start_result) == T_PROMISE) { 886 874 ant_value_t resolve_fn = js_heavy_mkfun(js, rs_start_resolve_handler, ctrl_obj); 887 875 ant_value_t reject_fn = js_heavy_mkfun(js, rs_start_reject_handler, ctrl_obj); 888 - ant_value_t then_fn = js_get(js, start_result, "then"); 889 - if (is_callable(then_fn)) { 890 - ant_value_t then_args[2] = { resolve_fn, reject_fn }; 891 - sv_vm_call(js->vm, js, then_fn, start_result, then_args, 2, NULL, false); 892 - } 876 + js_promise_then(js, start_result, resolve_fn, reject_fn); 893 877 } 894 878 895 879 if (vtype(start_result) != T_PROMISE) { ··· 897 881 js_resolve_promise(js, resolved, js_mkundef()); 898 882 ant_value_t res_fn = js_heavy_mkfun(js, rs_start_resolve_handler, ctrl_obj); 899 883 ant_value_t rej_fn = js_heavy_mkfun(js, rs_start_reject_handler, ctrl_obj); 900 - ant_value_t then_fn = js_get(js, resolved, "then"); 901 - if (is_callable(then_fn)) { 902 - ant_value_t then_args[2] = { res_fn, rej_fn }; 903 - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); 904 - } 884 + js_promise_then(js, resolved, res_fn, rej_fn); 905 885 } 906 886 } else { 907 887 ant_value_t resolved = js_mkpromise(js); 908 888 js_resolve_promise(js, resolved, js_mkundef()); 909 889 ant_value_t res_fn = js_heavy_mkfun(js, rs_start_resolve_handler, ctrl_obj); 910 890 ant_value_t rej_fn = js_heavy_mkfun(js, rs_start_reject_handler, ctrl_obj); 911 - ant_value_t then_fn = js_get(js, resolved, "then"); 912 - if (is_callable(then_fn)) { 913 - ant_value_t then_args[2] = { res_fn, rej_fn }; 914 - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); 915 - } 891 + js_promise_then(js, resolved, res_fn, rej_fn); 916 892 } 917 893 918 894 return obj; ··· 936 912 js_resolve_promise(js, resolved, js_mkundef()); 937 913 ant_value_t res_fn = js_heavy_mkfun(js, rs_start_resolve_handler, ctrl_obj); 938 914 ant_value_t rej_fn = js_heavy_mkfun(js, rs_start_reject_handler, ctrl_obj); 939 - ant_value_t then_fn = js_get(js, resolved, "then"); 940 - 941 - if (is_callable(then_fn)) { 942 - ant_value_t then_args[2] = { res_fn, rej_fn }; 943 - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); 944 - } 915 + js_promise_then(js, resolved, res_fn, rej_fn); 945 916 946 917 return obj; 947 918 }
+4 -20
src/streams/transform.c
··· 217 217 js_resolve_promise(js, promise, val); 218 218 } 219 219 220 - ant_value_t then_fn = js_get(js, promise, "then"); 221 - if (!is_callable(then_fn)) return; 222 - 223 - ant_value_t then_args[2] = { res_fn, rej_fn }; 224 - ant_value_t then_result = sv_vm_call(js->vm, js, then_fn, promise, then_args, 2, NULL, false); 220 + ant_value_t then_result = js_promise_then(js, promise, res_fn, rej_fn); 225 221 promise_mark_handled(then_result); 226 222 } 227 223 ··· 1030 1026 if (vtype(start_result) == T_PROMISE) { 1031 1027 ant_value_t resolve_fn = js_heavy_mkfun(js, ts_start_resolve, ts_obj); 1032 1028 ant_value_t reject_fn = js_heavy_mkfun(js, ts_start_reject, ts_obj); 1033 - ant_value_t then_fn = js_get(js, start_result, "then"); 1034 - if (is_callable(then_fn)) { 1035 - ant_value_t then_args[2] = { resolve_fn, reject_fn }; 1036 - sv_vm_call(js->vm, js, then_fn, start_result, then_args, 2, NULL, false); 1037 - } 1029 + js_promise_then(js, start_result, resolve_fn, reject_fn); 1038 1030 } 1039 1031 1040 1032 if (vtype(start_result) != T_PROMISE) { ··· 1042 1034 js_resolve_promise(js, resolved, js_mkundef()); 1043 1035 ant_value_t res_fn = js_heavy_mkfun(js, ts_start_resolve, ts_obj); 1044 1036 ant_value_t rej_fn = js_heavy_mkfun(js, ts_start_reject, ts_obj); 1045 - ant_value_t then_fn = js_get(js, resolved, "then"); 1046 - if (is_callable(then_fn)) { 1047 - ant_value_t then_args[2] = { res_fn, rej_fn }; 1048 - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); 1049 - } 1037 + js_promise_then(js, resolved, res_fn, rej_fn); 1050 1038 } 1051 1039 } else { 1052 1040 ant_value_t resolved = js_mkpromise(js); 1053 1041 js_resolve_promise(js, resolved, js_mkundef()); 1054 1042 ant_value_t res_fn = js_heavy_mkfun(js, ts_start_resolve, ts_obj); 1055 1043 ant_value_t rej_fn = js_heavy_mkfun(js, ts_start_reject, ts_obj); 1056 - ant_value_t then_fn = js_get(js, resolved, "then"); 1057 - if (is_callable(then_fn)) { 1058 - ant_value_t then_args[2] = { res_fn, rej_fn }; 1059 - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); 1060 - } 1044 + js_promise_then(js, resolved, res_fn, rej_fn); 1061 1045 } 1062 1046 1063 1047 return ts_obj;
+4 -20
src/streams/writable.c
··· 203 203 js_resolve_promise(js, promise, val); 204 204 } 205 205 206 - ant_value_t then_fn = js_get(js, promise, "then"); 207 - if (!is_callable(then_fn)) return; 208 - 209 - ant_value_t then_args[2] = { res_fn, rej_fn }; 210 - ant_value_t then_result = sv_vm_call(js->vm, js, then_fn, promise, then_args, 2, NULL, false); 206 + ant_value_t then_result = js_promise_then(js, promise, res_fn, rej_fn); 211 207 promise_mark_handled(then_result); 212 208 } 213 209 ··· 1208 1204 if (vtype(start_result) == T_PROMISE) { 1209 1205 ant_value_t resolve_fn = js_heavy_mkfun(js, ws_start_resolve_handler, ctrl_obj); 1210 1206 ant_value_t reject_fn = js_heavy_mkfun(js, ws_start_reject_handler, ctrl_obj); 1211 - ant_value_t then_fn = js_get(js, start_result, "then"); 1212 - if (is_callable(then_fn)) { 1213 - ant_value_t then_args[2] = { resolve_fn, reject_fn }; 1214 - sv_vm_call(js->vm, js, then_fn, start_result, then_args, 2, NULL, false); 1215 - } 1207 + js_promise_then(js, start_result, resolve_fn, reject_fn); 1216 1208 } 1217 1209 1218 1210 if (vtype(start_result) != T_PROMISE) { ··· 1220 1212 js_resolve_promise(js, resolved, js_mkundef()); 1221 1213 ant_value_t res_fn = js_heavy_mkfun(js, ws_start_resolve_handler, ctrl_obj); 1222 1214 ant_value_t rej_fn = js_heavy_mkfun(js, ws_start_reject_handler, ctrl_obj); 1223 - ant_value_t then_fn = js_get(js, resolved, "then"); 1224 - if (is_callable(then_fn)) { 1225 - ant_value_t then_args[2] = { res_fn, rej_fn }; 1226 - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); 1227 - } 1215 + js_promise_then(js, resolved, res_fn, rej_fn); 1228 1216 } 1229 1217 } else { 1230 1218 ant_value_t resolved = js_mkpromise(js); 1231 1219 js_resolve_promise(js, resolved, js_mkundef()); 1232 1220 ant_value_t res_fn = js_heavy_mkfun(js, ws_start_resolve_handler, ctrl_obj); 1233 1221 ant_value_t rej_fn = js_heavy_mkfun(js, ws_start_reject_handler, ctrl_obj); 1234 - ant_value_t then_fn = js_get(js, resolved, "then"); 1235 - if (is_callable(then_fn)) { 1236 - ant_value_t then_args[2] = { res_fn, rej_fn }; 1237 - sv_vm_call(js->vm, js, then_fn, resolved, then_args, 2, NULL, false); 1238 - } 1222 + js_promise_then(js, resolved, res_fn, rej_fn); 1239 1223 } 1240 1224 1241 1225 return obj;
+48 -3
src/sugar.c
··· 1 1 #include "internal.h" 2 2 #include "sugar.h" 3 + #include "modules/timer.h" 4 + #include "silver/engine.h" 3 5 #include "silver/vm.h" 4 6 5 7 #define MCO_USE_VMEM_ALLOCATOR ··· 55 57 if (!coro) return; 56 58 57 59 if (coro->mco) { 58 - if (mco_running() == coro->mco) fprintf(stderr, "WARNING: Attempting to free a running coroutine\n"); 59 60 void *ctx = mco_get_user_data(coro->mco); 60 61 if (ctx) CORO_FREE(ctx); 61 62 mco_destroy(coro->mco); ··· 90 91 } 91 92 92 93 static void resume_coroutine_if_suspended(ant_t *js, coroutine_t *coro) { 93 - if (!coro || !coro->mco || mco_status(coro->mco) != MCO_SUSPENDED) return; 94 + if (!coro) return; 95 + 96 + if (!coro->mco) { 97 + if (!coro->sv_vm || !coro->sv_vm->suspended) return; 98 + 99 + coro->is_ready = false; 100 + coro->sv_vm->suspended_resume_value = coro->result; 101 + coro->sv_vm->suspended_resume_is_error = coro->is_error; 102 + coro->sv_vm->suspended_resume_pending = true; 103 + 104 + coro->active_parent = js->active_async_coro; 105 + js->active_async_coro = coro; 106 + ant_value_t result = sv_resume_suspended(coro->sv_vm); 107 + 108 + js->active_async_coro = coro->active_parent; 109 + coro->active_parent = NULL; 110 + 111 + coro->is_settled = false; 112 + coro->awaited_promise = js_mkundef(); 113 + if (coro->sv_vm->suspended) return; 114 + remove_coroutine(coro); 115 + 116 + if (is_err(result)) { 117 + ant_value_t reject_value = js->thrown_value; 118 + if (vtype(reject_value) == T_UNDEF) reject_value = result; 119 + js->thrown_exists = false; 120 + js->thrown_value = js_mkundef(); 121 + js_reject_promise(js, coro->async_promise, reject_value); 122 + } else js_resolve_promise(js, coro->async_promise, result); 123 + 124 + js_maybe_drain_microtasks_after_async_settle(js); 125 + free_coroutine(coro); 126 + 127 + return; 128 + } 129 + 130 + if (mco_status(coro->mco) != MCO_SUSPENDED) return; 94 131 95 132 coro->is_ready = false; 96 133 mco_result res; 97 134 MCO_RESUME_SAVE(js, coro->mco, res); 135 + mco_state status = mco_status(coro->mco); 98 136 99 - if (res != MCO_SUCCESS || mco_status(coro->mco) == MCO_DEAD) { 137 + if (res != MCO_SUCCESS || status == MCO_DEAD) { 100 138 remove_coroutine(coro); 101 139 free_coroutine(coro); 102 140 } ··· 130 168 131 169 return js_mkundef(); 132 170 } 171 + 172 + void settle_and_resume_coroutine(ant_t *js, coroutine_t *coro, ant_value_t value, bool is_error) { 173 + if (!coro) return; 174 + ant_value_t args[1] = { value }; 175 + settle_coroutine(coro, args, 1, is_error); 176 + resume_coroutine_if_suspended(js, coro); 177 + }
+198
tests/hono_dispatch_repro.js
··· 1 + const TOTAL = Number(process.env.TOTAL || 20000); 2 + const CONCURRENCY = Number(process.env.CONCURRENCY || 64); 3 + const MODE = process.env.MODE || "composeAwaitNext"; 4 + 5 + function nowMs() { 6 + return performance.now(); 7 + } 8 + 9 + function percentile(sorted, p) { 10 + if (sorted.length === 0) return 0; 11 + const index = Math.min(sorted.length - 1, Math.floor(sorted.length * p)); 12 + return sorted[index]; 13 + } 14 + 15 + function makeContext() { 16 + return { 17 + finalized: false, 18 + req: { 19 + routeIndex: -1 20 + }, 21 + res: null, 22 + text(body, status = 200) { 23 + this.finalized = true; 24 + this.res = { body, status }; 25 + return this.res; 26 + } 27 + }; 28 + } 29 + 30 + function composeLike(middleware, onError, onNotFound) { 31 + return (context, next) => { 32 + let index = -1; 33 + return dispatch(0); 34 + 35 + async function dispatch(i) { 36 + if (i <= index) { 37 + throw new Error("next() called multiple times"); 38 + } 39 + index = i; 40 + 41 + let res; 42 + let isError = false; 43 + let handler; 44 + 45 + if (middleware[i]) { 46 + handler = middleware[i]; 47 + context.req.routeIndex = i; 48 + } else { 49 + handler = i === middleware.length && next || void 0; 50 + } 51 + 52 + if (handler) { 53 + try { 54 + res = await handler(context, () => dispatch(i + 1)); 55 + } catch (err) { 56 + if (err instanceof Error && onError) { 57 + res = await onError(err, context); 58 + isError = true; 59 + } else { 60 + throw err; 61 + } 62 + } 63 + } else if (context.finalized === false && onNotFound) { 64 + res = await onNotFound(context); 65 + } 66 + 67 + if (res && (context.finalized === false || isError)) { 68 + context.res = res; 69 + } 70 + return context; 71 + } 72 + }; 73 + } 74 + 75 + function routeSync(c) { 76 + return c.text("ok"); 77 + } 78 + 79 + function routeResolved(c) { 80 + return Promise.resolve(c.text("ok")); 81 + } 82 + 83 + function routeAsync(c) { 84 + return (async () => c.text("ok"))(); 85 + } 86 + 87 + function middlewareReturnNext(_c, next) { 88 + return next(); 89 + } 90 + 91 + async function middlewareAwaitNext(_c, next) { 92 + return await next(); 93 + } 94 + 95 + async function middlewareAwaitResolvedAround(_c, next) { 96 + await Promise.resolve(); 97 + const out = await next(); 98 + await Promise.resolve(); 99 + return out; 100 + } 101 + 102 + function makeRunner(mode) { 103 + switch (mode) { 104 + case "directSync": 105 + return async () => routeSync(makeContext()); 106 + case "directResolved": 107 + return async () => routeResolved(makeContext()); 108 + case "directAsync": 109 + return async () => routeAsync(makeContext()); 110 + case "composeRouteSync": { 111 + const app = composeLike([routeSync]); 112 + return async () => app(makeContext()); 113 + } 114 + case "composeRouteResolved": { 115 + const app = composeLike([routeResolved]); 116 + return async () => app(makeContext()); 117 + } 118 + case "composeRouteAsync": { 119 + const app = composeLike([routeAsync]); 120 + return async () => app(makeContext()); 121 + } 122 + case "composeReturnNext": { 123 + const app = composeLike([middlewareReturnNext, routeSync]); 124 + return async () => app(makeContext()); 125 + } 126 + case "composeAwaitNext": { 127 + const app = composeLike([middlewareAwaitNext, routeSync]); 128 + return async () => app(makeContext()); 129 + } 130 + case "composeAwaitResolvedAround": { 131 + const app = composeLike([middlewareAwaitResolvedAround, routeSync]); 132 + return async () => app(makeContext()); 133 + } 134 + case "composeReturnNextResolvedRoute": { 135 + const app = composeLike([middlewareReturnNext, routeResolved]); 136 + return async () => app(makeContext()); 137 + } 138 + case "composeAwaitNextResolvedRoute": { 139 + const app = composeLike([middlewareAwaitNext, routeResolved]); 140 + return async () => app(makeContext()); 141 + } 142 + case "composeReturnNextAsyncRoute": { 143 + const app = composeLike([middlewareReturnNext, routeAsync]); 144 + return async () => app(makeContext()); 145 + } 146 + case "composeAwaitNextAsyncRoute": { 147 + const app = composeLike([middlewareAwaitNext, routeAsync]); 148 + return async () => app(makeContext()); 149 + } 150 + default: 151 + throw new Error(`Unknown MODE: ${mode}`); 152 + } 153 + } 154 + 155 + async function main() { 156 + const runOne = makeRunner(MODE); 157 + const latencies = new Array(TOTAL); 158 + let nextIndex = 0; 159 + let completed = 0; 160 + const startedAt = nowMs(); 161 + 162 + async function worker() { 163 + for (;;) { 164 + const index = nextIndex++; 165 + if (index >= TOTAL) return; 166 + 167 + const opStart = nowMs(); 168 + await runOne(); 169 + latencies[index] = nowMs() - opStart; 170 + completed++; 171 + } 172 + } 173 + 174 + const workers = []; 175 + for (let i = 0; i < CONCURRENCY; i++) { 176 + workers.push(worker()); 177 + } 178 + await Promise.all(workers); 179 + 180 + const elapsedMs = nowMs() - startedAt; 181 + latencies.sort((a, b) => a - b); 182 + 183 + console.log(`mode=${MODE}`); 184 + console.log(`total=${TOTAL}`); 185 + console.log(`concurrency=${CONCURRENCY}`); 186 + console.log(`elapsed_ms=${elapsedMs.toFixed(3)}`); 187 + console.log(`avg_ms=${(elapsedMs / TOTAL).toFixed(3)}`); 188 + console.log(`rps=${(TOTAL / (elapsedMs / 1000)).toFixed(1)}`); 189 + console.log(`p50_ms=${percentile(latencies, 0.50).toFixed(3)}`); 190 + console.log(`p95_ms=${percentile(latencies, 0.95).toFixed(3)}`); 191 + console.log(`p99_ms=${percentile(latencies, 0.99).toFixed(3)}`); 192 + console.log(`completed=${completed}`); 193 + } 194 + 195 + main().catch((err) => { 196 + console.error(err && err.stack ? err.stack : String(err)); 197 + process.exitCode = 1; 198 + });
+57
tests/non_async_loop_repro.js
··· 1 + const TOTAL = Number(process.env.TOTAL || 5000000); 2 + const MODE = process.env.MODE || "routeSync"; 3 + 4 + function nowMs() { 5 + return performance.now(); 6 + } 7 + 8 + function makeContext() { 9 + return { 10 + finalized: false, 11 + req: { 12 + routeIndex: -1 13 + }, 14 + res: null, 15 + text(body, status = 200) { 16 + this.finalized = true; 17 + this.res = { body, status }; 18 + return this.res; 19 + } 20 + }; 21 + } 22 + 23 + function routeSync(c) { 24 + return c.text("ok"); 25 + } 26 + 27 + function runTightLoop() { 28 + let acc = 0; 29 + for (let i = 0; i < TOTAL; i++) { 30 + acc += i; 31 + } 32 + return acc; 33 + } 34 + 35 + function runRouteSyncLoop() { 36 + let finalized = 0; 37 + for (let i = 0; i < TOTAL; i++) { 38 + const ctx = makeContext(); 39 + routeSync(ctx); 40 + if (ctx.finalized) finalized++; 41 + } 42 + return finalized; 43 + } 44 + 45 + function main() { 46 + const startedAt = nowMs(); 47 + const result = MODE === "tightLoop" ? runTightLoop() : runRouteSyncLoop(); 48 + const elapsedMs = nowMs() - startedAt; 49 + 50 + console.log(`mode=${MODE}`); 51 + console.log(`total=${TOTAL}`); 52 + console.log(`elapsed_ms=${elapsedMs.toFixed(3)}`); 53 + console.log(`ops_per_sec=${(TOTAL / (elapsedMs / 1000)).toFixed(1)}`); 54 + console.log(`result=${result}`); 55 + } 56 + 57 + main();