diff --git a/README.md b/README.md index 54575df..f0aee96 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ Full async/await, modules, HTTP servers, crypto, and more. ## Table of contents -* [Installation](#installation) -* [Building Ant](#building-ant) -* [Security](#security) -* [Contributing to Ant](#contributing-to-ant) -* [Current project team members](#current-project-team-members) +- [Installation](#installation) +- [Building Ant](#building-ant) +- [Security](#security) +- [Contributing to Ant](#contributing-to-ant) +- [Project team members](#current-project-team-members) ## Installation @@ -37,6 +37,6 @@ For information on reporting security vulnerabilities in Ant, see [SECURITY.md]( We welcome contributions through pull request. See [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
For more information about Ant's internal structure, read the [Ant DeepWiki](https://deepwiki.com/theMackabu/ant). -## Current project team members +## Project team members For information about the governance of Ant, see [GOVERNANCE.md](GOVERNANCE.md). diff --git a/src/ant.c b/src/ant.c index c58fcfd..dba04e6 100644 --- a/src/ant.c +++ b/src/ant.c @@ -9855,10 +9855,12 @@ static jsval_t js_unary(struct js *js) { js_parse_state_t saved; JS_SAVE_STATE(js, saved); uint8_t saved_flags = js->flags; + jsval_t saved_current_func = js->current_func; mco_result mco_res = mco_yield(current_mco); JS_RESTORE_STATE(js, saved); js->flags = saved_flags; + js->current_func = saved_current_func; if (mco_res != MCO_SUCCESS) return js_mkerr(js, "failed to yield coroutine"); diff --git a/src/modules/server.c b/src/modules/server.c index 521cf9d..47fb22f 100644 --- a/src/modules/server.c +++ b/src/modules/server.c @@ -66,6 +66,8 @@ typedef struct response_ctx_s { int sent; int supports_gzip; int should_free_body; + int should_free_content_type; + int should_free_redirect; UT_array *custom_headers; char *redirect_location; uv_tcp_t *client_handle; @@ -400,8 +402,8 @@ static jsval_t res_header(struct js *js, jsval_t *args, int nargs) { if (vtype(args[0]) == T_STR && vtype(args[1]) == T_STR) { custom_header_t header; - header.name = js_getstr(js, args[0], NULL); - header.value = js_getstr(js, args[1], NULL); + header.name = strdup(js_getstr(js, args[0], NULL)); + header.value = strdup(js_getstr(js, args[1], NULL)); utarray_push_back(ctx->custom_headers, &header); } @@ -436,7 +438,16 @@ static jsval_t res_body(struct js *js, jsval_t *args, int nargs) { if (!ctx) return js_mkundef(); if (vtype(args[0]) == T_STR) { - ctx->body = js_getstr(js, args[0], &ctx->body_len); + size_t len; + const char *src = js_getstr(js, args[0], &len); + char *copy = malloc(len + 1); + if (copy) { + memcpy(copy, src, len); + copy[len] = '\0'; + } + ctx->body = copy; + ctx->body_len = len; + ctx->should_free_body = 1; } if (nargs >= 2 && vtype(args[1]) == T_NUM) { @@ -444,13 +455,11 @@ static jsval_t res_body(struct js *js, jsval_t *args, int nargs) { } if (nargs >= 3 && vtype(args[2]) == T_STR) { - ctx->content_type = js_getstr(js, args[2], NULL); - } else { - ctx->content_type = "text/plain"; - } + ctx->content_type = strdup(js_getstr(js, args[2], NULL)); + ctx->should_free_content_type = 1; + } else ctx->content_type = "text/plain"; ctx->sent = 1; - return js_mkundef(); } @@ -465,7 +474,16 @@ static jsval_t res_html(struct js *js, jsval_t *args, int nargs) { if (!ctx) return js_mkundef(); if (vtype(args[0]) == T_STR) { - ctx->body = js_getstr(js, args[0], &ctx->body_len); + size_t len; + const char *src = js_getstr(js, args[0], &len); + char *copy = malloc(len + 1); + if (copy) { + memcpy(copy, src, len); + copy[len] = '\0'; + } + ctx->body = copy; + ctx->body_len = len; + ctx->should_free_body = 1; } if (nargs >= 2 && vtype(args[1]) == T_NUM) { @@ -492,12 +510,28 @@ static jsval_t res_json(struct js *js, jsval_t *args, int nargs) { jsval_t result = js_json_stringify(js, stringify_args, 1); if (vtype(result) == T_STR) { - ctx->body = js_getstr(js, result, &ctx->body_len); + size_t len; + const char *src = js_getstr(js, result, &len); + char *copy = malloc(len + 1); + if (copy) { + memcpy(copy, src, len); + copy[len] = '\0'; + } + ctx->body = copy; + ctx->body_len = len; + ctx->should_free_body = 1; } else if (vtype(result) == T_ERR) { const char *json_str = js_str(js, args[0]); if (json_str) { - ctx->body = (char *)json_str; - ctx->body_len = strlen(json_str); + size_t len = strlen(json_str); + char *copy = malloc(len + 1); + if (copy) { + memcpy(copy, json_str, len); + copy[len] = '\0'; + } + ctx->body = copy; + ctx->body_len = len; + ctx->should_free_body = 1; } } @@ -541,7 +575,8 @@ static jsval_t res_redirect(struct js *js, jsval_t *args, int nargs) { if (!ctx) return js_mkundef(); if (vtype(args[0]) == T_STR) { - ctx->redirect_location = js_getstr(js, args[0], NULL); + ctx->redirect_location = strdup(js_getstr(js, args[0], NULL)); + ctx->should_free_redirect = 1; } ctx->status = 302; @@ -714,6 +749,8 @@ static void handle_http_request(client_t *client, http_request_t *http_req) { res_ctx->sent = 0; res_ctx->supports_gzip = http_req->accepts_gzip; res_ctx->should_free_body = 0; + res_ctx->should_free_content_type = 0; + res_ctx->should_free_redirect = 0; utarray_new(res_ctx->custom_headers, &custom_header_icd); res_ctx->redirect_location = NULL; res_ctx->client_handle = &client->handle; @@ -762,9 +799,15 @@ static void handle_http_request(client_t *client, http_request_t *http_req) { res_ctx->body_len = strlen(clean_error); res_ctx->should_free_body = 1; } else { - res_ctx->body = (char *)error_msg; - res_ctx->body_len = strlen(error_msg); - res_ctx->should_free_body = 0; + size_t err_len = strlen(error_msg); + char *err_copy = malloc(err_len + 1); + if (err_copy) { + memcpy(err_copy, error_msg, err_len); + err_copy[err_len] = '\0'; + } + res_ctx->body = err_copy; + res_ctx->body_len = err_len; + res_ctx->should_free_body = 1; } res_ctx->status = 500; res_ctx->content_type = "text/plain"; @@ -809,8 +852,18 @@ static void check_pending_responses(http_server_t *server) { uv_close((uv_handle_t *)ctx->client_handle, on_close); } - if (ctx->custom_headers) utarray_free(ctx->custom_headers); + if (ctx->custom_headers) { + custom_header_t *h = NULL; + while ((h = (custom_header_t*)utarray_next(ctx->custom_headers, h))) { + if (h->name) free(h->name); + if (h->value) free(h->value); + } + utarray_free(ctx->custom_headers); + } + if (ctx->should_free_body && ctx->body) free(ctx->body); + if (ctx->should_free_content_type && ctx->content_type) free(ctx->content_type); + if (ctx->should_free_redirect && ctx->redirect_location) free(ctx->redirect_location); free(ctx); } else { current = &ctx->next; } @@ -874,6 +927,8 @@ static void on_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { res_ctx->sent = 1; res_ctx->supports_gzip = 0; res_ctx->should_free_body = 0; + res_ctx->should_free_content_type = 0; + res_ctx->should_free_redirect = 0; utarray_new(res_ctx->custom_headers, &custom_header_icd); res_ctx->redirect_location = NULL; res_ctx->client_handle = &client->handle; diff --git a/tests/test_inline_await.js b/tests/test_inline_await.js new file mode 100644 index 0000000..4f7a839 --- /dev/null +++ b/tests/test_inline_await.js @@ -0,0 +1,70 @@ +async function delayed(val) { + return val; +} + +function add(a, b, c) { + return a + b + c; +} + +async function run() { + // 1: basic inline await in call args + console.log('=== Test 1: inline await in call args ==='); + const r1 = add(await delayed(1), 2, 3); + console.log(r1 === 6 ? '✓' : '✗', 'add(await delayed(1), 2, 3) =', r1); + + // 2: multiple inline awaits + console.log('\n=== Test 2: multiple inline awaits ==='); + const r2 = add(await delayed(10), await delayed(20), await delayed(30)); + console.log(r2 === 60 ? '✓' : '✗', 'add(await, await, await) =', r2); + + // 3: inline await as method call arg + console.log('\n=== Test 3: method call with inline await ==='); + const obj = { greet(name) { return 'hello ' + name; } }; + const r3 = obj.greet(await delayed('world')); + console.log(r3 === 'hello world' ? '✓' : '✗', 'obj.greet(await) =', r3); + + // 4: chained property method with inline await + console.log('\n=== Test 4: chained method with inline await ==='); + const nested = { inner: { say(msg) { return 'said: ' + msg; } } }; + const r4 = nested.inner.say(await delayed('hi')); + console.log(r4 === 'said: hi' ? '✓' : '✗', 'nested.inner.say(await) =', r4); + + // 5: inline await with async fs (the real-world case) + console.log('\n=== Test 5: console.log with inline await ==='); + console.log('value:', await delayed(42)); + + // 6: array push with inline await + console.log('\n=== Test 6: array method with inline await ==='); + const arr = [1, 2]; + arr.push(await delayed(3)); + console.log(arr.length === 3 && arr[2] === 3 ? '✓' : '✗', 'arr.push(await) =', arr); + + // 7: inline await with real fs stream + console.log('\n=== Test 7: inline await stream (real fs) ==='); + const { stream } = await import('ant:fs'); + const file = import.meta.dirname + '/test_inline_await.js'; + const data = await stream(file); + console.log(data.length > 0 ? '✓' : '✗', 'assigned await stream:', data.length, 'bytes'); + + // 8: inline await stream as function arg + console.log('\n=== Test 8: fn(await stream(...)) ==='); + function getLen(s) { return s.length; } + const r8 = getLen(await stream(file)); + console.log(r8 > 0 ? '✓' : '✗', 'getLen(await stream()) =', r8); + + // 9: inline await stream as method arg + console.log('\n=== Test 9: obj.method(await stream(...)) ==='); + const helper = { size(s) { return s.length; } }; + const r9 = helper.size(await stream(file)); + console.log(r9 > 0 ? '✓' : '✗', 'helper.size(await stream()) =', r9); + + // 10: chained method with inline await stream + console.log('\n=== Test 10: a.b.method(await stream(...)) ==='); + const deep = { util: { size(s) { return s.length; } } }; + const r10 = deep.util.size(await stream(file)); + console.log(r10 > 0 ? '✓' : '✗', 'deep.util.size(await stream()) =', r10); + + console.log('\n✓ done'); +} + +run();