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.

fetch api

+282 -5
+6 -1
include/ant.h
··· 36 36 jsval_t js_mknum(double); 37 37 38 38 jsval_t js_getthis(struct js *); 39 + jsval_t js_getcurrentfunc(struct js *); 39 40 jsval_t js_get(struct js *, jsval_t, const char *); 40 41 41 42 jsval_t js_mkobj(struct js *); ··· 61 62 62 63 js_prop_iter_t js_prop_iter_begin(struct js *js, jsval_t obj); 63 64 bool js_prop_iter_next(js_prop_iter_t *iter, const char **key, size_t *key_len, jsval_t *value); 64 - void js_prop_iter_end(js_prop_iter_t *iter); 65 + void js_prop_iter_end(js_prop_iter_t *iter); 66 + 67 + jsval_t js_mkpromise(struct js *js); 68 + void js_resolve_promise(struct js *js, jsval_t promise, jsval_t value); 69 + void js_reject_promise(struct js *js, jsval_t promise, jsval_t value);
+6
include/modules/fetch.h
··· 1 + #ifndef FETCH_H 2 + #define FETCH_H 3 + 4 + void init_fetch_module(void); 5 + 6 + #endif
+1 -1
include/modules/io.h
··· 1 1 #ifndef IO_H 2 2 #define IO_H 3 3 4 - void init_console_module(); 4 + void init_console_module(void); 5 5 6 6 #endif
+1 -1
include/modules/json.h
··· 3 3 4 4 #include "ant.h" 5 5 6 - void init_json_module(); 6 + void init_json_module(void); 7 7 8 8 jsval_t js_json_parse(struct js *js, jsval_t *args, int nargs); 9 9 jsval_t js_json_stringify(struct js *js, jsval_t *args, int nargs);
+1 -1
meson.build
··· 41 41 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 42 42 43 43 version_conf = configuration_data() 44 - version_conf.set('ANT_VERSION', '0.0.5.38') 44 + version_conf.set('ANT_VERSION', '0.0.5.39') 45 45 version_conf.set('ANT_GIT_HASH', git_hash) 46 46 version_conf.set('ANT_BUILD_DATE', build_date) 47 47
+23 -1
src/ant.c
··· 2486 2486 jsval_t p_obj = mkval(T_OBJ, vdata(resolved)); 2487 2487 jsoff_t state_off = lkp(js, p_obj, "__state", 7); 2488 2488 if (state_off == 0) return js_mkerr(js, "invalid promise state"); 2489 + 2490 + int max_iterations = 10000; 2491 + int iterations = 0; 2492 + while (iterations < max_iterations) { 2493 + int state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); 2494 + if (state != 0) break; 2495 + if (!has_pending_microtasks()) break; 2496 + 2497 + process_microtasks(js); 2498 + iterations++; 2499 + 2500 + if (js->flags & F_THROW) return mkval(T_ERR, 0); 2501 + state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); 2502 + if (state != 0) break; 2503 + } 2504 + 2489 2505 int state = (int)tod(resolveprop(js, mkval(T_PROP, state_off))); 2490 2506 jsoff_t val_off = lkp(js, p_obj, "__value", 7); 2491 2507 if (val_off == 0) return js_mkerr(js, "invalid promise value"); ··· 2495 2511 } else if (state == 2) { 2496 2512 return js_mkerr(js, "await: promise rejected"); 2497 2513 } else { 2498 - return js_mkerr(js, "await: promise not resolved"); 2514 + return js_mkerr(js, "await: promise not resolved after processing microtasks"); 2499 2515 } 2500 2516 } else if (next(js) == TOK_NOT || js->tok == TOK_TILDA || js->tok == TOK_TYPEOF || 2501 2517 js->tok == TOK_VOID || js->tok == TOK_MINUS || js->tok == TOK_PLUS) { ··· 4891 4907 jsval_t js_glob(struct js *js) { (void) js; return mkval(T_OBJ, 0); } 4892 4908 jsval_t js_mkfun(jsval_t (*fn)(struct js *, jsval_t *, int)) { return mkval(T_CFUNC, (size_t) (void *) fn); } 4893 4909 jsval_t js_getthis(struct js *js) { return js->this_val; } 4910 + jsval_t js_getcurrentfunc(struct js *js) { return js->current_func; } 4894 4911 4895 4912 void js_set(struct js *js, jsval_t obj, const char *key, jsval_t val) { 4896 4913 if (vtype(obj) == T_OBJ) { ··· 5128 5145 iter->js_internal = NULL; 5129 5146 } 5130 5147 } 5148 + 5149 + jsval_t js_mkpromise(struct js *js) { return mkpromise(js); } 5150 + 5151 + void js_resolve_promise(struct js *js, jsval_t promise, jsval_t value) { resolve_promise(js, promise, value); } 5152 + void js_reject_promise(struct js *js, jsval_t promise, jsval_t value) { reject_promise(js, promise, value); } 5131 5153 5132 5154 #ifdef JS_DUMP 5133 5155 void js_dump(struct js *js) {
+2
src/main.c
··· 16 16 #include "modules/server.h" 17 17 #include "modules/timer.h" 18 18 #include "modules/json.h" 19 + #include "modules/fetch.h" 19 20 20 21 static struct { 21 22 char *path; ··· 242 243 243 244 init_console_module(); 244 245 init_json_module(); 246 + init_fetch_module(); 245 247 246 248 init_crypto_module(js, rt->ant_obj); 247 249 init_timer_module(js, rt->ant_obj);
+221
src/modules/fetch.c
··· 1 + #include <stdio.h> 2 + #include <stdlib.h> 3 + #include <string.h> 4 + #include <curl/curl.h> 5 + 6 + #include "runtime.h" 7 + #include "modules/fetch.h" 8 + #include "modules/timer.h" 9 + 10 + typedef struct { 11 + char *data; 12 + size_t size; 13 + } fetch_buffer_t; 14 + 15 + static CURL *global_curl = NULL; 16 + static void cleanup_fetch_module(void); 17 + 18 + static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { 19 + size_t realsize = size * nmemb; 20 + fetch_buffer_t *buf = (fetch_buffer_t *)userp; 21 + 22 + char *ptr = realloc(buf->data, buf->size + realsize + 1); 23 + if (!ptr) return 0; 24 + 25 + buf->data = ptr; 26 + memcpy(&(buf->data[buf->size]), contents, realsize); 27 + buf->size += realsize; 28 + buf->data[buf->size] = 0; 29 + 30 + return realsize; 31 + } 32 + 33 + static jsval_t create_response(struct js *js, int status, const char *body, size_t body_len) { 34 + jsval_t response_obj = js_mkobj(js); 35 + 36 + js_set(js, response_obj, "status", js_mknum(status)); 37 + js_set(js, response_obj, "ok", status >= 200 && status < 300 ? js_mktrue() : js_mkfalse()); 38 + js_set(js, response_obj, "__body", js_mkstr(js, body, body_len)); 39 + 40 + const char *json_code = "(){return JSON.parse(this.__body)}"; 41 + jsval_t json_str = js_mkstr(js, json_code, strlen(json_code)); 42 + jsval_t json_obj = js_mkobj(js); 43 + js_set(js, json_obj, "__code", json_str); 44 + jsval_t json_func = js_mknum(0); 45 + memcpy(&json_func, &json_obj, sizeof(jsval_t)); 46 + json_func = (json_func & 0xFFFFFFFFFFFFULL) | (0x7FF0000000000000ULL | ((uint64_t)7 << 48)); 47 + js_set(js, response_obj, "json", json_func); 48 + 49 + const char *text_code = "(){return this.__body}"; 50 + jsval_t text_str = js_mkstr(js, text_code, strlen(text_code)); 51 + jsval_t text_obj = js_mkobj(js); 52 + js_set(js, text_obj, "__code", text_str); 53 + jsval_t text_func = js_mknum(0); 54 + memcpy(&text_func, &text_obj, sizeof(jsval_t)); 55 + text_func = (text_func & 0xFFFFFFFFFFFFULL) | (0x7FF0000000000000ULL | ((uint64_t)7 << 48)); 56 + js_set(js, response_obj, "text", text_func); 57 + 58 + return response_obj; 59 + } 60 + 61 + static jsval_t do_fetch_microtask(struct js *js, jsval_t *args, int nargs) { 62 + (void)args; 63 + (void)nargs; 64 + 65 + jsval_t current_func = js_getcurrentfunc(js); 66 + jsval_t url_val = js_get(js, current_func, "url"); 67 + jsval_t options_val = js_get(js, current_func, "options"); 68 + jsval_t promise = js_get(js, current_func, "promise"); 69 + 70 + char *url_str = js_getstr(js, url_val, NULL); 71 + if (!url_str) { 72 + jsval_t err = js_mkstr(js, "Invalid URL", 11); 73 + js_reject_promise(js, promise, err); 74 + return js_mkundef(); 75 + } 76 + 77 + const char *method = "GET"; 78 + char *body = NULL; 79 + 80 + if (js_type(options_val) != JS_UNDEF && js_type(options_val) != JS_NULL) { 81 + jsval_t method_val = js_get(js, options_val, "method"); 82 + if (js_type(method_val) == JS_STR) { 83 + method = js_getstr(js, method_val, NULL); 84 + } 85 + 86 + jsval_t body_val = js_get(js, options_val, "body"); 87 + if (js_type(body_val) == JS_STR) { 88 + body = js_getstr(js, body_val, NULL); 89 + } 90 + } 91 + 92 + CURL *curl = global_curl; 93 + if (!curl) { 94 + jsval_t err = js_mkstr(js, "CURL not initialized", 20); 95 + js_reject_promise(js, promise, err); 96 + return js_mkundef(); 97 + } 98 + 99 + fetch_buffer_t response_buf = { .data = malloc(1), .size = 0 }; 100 + if (!response_buf.data) { 101 + jsval_t err = js_mkstr(js, "Memory allocation failed", 24); 102 + js_reject_promise(js, promise, err); 103 + return js_mkundef(); 104 + } 105 + 106 + curl_easy_reset(curl); 107 + 108 + curl_easy_setopt(curl, CURLOPT_URL, url_str); 109 + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); 110 + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buf); 111 + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 112 + 113 + if (strcmp(method, "GET") != 0) { 114 + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method); 115 + } 116 + 117 + struct curl_slist *headers = NULL; 118 + if (js_type(options_val) != JS_UNDEF && js_type(options_val) != JS_NULL) { 119 + jsval_t headers_val = js_get(js, options_val, "headers"); 120 + if (js_type(headers_val) != JS_UNDEF && js_type(headers_val) != JS_NULL) { 121 + js_prop_iter_t iter = js_prop_iter_begin(js, headers_val); 122 + const char *key; 123 + size_t key_len; 124 + jsval_t value; 125 + 126 + while (js_prop_iter_next(&iter, &key, &key_len, &value)) { 127 + char *value_str = js_getstr(js, value, NULL); 128 + if (value_str) { 129 + char header_line[1024]; 130 + snprintf(header_line, sizeof(header_line), "%.*s: %s", (int)key_len, key, value_str); 131 + headers = curl_slist_append(headers, header_line); 132 + } 133 + } 134 + js_prop_iter_end(&iter); 135 + 136 + if (headers) { 137 + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 138 + } 139 + } 140 + } 141 + 142 + if (body) { 143 + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body); 144 + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(body)); 145 + } 146 + 147 + CURLcode res = curl_easy_perform(curl); 148 + if (headers) curl_slist_free_all(headers); 149 + 150 + if (res != CURLE_OK) { 151 + free(response_buf.data); 152 + const char *err_msg = curl_easy_strerror(res); 153 + jsval_t err = js_mkstr(js, err_msg, strlen(err_msg)); 154 + js_reject_promise(js, promise, err); 155 + return js_mkundef(); 156 + } 157 + 158 + long status_code = 0; 159 + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); 160 + 161 + jsval_t response = create_response(js, (int)status_code, response_buf.data, response_buf.size); 162 + 163 + free(response_buf.data); 164 + js_resolve_promise(js, promise, response); 165 + 166 + return js_mkundef(); 167 + } 168 + 169 + static jsval_t js_fetch(struct js *js, jsval_t *args, int nargs) { 170 + if (nargs < 1) { 171 + return js_mkerr(js, "fetch requires at least 1 argument"); 172 + } 173 + 174 + if (js_type(args[0]) != JS_STR) { 175 + return js_mkerr(js, "fetch URL must be a string"); 176 + } 177 + 178 + jsval_t url_val = args[0]; 179 + jsval_t options_val = nargs > 1 ? args[1] : js_mkundef(); 180 + 181 + jsval_t promise = js_mkpromise(js); 182 + 183 + jsval_t wrapper_obj = js_mkobj(js); 184 + js_set(js, wrapper_obj, "__native_func", js_mkfun(do_fetch_microtask)); 185 + js_set(js, wrapper_obj, "url", url_val); 186 + js_set(js, wrapper_obj, "options", options_val); 187 + js_set(js, wrapper_obj, "promise", promise); 188 + 189 + jsval_t wrapper_func = js_mknum(0); 190 + memcpy(&wrapper_func, &wrapper_obj, sizeof(jsval_t)); 191 + wrapper_func = (wrapper_func & 0xFFFFFFFFFFFFULL) | (0x7FF0000000000000ULL | ((uint64_t)7 << 48)); 192 + queue_microtask(js, wrapper_func); 193 + 194 + return promise; 195 + } 196 + 197 + void init_fetch_module(void) { 198 + static int curl_initialized = 0; 199 + if (!curl_initialized) { 200 + curl_global_init(CURL_GLOBAL_DEFAULT); 201 + curl_initialized = 1; 202 + 203 + global_curl = curl_easy_init(); 204 + if (!global_curl) { 205 + fprintf(stderr, "Warning: Failed to initialize CURL handle\n"); 206 + } 207 + 208 + atexit(cleanup_fetch_module); 209 + } 210 + 211 + struct js *js = rt->js; 212 + js_set(js, js_glob(js), "fetch", js_mkfun(js_fetch)); 213 + } 214 + 215 + static void cleanup_fetch_module(void) { 216 + if (global_curl) { 217 + curl_easy_cleanup(global_curl); 218 + global_curl = NULL; 219 + } 220 + curl_global_cleanup(); 221 + }
+21
tests/fetch.cjs
··· 1 + async function test_get() { 2 + const { origin } = (await fetch('https://httpbin.org/get')).json(); 3 + console.log(origin); 4 + } 5 + 6 + async function test_post() { 7 + const response = await fetch('https://httpbin.org/post', { 8 + method: 'POST', 9 + body: JSON.stringify({ runtime: 'ant' }), 10 + headers: { 11 + 'Content-Type': 'application/json', 12 + 'User-Agent': 'ant/alpha (ant)' 13 + } 14 + }); 15 + 16 + const { json, headers } = response.json(); 17 + console.log(`${json}\n${headers}`); 18 + } 19 + 20 + void test_get(); 21 + void test_post();