MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add WebAssembly/WASI support and implement Promise.allSettled, regex v-flag, watch API, and add node stubs and fix Function.call/apply native dispatch

+6111 -373
+1 -1
examples/npm/elysia/index.ts
··· 3 3 4 4 console.log('started on http://localhost:3000'); 5 5 6 - export default new Elysia().use(logger()).get('/', () => 'hello elysia!!\n๐Ÿœ\n'); 6 + export default new Elysia().use(logger()).get('/', () => `hello elysia!!\n\n๐Ÿœ ${Ant.version}\n`);
+2 -2
examples/npm/express/index.cjs
··· 4 4 let port = 3000; 5 5 6 6 app.get('/', (_req, res) => { 7 - res.send('Hello World!'); 7 + res.type('txt').send(`hello express!!\n\n๐Ÿœ ${Ant.version}\n`); 8 8 }); 9 9 10 10 app.listen(port, () => { 11 - console.log(`Example app listening on port ${port}`); 11 + console.log(`started on http://localhost:${port}`); 12 12 });
+1 -1
examples/npm/hono/src/index.ts
··· 4 4 const app = new Hono(); 5 5 6 6 app.use(logger()); 7 - app.get('/', c => c.text('hello hono!!\n๐Ÿœ\n')); 7 + app.get('/', c => c.text(`hello hono!!\n\n๐Ÿœ ${Ant.version}\n`)); 8 8 9 9 console.log('started on http://localhost:3000'); 10 10
+5 -5
examples/results.txt
··· 607 607 compat-table/es6/String.prototype.startsWith.throws-regex.js: OK 608 608 compat-table/es6/String.raw.js: OK 609 609 compat-table/es6/Symbol.JSON.stringify.object.js: TypeError: Object.defineProperty called on non-object 610 - compat-table/es6/Symbol.JSON.stringify.primitive.js: failed 610 + compat-table/es6/Symbol.JSON.stringify.primitive.js: OK 611 611 compat-table/es6/Symbol.Object.js: failed 612 612 compat-table/es6/Symbol.String.js: OK 613 613 compat-table/es6/Symbol.defineProperty.js: OK ··· 1042 1042 compat-table/es6/typed-arrays.ArrayBuffer.Symbol.species.js: OK 1043 1043 compat-table/es6/typed-arrays.DataView.Float32.js: failed 1044 1044 compat-table/es6/typed-arrays.DataView.Float64.js: OK 1045 - compat-table/es6/typed-arrays.DataView.Int8.js: TypeError: undefined is not a function 1045 + compat-table/es6/typed-arrays.DataView.Int8.js: OK 1046 1046 compat-table/es6/typed-arrays.DataView.Int16.js: OK 1047 1047 compat-table/es6/typed-arrays.DataView.Int32.js: OK 1048 1048 compat-table/es6/typed-arrays.DataView.Uint8.js: OK 1049 - compat-table/es6/typed-arrays.DataView.Uint16.js: TypeError: undefined is not a function 1050 - compat-table/es6/typed-arrays.DataView.Uint32.js: TypeError: undefined is not a function 1049 + compat-table/es6/typed-arrays.DataView.Uint16.js: OK 1050 + compat-table/es6/typed-arrays.DataView.Uint32.js: OK 1051 1051 compat-table/es6/typed-arrays.Float32Array.js: failed 1052 1052 compat-table/es6/typed-arrays.Float64Array.js: OK 1053 1053 compat-table/es6/typed-arrays.Int8Array.js: OK ··· 1248 1248 compat-table/es2020/BigUint64Array.js: failed 1249 1249 compat-table/es2020/DataView.prototype.getBigInt64.js: TypeError: undefined is not a function 1250 1250 compat-table/es2020/DataView.prototype.getBigUint64.js: TypeError: undefined is not a function 1251 - compat-table/es2020/Promise.allSettled.js: TypeError: undefined is not a function 1251 + compat-table/es2020/Promise.allSettled.js: OK 1252 1252 compat-table/es2020/String.prototype.matchAll.js: TypeError: undefined is not a function 1253 1253 compat-table/es2020/String.prototype.matchAll.throws-non-global.js: failed 1254 1254 compat-table/es2020/globalThis.descriptor.js: OK
examples/wasm/cowsay.wasm

This is a binary file and will not be displayed.

+15
examples/wasm/index.mjs
··· 1 + import { readFile } from 'ant:fs'; 2 + 3 + const [wasmFile, ...wasmArgs] = process.argv.slice(2); 4 + if (!wasmFile) { 5 + console.error('Usage: ant wasm.mjs <file.wasm> [args...]'); 6 + process.exit(1); 7 + } 8 + 9 + const bytes = await readFile(wasmFile); 10 + 11 + const { instance } = await WebAssembly.instantiate(bytes, { 12 + wasi: { args: [wasmFile, ...wasmArgs] } 13 + }); 14 + 15 + instance.exports._start();
+8 -1
include/common.h
··· 106 106 BRAND_WRITABLE_STREAM_WRITER, 107 107 BRAND_WRITABLE_STREAM_CONTROLLER, 108 108 BRAND_TRANSFORM_STREAM, 109 - BRAND_TRANSFORM_STREAM_CONTROLLER 109 + BRAND_TRANSFORM_STREAM_CONTROLLER, 110 + BRAND_WASM_MODULE, 111 + BRAND_WASM_INSTANCE, 112 + BRAND_WASM_GLOBAL, 113 + BRAND_WASM_MEMORY, 114 + BRAND_WASM_TABLE, 115 + BRAND_WASM_TAG, 116 + BRAND_WASM_EXCEPTION 110 117 } object_brand_id_t; 111 118 112 119 static inline void *mantissa_chk(void *p, const char *func) {
+1
include/gc/modules.h
··· 29 29 void gc_mark_codec_streams(ant_t *js, gc_mark_fn mark); 30 30 void gc_mark_compression_streams(ant_t *js, gc_mark_fn mark); 31 31 void gc_mark_zlib(ant_t *js, gc_mark_fn mark); 32 + void gc_mark_wasm(ant_t *js, gc_mark_fn mark); 32 33 void gc_mark_abort_signal_object(ant_t *js, ant_value_t signal, gc_mark_fn mark); 33 34 34 35 #endif
+8
include/internal.h
··· 402 402 return js_brand_id(obj) == brand; 403 403 } 404 404 405 + static inline void js_set_module_default(ant_t *js, ant_value_t lib, ant_value_t ctor_fn, const char *name) { 406 + js_set(js, ctor_fn, name, ctor_fn); 407 + js_set(js, lib, name, ctor_fn); 408 + js_set(js, lib, "default", ctor_fn); 409 + js_set(js, ctor_fn, "default", ctor_fn); 410 + js_set_slot_wb(js, lib, SLOT_DEFAULT, ctor_fn); 411 + } 412 + 405 413 static inline ant_value_t js_make_ctor(ant_t *js, ant_cfunc_t fn, ant_value_t proto, const char *name, size_t nlen) { 406 414 ant_value_t obj = js_mkobj(js); 407 415 js_set_slot(obj, SLOT_CFUNC, js_mkfun(fn));
+4 -3
include/modules/process.h
··· 3 3 4 4 #include "types.h" 5 5 6 + void init_process_module(void); 7 + ant_value_t process_library(ant_t *js); 8 + 6 9 void process_enable_keypress_events(void); 7 10 void emit_process_event(const char *event_type, ant_value_t *args, int nargs); 8 11 9 - void init_process_module(void); 10 12 bool has_active_stdin(void); 11 - 12 - ant_value_t process_library(ant_t *js); 13 + bool process_has_event_listeners(const char *event_type); 13 14 14 15 #endif
+17 -13
include/modules/regex.h
··· 3 3 4 4 #include "types.h" 5 5 6 + typedef struct { 7 + const char *pattern_ptr; 8 + ant_offset_t pattern_len; 9 + 10 + const char *str_ptr; 11 + ant_offset_t str_len; 12 + 13 + bool global; 14 + bool ignore_case; 15 + bool multiline; 16 + } regex_match_args_t; 17 + 6 18 void init_regex_module(void); 7 19 void cleanup_regex_module(void); 8 20 void gc_sweep_regex_cache(void); 9 21 10 - size_t js_to_pcre2_pattern(const char *src, size_t src_len, char *dst, size_t dst_size); 11 - 12 - ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs); 13 - ant_value_t builtin_regexp_symbol_match(ant_t *js, ant_value_t *args, int nargs); 14 - ant_value_t builtin_regexp_symbol_replace(ant_t *js, ant_value_t *args, int nargs); 15 - ant_value_t builtin_regexp_symbol_search(ant_t *js, ant_value_t *args, int nargs); 16 - ant_value_t builtin_regexp_flags_getter(ant_t *js, ant_value_t *args, int nargs); 22 + size_t js_to_pcre2_pattern( 23 + const char *src, size_t src_len, 24 + char *dst, size_t dst_size, bool v_flag 25 + ); 17 26 18 27 ant_value_t is_regexp_like(ant_t *js, ant_value_t value); 28 + ant_value_t do_regex_match_pcre2(ant_t *js, regex_match_args_t args); 19 29 ant_value_t reject_regexp_arg(ant_t *js, ant_value_t value, const char *method_name); 20 - 21 - ant_value_t do_regex_match_pcre2( 22 - ant_t *js, const char *pattern_ptr, ant_offset_t pattern_len, 23 - const char *str_ptr, ant_offset_t str_len, 24 - bool global_flag, bool ignore_case, bool multiline 25 - ); 26 30 27 31 #endif
+15
include/modules/wasi.h
··· 1 + #ifndef ANT_WASI_H 2 + #define ANT_WASI_H 3 + 4 + #include "types.h" 5 + #include <stdbool.h> 6 + #include <stdint.h> 7 + 8 + bool wasi_module_has_wasi_imports(void *c_api_module); 9 + 10 + ant_value_t wasi_instantiate( 11 + ant_t *js, const uint8_t *wasm_bytes, size_t wasm_len, 12 + ant_value_t module_obj, ant_value_t wasi_opts 13 + ); 14 + 15 + #endif
+6
include/modules/wasm.h
··· 1 + #ifndef ANT_WASM_MODULE_H 2 + #define ANT_WASM_MODULE_H 3 + 4 + void init_wasm_module(void); 5 + 6 + #endif
+14
include/watch.h
··· 2 2 #define ANT_WATCH_H 3 3 4 4 #include <stdbool.h> 5 + #include <uv.h> 6 + 7 + int ant_watch_start( 8 + uv_loop_t *loop, 9 + uv_fs_event_t *event, 10 + const char *path, 11 + uv_fs_event_cb callback, 12 + void *data, 13 + unsigned int flags, 14 + char **resolved_path_out 15 + ); 5 16 6 17 int ant_watch_run( 7 18 int argc, char **argv, 8 19 const char *entry_file, bool no_clear_screen 9 20 ); 21 + 22 + void ant_watch_stop(uv_fs_event_t *event); 23 + char *ant_watch_resolve_path(const char *path); 10 24 11 25 #endif
+2 -1
meson/deps/meson.build
··· 107 107 minicoro_dep = subproject('minicoro').get_variable('minicoro_dep') 108 108 crprintf_dep = subproject('crprintf').get_variable('crprintf_dep') 109 109 uriparser_dep = subproject('uriparser').get_variable('uriparser_dep') 110 + wamr_dep = subproject('wasm-micro-runtime').get_variable('wamr_dep') 110 111 111 112 utf8proc_base_dep = subproject('utf8proc').get_variable('utf8proc_dep') 112 113 utf8proc_dep = declare_dependency( ··· 214 215 yyjson_dep, minicoro_dep, uuidv7_dep, 215 216 uriparser_dep, utf8proc_dep, openssl_dep, 216 217 zlib_dep, brotli_common_dep, brotli_dec_dep, 217 - brotli_enc_dep, uthash_dep, lmdb_dep, 218 + brotli_enc_dep, uthash_dep, lmdb_dep, wamr_dep, 218 219 ] + mbedtls_dep + win_deps 219 220 220 221 if get_option('jit')
+216 -8
src/ant.c
··· 1397 1397 } 1398 1398 1399 1399 if (tag_str) { 1400 + bool is_timeout = (tlen == 7 && memcmp(tag_str, "Timeout", 7) == 0); 1401 + bool is_interval = (tlen == 8 && memcmp(tag_str, "Interval", 8) == 0); 1402 + if (is_timeout || is_interval) { 1403 + ant_value_t id_val = js_get_slot(obj, SLOT_DATA); 1404 + int timer_id = vtype(id_val) == T_NUM ? (int)js_getnum(id_val) : 0; 1405 + n += cpy(buf + n, REMAIN(n, len), tag_str, tlen); 1406 + n += (size_t) snprintf(buf + n, REMAIN(n, len), " (%d) {\n", timer_id); 1407 + goto continue_object_print; 1408 + } 1400 1409 bool is_blob = (tlen == 4 && memcmp(tag_str, "Blob", 4) == 0); 1401 1410 bool is_file = (tlen == 4 && memcmp(tag_str, "File", 4) == 0); 1402 1411 if (is_blob || is_file) { ··· 4744 4753 static ant_value_t builtin_function_empty(ant_t *js, ant_value_t *args, int nargs) { 4745 4754 (void)js; (void)args; (void)nargs; 4746 4755 return js_mkundef(); 4756 + } 4757 + 4758 + static bool function_uses_native_call(ant_value_t func) { 4759 + if (vtype(func) == T_CFUNC) return true; 4760 + if (vtype(func) != T_FUNC) return false; 4761 + 4762 + ant_value_t func_obj = js_func_obj(func); 4763 + return vtype(get_slot(func_obj, SLOT_CFUNC)) == T_CFUNC; 4764 + } 4765 + 4766 + static ant_value_t function_call_dispatch( 4767 + ant_t *js, 4768 + ant_value_t func, 4769 + ant_value_t this_arg, 4770 + ant_value_t *call_args, 4771 + int call_nargs 4772 + ) { 4773 + if (function_uses_native_call(func)) return sv_call_native(js, func, this_arg, call_args, call_nargs); 4774 + return sv_vm_call(js->vm, js, func, this_arg, call_args, call_nargs, NULL, false); 4747 4775 } 4748 4776 4749 4777 static ant_value_t builtin_function_call(ant_t *js, ant_value_t *args, int nargs) { ··· 4753 4781 } 4754 4782 4755 4783 ant_value_t this_arg = (nargs > 0) ? args[0] : js_mkundef(); 4756 - 4757 4784 ant_value_t *call_args = NULL; 4758 - int call_nargs = (nargs > 1) ? nargs - 1 : 0; 4759 - if (call_nargs > 0) { 4760 - call_args = &args[1]; 4761 - } 4762 4785 4763 - return sv_vm_call(js->vm, js, func, this_arg, call_args, call_nargs, NULL, false); 4786 + int call_nargs = (nargs > 1) ? nargs - 1 : 0; 4787 + if (call_nargs > 0) call_args = &args[1]; 4788 + 4789 + return function_call_dispatch(js, func, this_arg, call_args, call_nargs); 4764 4790 } 4765 4791 4766 4792 static int extract_array_args(ant_t *js, ant_value_t arr, ant_value_t **out_args) { ··· 4883 4909 } else if (t != T_UNDEF && t != T_NULL) {} 4884 4910 } 4885 4911 4886 - ant_value_t result = sv_vm_call(js->vm, js, func, this_arg, call_args, call_nargs, NULL, false); 4912 + ant_value_t result = function_call_dispatch(js, func, this_arg, call_args, call_nargs); 4887 4913 if (call_args) free(call_args); 4914 + 4888 4915 return result; 4889 4916 } 4890 4917 ··· 8344 8371 } 8345 8372 8346 8373 char pcre2_pattern[512]; 8347 - size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, plen, pcre2_pattern, sizeof(pcre2_pattern)); 8374 + size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, plen, pcre2_pattern, sizeof(pcre2_pattern), false); 8348 8375 8349 8376 uint32_t options = PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_UNSET_BACKREF; 8350 8377 int errcode; ··· 10667 10694 return js_mkundef(); 10668 10695 } 10669 10696 10697 + static ant_value_t promise_all_settled_make_result(ant_t *js, bool fulfilled, ant_value_t value) { 10698 + ant_value_t result = mkobj(js, 0); 10699 + if (is_err(result)) return result; 10700 + 10701 + js_setprop( 10702 + js, result, 10703 + js_mkstr(js, "status", 6), 10704 + js_mkstr(js, fulfilled ? "fulfilled" : "rejected", fulfilled ? 9 : 8) 10705 + ); 10706 + 10707 + js_setprop( 10708 + js, result, 10709 + js_mkstr(js, fulfilled ? "value" : "reason", fulfilled ? 5 : 6), value 10710 + ); 10711 + 10712 + return result; 10713 + } 10714 + 10715 + static ant_value_t promise_all_settled_store_result( 10716 + ant_t *js, 10717 + ant_value_t tracker, 10718 + int index, 10719 + bool fulfilled, 10720 + ant_value_t value 10721 + ) { 10722 + ant_value_t results = js_get(js, tracker, "results"); 10723 + ant_value_t result = promise_all_settled_make_result(js, fulfilled, value); 10724 + ant_value_t remaining_val = 0; 10725 + int remaining = 0; 10726 + 10727 + if (is_err(result)) return result; 10728 + arr_set(js, results, (ant_offset_t)index, result); 10729 + 10730 + remaining_val = js_get(js, tracker, "remaining"); 10731 + remaining = (int)tod(remaining_val) - 1; 10732 + js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)remaining)); 10733 + 10734 + if (remaining == 0) { 10735 + ant_value_t result_promise = get_slot(tracker, SLOT_DATA); 10736 + js_resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); 10737 + } 10738 + 10739 + return js_mkundef(); 10740 + } 10741 + 10742 + static ant_value_t builtin_Promise_allSettled_resolve_handler(ant_t *js, ant_value_t *args, int nargs) { 10743 + ant_value_t me = js->current_func; 10744 + ant_value_t tracker = js_get(js, me, "tracker"); 10745 + ant_value_t index_val = js_get(js, me, "index"); 10746 + int index = (int)tod(index_val); 10747 + ant_value_t value = nargs > 0 ? args[0] : js_mkundef(); 10748 + 10749 + return promise_all_settled_store_result(js, tracker, index, true, value); 10750 + } 10751 + 10752 + static ant_value_t builtin_Promise_allSettled_reject_handler(ant_t *js, ant_value_t *args, int nargs) { 10753 + ant_value_t me = js->current_func; 10754 + ant_value_t tracker = js_get(js, me, "tracker"); 10755 + ant_value_t index_val = js_get(js, me, "index"); 10756 + int index = (int)tod(index_val); 10757 + ant_value_t reason = nargs > 0 ? args[0] : js_mkundef(); 10758 + 10759 + return promise_all_settled_store_result(js, tracker, index, false, reason); 10760 + } 10761 + 10670 10762 typedef struct { 10671 10763 ant_value_t tracker; 10672 10764 int index; 10673 10765 } promise_all_iter_ctx_t; 10674 10766 10767 + // TODO: move Promise combinator bookkeeping off JS-visible properties and into slots/native state 10675 10768 static iter_action_t promise_all_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) { 10676 10769 GC_ROOT_SAVE(root_mark, js); 10677 10770 promise_all_iter_ctx_t *pctx = (promise_all_iter_ctx_t *)ctx; ··· 10726 10819 return ITER_CONTINUE; 10727 10820 } 10728 10821 10822 + static iter_action_t promise_all_settled_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) { 10823 + GC_ROOT_SAVE(root_mark, js); 10824 + promise_all_iter_ctx_t *pctx = (promise_all_iter_ctx_t *)ctx; 10825 + ant_value_t item = value; 10826 + GC_ROOT_PIN(js, item); 10827 + 10828 + if (vtype(item) != T_PROMISE) { 10829 + ant_value_t wrap_args[] = { item }; 10830 + item = builtin_Promise_resolve(js, wrap_args, 1); 10831 + GC_ROOT_PIN(js, item); 10832 + } 10833 + 10834 + ant_value_t resolve_obj = mkobj(js, 0); 10835 + if (is_err(resolve_obj)) { 10836 + *out = resolve_obj; 10837 + GC_ROOT_RESTORE(js, root_mark); 10838 + return ITER_ERROR; 10839 + } 10840 + GC_ROOT_PIN(js, resolve_obj); 10841 + set_slot(resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_allSettled_resolve_handler)); 10842 + js_setprop(js, resolve_obj, js_mkstr(js, "index", 5), tov((double)pctx->index)); 10843 + js_setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), pctx->tracker); 10844 + ant_value_t resolve_fn = js_obj_to_func(resolve_obj); 10845 + GC_ROOT_PIN(js, resolve_fn); 10846 + 10847 + ant_value_t reject_obj = mkobj(js, 0); 10848 + if (is_err(reject_obj)) { 10849 + *out = reject_obj; 10850 + GC_ROOT_RESTORE(js, root_mark); 10851 + return ITER_ERROR; 10852 + } 10853 + GC_ROOT_PIN(js, reject_obj); 10854 + set_slot(reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_allSettled_reject_handler)); 10855 + js_setprop(js, reject_obj, js_mkstr(js, "index", 5), tov((double)pctx->index)); 10856 + js_setprop(js, reject_obj, js_mkstr(js, "tracker", 7), pctx->tracker); 10857 + ant_value_t reject_fn = js_obj_to_func(reject_obj); 10858 + GC_ROOT_PIN(js, reject_fn); 10859 + 10860 + ant_value_t then_args[] = { resolve_fn, reject_fn }; 10861 + ant_value_t saved_this = js->this_val; 10862 + GC_ROOT_PIN(js, saved_this); 10863 + js->this_val = item; 10864 + ant_value_t then_result = builtin_promise_then(js, then_args, 2); 10865 + js->this_val = saved_this; 10866 + if (is_err(then_result)) { 10867 + *out = then_result; 10868 + GC_ROOT_RESTORE(js, root_mark); 10869 + return ITER_ERROR; 10870 + } 10871 + 10872 + pctx->index++; 10873 + GC_ROOT_RESTORE(js, root_mark); 10874 + return ITER_CONTINUE; 10875 + } 10876 + 10729 10877 static ant_value_t builtin_Promise_all(ant_t *js, ant_value_t *args, int nargs) { 10730 10878 if (nargs < 1) return js_mkerr(js, "Promise.all requires an iterable"); 10731 10879 ··· 10774 10922 if ((ant_offset_t)len > dense_capacity(doff)) doff = dense_grow(js, results, (ant_offset_t)len); 10775 10923 if (doff) array_len_set(js, results, (ant_offset_t)len); 10776 10924 } 10925 + } 10926 + 10927 + if (len == 0) { 10928 + js_resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); 10929 + GC_ROOT_RESTORE(js, root_mark); 10930 + return result_promise; 10931 + } 10932 + 10933 + js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len)); 10934 + GC_ROOT_RESTORE(js, root_mark); 10935 + return result_promise; 10936 + } 10937 + 10938 + static ant_value_t builtin_Promise_allSettled(ant_t *js, ant_value_t *args, int nargs) { 10939 + if (nargs < 1) return js_mkerr(js, "Promise.allSettled requires an iterable"); 10940 + 10941 + GC_ROOT_SAVE(root_mark, js); 10942 + ant_value_t iterable = args[0]; 10943 + GC_ROOT_PIN(js, iterable); 10944 + uint8_t t = vtype(iterable); 10945 + if (t != T_ARR && t != T_OBJ) { 10946 + ant_value_t err = js_mkerr(js, "Promise.allSettled requires an iterable"); 10947 + GC_ROOT_RESTORE(js, root_mark); 10948 + return err; 10949 + } 10950 + 10951 + ant_value_t ctor = js->this_val; 10952 + GC_ROOT_PIN(js, ctor); 10953 + if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) ctor = js_mkundef(); 10954 + 10955 + ant_value_t result_promise = mkpromise_with_ctor(js, ctor); 10956 + GC_ROOT_PIN(js, result_promise); 10957 + if (is_err(result_promise)) { 10958 + GC_ROOT_RESTORE(js, root_mark); 10959 + return result_promise; 10960 + } 10961 + 10962 + ant_value_t tracker = mkobj(js, 0); 10963 + GC_ROOT_PIN(js, tracker); 10964 + ant_value_t results = mkarr(js); 10965 + GC_ROOT_PIN(js, results); 10966 + 10967 + js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov(0.0)); 10968 + js_setprop(js, tracker, js_mkstr(js, "results", 7), results); 10969 + set_slot(tracker, SLOT_DATA, result_promise); 10970 + 10971 + promise_all_iter_ctx_t ctx = { .tracker = tracker, .index = 0 }; 10972 + ant_value_t iter_result = iter_foreach(js, iterable, promise_all_settled_iter_cb, &ctx); 10973 + 10974 + if (is_err(iter_result)) { 10975 + GC_ROOT_RESTORE(js, root_mark); 10976 + return iter_result; 10977 + } 10978 + 10979 + int len = ctx.index; 10980 + ant_offset_t doff = get_dense_buf(results); 10981 + if (doff) { 10982 + if ((ant_offset_t)len > dense_capacity(doff)) doff = dense_grow(js, results, (ant_offset_t)len); 10983 + if (doff) array_len_set(js, results, (ant_offset_t)len); 10777 10984 } 10778 10985 10779 10986 if (len == 0) { ··· 12482 12689 js_setprop(js, p_ctor_obj, js_mkstr(js, "try", 3), js_mkfun(builtin_Promise_try)); 12483 12690 js_setprop(js, p_ctor_obj, js_mkstr(js, "withResolvers", 13), js_mkfun(builtin_Promise_withResolvers)); 12484 12691 js_setprop(js, p_ctor_obj, js_mkstr(js, "all", 3), js_mkfun(builtin_Promise_all)); 12692 + js_setprop(js, p_ctor_obj, js_mkstr(js, "allSettled", 10), js_mkfun(builtin_Promise_allSettled)); 12485 12693 js_setprop(js, p_ctor_obj, js_mkstr(js, "race", 4), js_mkfun(builtin_Promise_race)); 12486 12694 js_setprop(js, p_ctor_obj, js_mkstr(js, "any", 3), js_mkfun(builtin_Promise_any)); 12487 12695 js_setprop_nonconfigurable(js, p_ctor_obj, "prototype", 9, promise_proto);
+59
src/builtins/node/http2.mjs
··· 1 + import * as http from 'node:http'; 2 + import * as https from 'node:https'; 3 + 4 + function clientNotImplemented() { 5 + throw new Error('node:http2 client transport is not implemented yet'); 6 + } 7 + 8 + // compatibility stub only 9 + export class Http2Server extends http.Server {} 10 + export class Http2SecureServer extends https.Server {} 11 + 12 + export class ClientHttp2Session {} 13 + export class ClientHttp2Stream {} 14 + export class ServerHttp2Session {} 15 + export class ServerHttp2Stream {} 16 + 17 + export function createServer(options, requestListener) { 18 + return http.createServer(options, requestListener); 19 + } 20 + 21 + export function createSecureServer(options, requestListener) { 22 + return https.createServer(options, requestListener); 23 + } 24 + 25 + export function connect() { 26 + clientNotImplemented(); 27 + } 28 + 29 + export function getDefaultSettings() { 30 + return {}; 31 + } 32 + 33 + export function getPackedSettings() { 34 + return new Uint8Array(0); 35 + } 36 + 37 + export function getUnpackedSettings() { 38 + return {}; 39 + } 40 + 41 + export const constants = Object.freeze({}); 42 + export const sensitiveHeaders = Object.freeze([]); 43 + 44 + export default { 45 + ClientHttp2Session, 46 + ClientHttp2Stream, 47 + Http2Server, 48 + Http2SecureServer, 49 + ServerHttp2Session, 50 + ServerHttp2Stream, 51 + connect, 52 + constants, 53 + createServer, 54 + createSecureServer, 55 + getDefaultSettings, 56 + getPackedSettings, 57 + getUnpackedSettings, 58 + sensitiveHeaders 59 + };
+563
src/builtins/node/stream.cjs
··· 1 + 'use strict'; 2 + 3 + var EventEmitter = require('node:events').EventEmitter; 4 + var BufferCtor = typeof Buffer !== 'undefined' ? Buffer : require('node:buffer').Buffer; 5 + 6 + function noop() {} 7 + 8 + function nextTick(fn) { 9 + if (typeof process !== 'undefined' && process && typeof process.nextTick === 'function') { 10 + var args = Array.prototype.slice.call(arguments, 1); 11 + process.nextTick(function () { 12 + fn.apply(undefined, args); 13 + }); 14 + return; 15 + } 16 + 17 + Promise.resolve().then(function () { 18 + fn(); 19 + }); 20 + } 21 + 22 + function once(fn) { 23 + var called = false; 24 + return function () { 25 + if (called) return; 26 + called = true; 27 + return fn.apply(this, arguments); 28 + }; 29 + } 30 + 31 + function inherits(target, source) { 32 + Object.getOwnPropertyNames(source).forEach(function (name) { 33 + if (name === 'constructor') return; 34 + if (Object.prototype.hasOwnProperty.call(target, name)) return; 35 + Object.defineProperty(target, name, Object.getOwnPropertyDescriptor(source, name)); 36 + }); 37 + } 38 + 39 + function normalizeChunk(chunk, objectMode, encoding) { 40 + if (objectMode || chunk == null || BufferCtor.isBuffer(chunk) || chunk instanceof Uint8Array) return chunk; 41 + if (typeof chunk === 'string') return BufferCtor.from(chunk, encoding || 'utf8'); 42 + return BufferCtor.from(String(chunk), encoding || 'utf8'); 43 + } 44 + 45 + function toAsyncIterable(source) { 46 + if (source && typeof source[Symbol.asyncIterator] === 'function') return source; 47 + if (source && typeof source[Symbol.iterator] === 'function') { 48 + return { 49 + async *[Symbol.asyncIterator]() { 50 + for (var value of source) yield value; 51 + } 52 + }; 53 + } 54 + 55 + if (source && typeof source.getReader === 'function') { 56 + return { 57 + async *[Symbol.asyncIterator]() { 58 + var reader = source.getReader(); 59 + try { 60 + while (true) { 61 + var result = await reader.read(); 62 + if (!result || result.done) break; 63 + yield result.value; 64 + } 65 + } finally { 66 + if (reader.releaseLock) reader.releaseLock(); 67 + } 68 + } 69 + }; 70 + } 71 + 72 + return { 73 + async *[Symbol.asyncIterator]() { 74 + if (source !== undefined) yield source; 75 + } 76 + }; 77 + } 78 + 79 + function Stream(options) { 80 + if (!(this instanceof Stream)) return new Stream(options); 81 + this.readable = true; 82 + this.writable = true; 83 + this.destroyed = false; 84 + this._paused = false; 85 + this._pipes = []; 86 + this._streamOptions = options || {}; 87 + } 88 + 89 + Stream.prototype = Object.create(EventEmitter.prototype); 90 + Stream.prototype.constructor = Stream; 91 + 92 + Stream.prototype.pipe = function (dest, options) { 93 + var source = this; 94 + var end = !options || options.end !== false; 95 + 96 + function onData(chunk) { 97 + if (!dest || typeof dest.write !== 'function') return; 98 + var ok = dest.write(chunk); 99 + if (ok === false && typeof source.pause === 'function') source.pause(); 100 + } 101 + 102 + function onDrain() { 103 + if (typeof source.resume === 'function') source.resume(); 104 + } 105 + 106 + function onEnd() { 107 + cleanup(); 108 + if (end && dest && typeof dest.end === 'function') dest.end(); 109 + } 110 + 111 + function onClose() { 112 + cleanup(); 113 + } 114 + 115 + function onError(err) { 116 + cleanup(); 117 + if (dest && typeof dest.emit === 'function' && dest.listenerCount('error') > 0) dest.emit('error', err); 118 + } 119 + 120 + function cleanup() { 121 + source.removeListener('data', onData); 122 + source.removeListener('end', onEnd); 123 + source.removeListener('close', onClose); 124 + source.removeListener('error', onError); 125 + if (dest && typeof dest.removeListener === 'function') dest.removeListener('drain', onDrain); 126 + source._pipes = source._pipes.filter(function (entry) { 127 + return entry.dest !== dest; 128 + }); 129 + } 130 + 131 + this._pipes.push({ dest: dest, cleanup: cleanup }); 132 + this.on('data', onData); 133 + this.once('end', onEnd); 134 + this.once('close', onClose); 135 + this.on('error', onError); 136 + if (dest && typeof dest.on === 'function') dest.on('drain', onDrain); 137 + if (dest && typeof dest.emit === 'function') dest.emit('pipe', this); 138 + if (typeof this.resume === 'function') this.resume(); 139 + return dest; 140 + }; 141 + 142 + Stream.prototype.unpipe = function (dest) { 143 + var pipes = this._pipes.slice(); 144 + for (var i = 0; i < pipes.length; i++) { 145 + if (!dest || pipes[i].dest === dest) pipes[i].cleanup(); 146 + } 147 + return this; 148 + }; 149 + 150 + Stream.prototype.pause = function () { 151 + this._paused = true; 152 + this.emit('pause'); 153 + return this; 154 + }; 155 + 156 + Stream.prototype.resume = function () { 157 + this._paused = false; 158 + this.emit('resume'); 159 + return this; 160 + }; 161 + 162 + Stream.prototype.isPaused = function () { 163 + return !!this._paused; 164 + }; 165 + 166 + Stream.prototype.destroy = function (err) { 167 + if (this.destroyed) return this; 168 + 169 + var self = this; 170 + this.destroyed = true; 171 + 172 + function done(destroyErr) { 173 + if (destroyErr) self.emit('error', destroyErr); 174 + self.emit('close'); 175 + } 176 + 177 + if (typeof this._destroy === 'function') { 178 + this._destroy(err || null, once(done)); 179 + } else done(err || null); 180 + 181 + return this; 182 + }; 183 + 184 + function Readable(options) { 185 + Stream.call(this, options); 186 + options = options || {}; 187 + this.readable = true; 188 + this.writable = false; 189 + this.readableEnded = false; 190 + this._readableState = { 191 + objectMode: !!options.objectMode, 192 + ended: false, 193 + endEmitted: false, 194 + flowing: false, 195 + reading: false, 196 + highWaterMark: options.highWaterMark || 16384, 197 + buffer: [] 198 + }; 199 + 200 + if (typeof options.read === 'function') this._read = options.read; 201 + } 202 + 203 + Readable.prototype = Object.create(Stream.prototype); 204 + Readable.prototype.constructor = Readable; 205 + 206 + Readable.prototype._read = noop; 207 + 208 + Readable.prototype._flushReadable = function () { 209 + while (this._readableState.flowing && this._readableState.buffer.length > 0) { 210 + var chunk = this._readableState.buffer.shift(); 211 + this.emit('data', chunk); 212 + } 213 + 214 + if (this._readableState.ended && this._readableState.buffer.length === 0 && !this._readableState.endEmitted) { 215 + this._readableState.endEmitted = true; 216 + this.readableEnded = true; 217 + this.emit('end'); 218 + this.emit('close'); 219 + } 220 + }; 221 + 222 + Readable.prototype._maybeRead = function () { 223 + if (this.destroyed || this._readableState.reading || this._readableState.ended) return; 224 + if (this._readableState.buffer.length > 0) return; 225 + 226 + this._readableState.reading = true; 227 + try { 228 + this._read(this._readableState.highWaterMark); 229 + } finally { 230 + this._readableState.reading = false; 231 + } 232 + }; 233 + 234 + Readable.prototype.push = function (chunk, encoding) { 235 + if (this.destroyed) return false; 236 + 237 + if (chunk === null) { 238 + this._readableState.ended = true; 239 + this._flushReadable(); 240 + return false; 241 + } 242 + 243 + this._readableState.buffer.push(normalizeChunk(chunk, this._readableState.objectMode, encoding)); 244 + if (this._readableState.flowing) this._flushReadable(); 245 + return this._readableState.flowing; 246 + }; 247 + 248 + Readable.prototype.read = function () { 249 + if (this._readableState.buffer.length === 0) this._maybeRead(); 250 + if (this._readableState.buffer.length === 0) return null; 251 + 252 + var chunk = this._readableState.buffer.shift(); 253 + if (this._readableState.flowing) this._flushReadable(); 254 + return chunk; 255 + }; 256 + 257 + Readable.prototype.on = function (event, listener) { 258 + var result = Stream.prototype.on.call(this, event, listener); 259 + if (event === 'data') this.resume(); 260 + return result; 261 + }; 262 + 263 + Readable.prototype.resume = function () { 264 + this._readableState.flowing = true; 265 + Stream.prototype.resume.call(this); 266 + this._maybeRead(); 267 + this._flushReadable(); 268 + return this; 269 + }; 270 + 271 + Readable.prototype.pause = function () { 272 + this._readableState.flowing = false; 273 + return Stream.prototype.pause.call(this); 274 + }; 275 + 276 + Readable.from = function (source, options) { 277 + var readable = new Readable(options); 278 + 279 + nextTick(function () { 280 + (async function () { 281 + try { 282 + for await (var chunk of toAsyncIterable(source)) { 283 + if (readable.destroyed) return; 284 + readable.push(chunk); 285 + } 286 + readable.push(null); 287 + } catch (err) { 288 + readable.destroy(err); 289 + } 290 + })(); 291 + }); 292 + 293 + return readable; 294 + }; 295 + 296 + Readable.fromWeb = function (source, options) { 297 + return Readable.from(source, options); 298 + }; 299 + 300 + function Writable(options) { 301 + Stream.call(this, options); 302 + options = options || {}; 303 + this.readable = false; 304 + this.writable = true; 305 + this.writableEnded = false; 306 + this.writableFinished = false; 307 + this._writableState = { 308 + objectMode: !!options.objectMode || !!options.writableObjectMode, 309 + finished: false, 310 + ended: false 311 + }; 312 + 313 + if (typeof options.write === 'function') this._write = options.write; 314 + if (typeof options.final === 'function') this._final = options.final; 315 + if (typeof options.destroy === 'function') this._destroy = options.destroy; 316 + } 317 + 318 + Writable.prototype = Object.create(Stream.prototype); 319 + Writable.prototype.constructor = Writable; 320 + 321 + Writable.prototype._write = function (_chunk, _encoding, callback) { 322 + callback(); 323 + }; 324 + 325 + Writable.prototype._final = function (callback) { 326 + callback(); 327 + }; 328 + 329 + Writable.prototype.write = function (chunk, encoding, callback) { 330 + if (typeof encoding === 'function') { 331 + callback = encoding; 332 + encoding = undefined; 333 + } 334 + 335 + if (this.writableEnded || this.destroyed) { 336 + var writeErr = new Error('write after end'); 337 + if (typeof callback === 'function') callback(writeErr); 338 + else this.emit('error', writeErr); 339 + return false; 340 + } 341 + 342 + var self = this; 343 + var done = once(function (err) { 344 + if (err) { 345 + self.destroy(err); 346 + if (typeof callback === 'function') callback(err); 347 + return; 348 + } 349 + if (typeof callback === 'function') callback(); 350 + self.emit('drain'); 351 + }); 352 + 353 + try { 354 + this._write(normalizeChunk(chunk, this._writableState.objectMode, encoding), encoding || 'utf8', done); 355 + } catch (err) { 356 + done(err); 357 + return false; 358 + } 359 + 360 + return !this.destroyed; 361 + }; 362 + 363 + Writable.prototype.end = function (chunk, encoding, callback) { 364 + if (typeof chunk === 'function') { 365 + callback = chunk; 366 + chunk = undefined; 367 + encoding = undefined; 368 + } else if (typeof encoding === 'function') { 369 + callback = encoding; 370 + encoding = undefined; 371 + } 372 + 373 + if (chunk !== undefined && chunk !== null) this.write(chunk, encoding); 374 + if (this.writableEnded) { 375 + if (typeof callback === 'function') callback(); 376 + return this; 377 + } 378 + 379 + var self = this; 380 + this.writableEnded = true; 381 + this._writableState.ended = true; 382 + 383 + this._final( 384 + once(function (err) { 385 + if (err) { 386 + self.destroy(err); 387 + if (typeof callback === 'function') callback(err); 388 + return; 389 + } 390 + 391 + self.writableFinished = true; 392 + self._writableState.finished = true; 393 + self.emit('finish'); 394 + if (typeof callback === 'function') callback(); 395 + if (!self.readable) self.emit('close'); 396 + }) 397 + ); 398 + 399 + return this; 400 + }; 401 + 402 + Writable.prototype.cork = noop; 403 + Writable.prototype.uncork = noop; 404 + 405 + function Duplex(options) { 406 + Readable.call(this, options); 407 + Writable.call(this, options); 408 + options = options || {}; 409 + this.readable = true; 410 + this.writable = true; 411 + this.allowHalfOpen = options.allowHalfOpen !== false; 412 + } 413 + 414 + Duplex.prototype = Object.create(Readable.prototype); 415 + Duplex.prototype.constructor = Duplex; 416 + inherits(Duplex.prototype, Writable.prototype); 417 + 418 + function Transform(options) { 419 + Duplex.call(this, options); 420 + options = options || {}; 421 + if (typeof options.transform === 'function') this._transform = options.transform; 422 + if (typeof options.flush === 'function') this._flush = options.flush; 423 + } 424 + 425 + Transform.prototype = Object.create(Duplex.prototype); 426 + Transform.prototype.constructor = Transform; 427 + 428 + Transform.prototype._transform = function (chunk, _encoding, callback) { 429 + callback(null, chunk); 430 + }; 431 + 432 + Transform.prototype._write = function (chunk, encoding, callback) { 433 + var self = this; 434 + this._transform(chunk, encoding, function (err, data) { 435 + if (err) { 436 + callback(err); 437 + return; 438 + } 439 + if (data !== undefined && data !== null) self.push(data); 440 + callback(); 441 + }); 442 + }; 443 + 444 + Transform.prototype._final = function (callback) { 445 + var self = this; 446 + if (typeof this._flush === 'function') { 447 + this._flush(function (err, data) { 448 + if (err) { 449 + callback(err); 450 + return; 451 + } 452 + if (data !== undefined && data !== null) self.push(data); 453 + self.push(null); 454 + callback(); 455 + }); 456 + return; 457 + } 458 + 459 + this.push(null); 460 + callback(); 461 + }; 462 + 463 + function PassThrough(options) { 464 + Transform.call(this, options); 465 + } 466 + 467 + PassThrough.prototype = Object.create(Transform.prototype); 468 + PassThrough.prototype.constructor = PassThrough; 469 + 470 + PassThrough.prototype._transform = function (chunk, _encoding, callback) { 471 + callback(null, chunk); 472 + }; 473 + 474 + function finished(stream, callback) { 475 + callback = once(typeof callback === 'function' ? callback : noop); 476 + 477 + function onFinish() { 478 + cleanup(); 479 + callback(); 480 + } 481 + 482 + function onError(err) { 483 + cleanup(); 484 + callback(err); 485 + } 486 + 487 + function cleanup() { 488 + stream.removeListener('end', onFinish); 489 + stream.removeListener('finish', onFinish); 490 + stream.removeListener('close', onFinish); 491 + stream.removeListener('error', onError); 492 + } 493 + 494 + stream.on('end', onFinish); 495 + stream.on('finish', onFinish); 496 + stream.on('close', onFinish); 497 + stream.on('error', onError); 498 + return stream; 499 + } 500 + 501 + function pipeline() { 502 + var args = Array.prototype.slice.call(arguments); 503 + var callback = typeof args[args.length - 1] === 'function' ? args.pop() : noop; 504 + 505 + if (args.length < 2) { 506 + nextTick(callback); 507 + return args[0]; 508 + } 509 + 510 + var done = once(callback); 511 + for (var i = 0; i < args.length - 1; i++) { 512 + finished(args[i], function (err) { 513 + if (err) done(err); 514 + }); 515 + args[i].pipe(args[i + 1]); 516 + } 517 + 518 + finished(args[args.length - 1], done); 519 + return args[args.length - 1]; 520 + } 521 + 522 + var promises = { 523 + pipeline: function () { 524 + var args = Array.prototype.slice.call(arguments); 525 + return new Promise(function (resolve, reject) { 526 + args.push(function (err) { 527 + if (err) reject(err); 528 + else resolve(); 529 + }); 530 + pipeline.apply(undefined, args); 531 + }); 532 + }, 533 + finished: function (stream) { 534 + return new Promise(function (resolve, reject) { 535 + finished(stream, function (err) { 536 + if (err) reject(err); 537 + else resolve(); 538 + }); 539 + }); 540 + } 541 + }; 542 + 543 + Stream.Stream = Stream; 544 + Stream.Readable = Readable; 545 + Stream.Writable = Writable; 546 + Stream.Duplex = Duplex; 547 + Stream.Transform = Transform; 548 + Stream.PassThrough = PassThrough; 549 + Stream.pipeline = pipeline; 550 + Stream.finished = finished; 551 + Stream.promises = promises; 552 + 553 + module.exports = Stream; 554 + module.exports.default = Stream; 555 + module.exports.Stream = Stream; 556 + module.exports.Readable = Readable; 557 + module.exports.Writable = Writable; 558 + module.exports.Duplex = Duplex; 559 + module.exports.Transform = Transform; 560 + module.exports.PassThrough = PassThrough; 561 + module.exports.pipeline = pipeline; 562 + module.exports.finished = finished; 563 + module.exports.promises = promises;
+7
src/builtins/node/stub.txt
··· 1 + // TODO: 2 + the following builtins are currently compatibility stubs, not full node.js implementations: 3 + 4 + - https.mjs 5 + - http2.mjs 6 + - tls.mjs 7 + - stream.cjs
+131
src/builtins/node/tls.mjs
··· 1 + import net from 'node:net'; 2 + import internalTls from 'ant:internal/tls'; 3 + 4 + function defineTlsState(target) { 5 + if (!target || (typeof target !== 'object' && typeof target !== 'function')) return target; 6 + 7 + if (target.encrypted === undefined) target.encrypted = true; 8 + if (target.authorized === undefined) target.authorized = true; 9 + if (target.authorizationError === undefined) target.authorizationError = null; 10 + if (target.secureConnecting === undefined) target.secureConnecting = false; 11 + 12 + return target; 13 + } 14 + 15 + function wrapSocket(socket) { 16 + if (!socket || (typeof socket !== 'object' && typeof socket !== 'function')) return socket; 17 + defineTlsState(socket); 18 + 19 + if (!(socket instanceof TLSSocket)) { 20 + Object.setPrototypeOf(socket, TLSSocket.prototype); 21 + } 22 + 23 + return socket; 24 + } 25 + 26 + function createConnectionArgs(args) { 27 + if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null) { 28 + return args[0]; 29 + } 30 + 31 + const [port, host, options] = args; 32 + const normalized = Object.create(null); 33 + 34 + if (typeof port === 'number') normalized.port = port; 35 + if (typeof host === 'string') normalized.host = host; 36 + if (options && typeof options === 'object') Object.assign(normalized, options); 37 + return normalized; 38 + } 39 + 40 + export class TLSSocket extends net.Socket { 41 + constructor(socket, options = undefined) { 42 + if (socket && typeof socket === 'object') { 43 + super(options); 44 + return wrapSocket(socket); 45 + } 46 + 47 + super(socket); 48 + defineTlsState(this); 49 + } 50 + 51 + getCipher() { 52 + return undefined; 53 + } 54 + 55 + getProtocol() { 56 + return undefined; 57 + } 58 + 59 + getSession() { 60 + return undefined; 61 + } 62 + 63 + getPeerCertificate() { 64 + return {}; 65 + } 66 + 67 + renegotiate(_options, callback) { 68 + if (typeof callback === 'function') callback(null); 69 + return true; 70 + } 71 + } 72 + 73 + export class SecureContext { 74 + constructor(options) { 75 + return internalTls.createContext(options); 76 + } 77 + } 78 + 79 + export function createSecureContext(options) { 80 + return internalTls.createContext(options); 81 + } 82 + 83 + export function isSecureContext(value) { 84 + return internalTls.isContext(value); 85 + } 86 + 87 + export function setConfigPath(path) { 88 + return internalTls.setConfigPath(path); 89 + } 90 + 91 + export function checkServerIdentity() { 92 + return undefined; 93 + } 94 + 95 + export function getCiphers() { 96 + return []; 97 + } 98 + 99 + export function createConnection(...args) { 100 + const socket = net.connect(createConnectionArgs(args)); 101 + return wrapSocket(socket); 102 + } 103 + 104 + export function connect(...args) { 105 + return createConnection(...args); 106 + } 107 + 108 + export const rootCertificates = Object.freeze([]); 109 + export const DEFAULT_ECDH_CURVE = 'auto'; 110 + export const DEFAULT_MIN_VERSION = 'TLSv1.2'; 111 + export const DEFAULT_MAX_VERSION = 'TLSv1.3'; 112 + export const CLIENT_RENEG_LIMIT = 3; 113 + export const CLIENT_RENEG_WINDOW = 600; 114 + 115 + export default { 116 + TLSSocket, 117 + SecureContext, 118 + CLIENT_RENEG_LIMIT, 119 + CLIENT_RENEG_WINDOW, 120 + DEFAULT_ECDH_CURVE, 121 + DEFAULT_MIN_VERSION, 122 + DEFAULT_MAX_VERSION, 123 + rootCertificates, 124 + checkServerIdentity, 125 + connect, 126 + createConnection, 127 + createSecureContext, 128 + getCiphers, 129 + isSecureContext, 130 + setConfigPath 131 + };
+1
src/esm/commonjs.c
··· 33 33 ant_value_t default_export = js_get_slot(ns, SLOT_DEFAULT); 34 34 if (vtype(default_export) != T_UNDEF) return default_export; 35 35 } 36 + 36 37 return ns; 37 38 } 38 39
+1
src/gc/objects.c
··· 483 483 gc_mark_codec_streams(js, gc_mark_value); 484 484 gc_mark_compression_streams(js, gc_mark_value); 485 485 gc_mark_zlib(js, gc_mark_value); 486 + gc_mark_wasm(js, gc_mark_value); 486 487 487 488 for ( 488 489 ant_object_t *obj = g_pending_promises;
+2
src/main.c
··· 79 79 #include "modules/domexception.h" 80 80 #include "modules/abort.h" 81 81 #include "modules/globals.h" 82 + #include "modules/wasm.h" 82 83 #include "modules/structured-clone.h" 83 84 #include "modules/v8.h" 84 85 #include "modules/worker_threads.h" ··· 579 580 init_timer_module(); 580 581 init_domexception_module(); 581 582 init_globals_module(); 583 + init_wasm_module(); 582 584 init_builtin_module(); 583 585 init_buffer_module(); 584 586 init_structured_clone_module();
+41 -15
src/modules/abort.c
··· 44 44 static ant_value_t g_signal_proto = 0; 45 45 static bool g_initialized = false; 46 46 47 + static inline unsigned int abort_array_len(UT_array *arr) { 48 + return arr ? utarray_len(arr) : 0; 49 + } 50 + 47 51 static abort_signal_data_t *get_signal_data(ant_value_t obj) { 48 52 ant_value_t slot = js_get_slot(obj, SLOT_DATA); 49 53 if (vtype(slot) != T_NUM) return NULL; ··· 80 84 abort_signal_data_t *data = get_signal_data(signal_obj); 81 85 if (!data || data->aborted) return; 82 86 83 - UT_array *queue; utarray_new(queue, &abort_value_icd); 84 - UT_array *to_fire; utarray_new(to_fire, &abort_value_icd); 87 + UT_array *queue = NULL; 88 + UT_array *to_fire = NULL; 89 + 90 + utarray_new(queue, &abort_value_icd); 91 + utarray_new(to_fire, &abort_value_icd); 92 + 93 + if (!queue || !to_fire) { 94 + if (queue) utarray_free(queue); 95 + if (to_fire) utarray_free(to_fire); 96 + signal_mark_aborted(js, signal_obj, reason); 97 + return; 98 + } 85 99 86 100 utarray_push_back(queue, &signal_obj); 87 101 ··· 93 107 d->aborted = true; 94 108 d->fired = true; 95 109 d->reason = reason; 110 + 96 111 js_set(js, *cur, "aborted", js_true); 97 112 js_set(js, *cur, "reason", reason); 98 113 utarray_push_back(to_fire, cur); 99 114 100 - unsigned int nf = utarray_len(d->followers); 115 + unsigned int nf = abort_array_len(d->followers); 101 116 for (unsigned int i = 0; i < nf; i++) { 102 117 ant_value_t *sig = (ant_value_t *)utarray_eltptr(d->followers, i); 103 118 utarray_push_back(queue, sig); 104 119 }} 120 + 105 121 utarray_free(queue); 106 - 107 122 for (unsigned int qi = 0; qi < utarray_len(to_fire); qi++) { 108 123 ant_value_t *cur = (ant_value_t *)utarray_eltptr(to_fire, qi); 109 124 abort_signal_data_t *d = get_signal_data(*cur); ··· 120 135 process_microtasks(js); 121 136 } 122 137 123 - unsigned int n = utarray_len(d->listeners); 124 - for (unsigned int i = 0; i < n;) { 138 + for (unsigned int i = 0;;) { 139 + unsigned int n = abort_array_len(d->listeners); 140 + if (i >= n) break; 125 141 abort_listener_t *entry = (abort_listener_t *)utarray_eltptr(d->listeners, i); 142 + 143 + if (!entry) break; 126 144 ant_value_t cb = entry->callback; 127 145 bool once = entry->once; 128 146 ··· 140 158 abort_signal_data_t *data = get_signal_data(signal); 141 159 if (!data) return; 142 160 143 - unsigned int n = utarray_len(data->listeners); 161 + unsigned int n = abort_array_len(data->listeners); 144 162 for (unsigned int i = 0; i < n; i++) { 145 163 abort_listener_t *entry = (abort_listener_t *)utarray_eltptr(data->listeners, i); 146 164 if (entry->callback != callback) continue; ··· 156 174 data->aborted = false; 157 175 data->fired = false; 158 176 data->reason = js_mkundef(); 177 + 159 178 utarray_new(data->listeners, &abort_listener_icd); 160 179 utarray_new(data->followers, &abort_value_icd); 180 + 181 + if (!data->listeners || !data->followers) { 182 + if (data->listeners) utarray_free(data->listeners); 183 + if (data->followers) utarray_free(data->followers); 184 + free(data); 185 + return js_mkerr(js, "AbortSignal: out of memory"); 186 + } 161 187 162 188 ant_value_t obj = js_mkobj(js); 163 189 js_set_slot(obj, SLOT_DATA, ANT_PTR(data)); ··· 189 215 if (vtype(once_val) != T_UNDEF) once = js_truthy(js, once_val); 190 216 } else if (nargs >= 3 && vtype(args[2]) == T_BOOL) once = js_truthy(js, args[2]); 191 217 192 - unsigned int n = utarray_len(data->listeners); 218 + unsigned int n = abort_array_len(data->listeners); 193 219 for (unsigned int i = 0; i < n; i++) { 194 220 abort_listener_t *e = (abort_listener_t *)utarray_eltptr(data->listeners, i); 195 221 if (e->callback == args[1] && e->once == once) return js_mkundef(); 196 222 } 197 223 198 224 abort_listener_t entry = { args[1], once }; 199 - utarray_push_back(data->listeners, &entry); 225 + if (data->listeners) utarray_push_back(data->listeners, &entry); 200 226 201 227 return js_mkundef(); 202 228 } ··· 211 237 abort_signal_data_t *data = get_signal_data(js_getthis(js)); 212 238 if (!data) return js_mkundef(); 213 239 214 - unsigned int n = utarray_len(data->listeners); 240 + unsigned int n = abort_array_len(data->listeners); 215 241 for (unsigned int i = 0; i < n; i++) { 216 242 abort_listener_t *e = (abort_listener_t *)utarray_eltptr(data->listeners, i); 217 243 if (e->callback != args[1]) continue; ··· 280 306 ant_value_t sig = js_arr_get(js, args[0], i); 281 307 abort_signal_data_t *d = get_signal_data(sig); 282 308 if (!d) continue; 283 - utarray_push_back(d->followers, &composite); 309 + if (d->followers) utarray_push_back(d->followers, &composite); 284 310 } 285 311 286 312 return composite; ··· 296 322 if (!d) return composite; 297 323 298 324 if (d->aborted) signal_mark_aborted(js, composite, d->reason); 299 - else utarray_push_back(d->followers, &composite); 325 + else if (d->followers) utarray_push_back(d->followers, &composite); 300 326 301 327 return composite; 302 328 } ··· 430 456 if (!data) return; 431 457 mark(js, data->reason); 432 458 433 - unsigned int listener_count = utarray_len(data->listeners); 459 + unsigned int listener_count = abort_array_len(data->listeners); 434 460 for (unsigned int i = 0; i < listener_count; i++) { 435 461 abort_listener_t *entry = (abort_listener_t *)utarray_eltptr(data->listeners, i); 436 462 if (!entry) continue; 437 463 mark(js, entry->callback); 438 464 } 439 465 440 - unsigned int follower_count = utarray_len(data->followers); 466 + unsigned int follower_count = abort_array_len(data->followers); 441 467 for (unsigned int i = 0; i < follower_count; i++) { 442 468 ant_value_t *follower = (ant_value_t *)utarray_eltptr(data->followers, i); 443 469 if (!follower) continue; ··· 473 499 } 474 500 475 501 abort_listener_t entry = { callback, false }; 476 - utarray_push_back(data->listeners, &entry); 502 + if (data->listeners) utarray_push_back(data->listeners, &entry); 477 503 }
+175 -26
src/modules/buffer.c
··· 221 221 return ptr; 222 222 } 223 223 224 - static ant_value_t js_arraybuffer_slice(ant_t *js, ant_value_t *args, int nargs); 225 - static ant_value_t js_typedarray_slice(ant_t *js, ant_value_t *args, int nargs); 226 - static ant_value_t js_typedarray_subarray(ant_t *js, ant_value_t *args, int nargs); 227 - static ant_value_t js_typedarray_fill(ant_t *js, ant_value_t *args, int nargs); 228 - static ant_value_t js_typedarray_at(ant_t *js, ant_value_t *args, int nargs); 229 - static ant_value_t js_typedarray_set(ant_t *js, ant_value_t *args, int nargs); 230 - static ant_value_t js_typedarray_copyWithin(ant_t *js, ant_value_t *args, int nargs); 231 - static ant_value_t js_typedarray_toReversed(ant_t *js, ant_value_t *args, int nargs); 232 - static ant_value_t js_typedarray_toSorted(ant_t *js, ant_value_t *args, int nargs); 233 - static ant_value_t js_typedarray_with(ant_t *js, ant_value_t *args, int nargs); 234 - static ant_value_t js_dataview_getUint8(ant_t *js, ant_value_t *args, int nargs); 235 - static ant_value_t js_dataview_setUint8(ant_t *js, ant_value_t *args, int nargs); 236 - static ant_value_t js_dataview_getInt16(ant_t *js, ant_value_t *args, int nargs); 237 - static ant_value_t js_dataview_setInt16(ant_t *js, ant_value_t *args, int nargs); 238 - static ant_value_t js_dataview_getInt32(ant_t *js, ant_value_t *args, int nargs); 239 - static ant_value_t js_dataview_setInt32(ant_t *js, ant_value_t *args, int nargs); 240 - static ant_value_t js_dataview_getFloat32(ant_t *js, ant_value_t *args, int nargs); 241 - static ant_value_t js_dataview_setFloat32(ant_t *js, ant_value_t *args, int nargs); 242 - static ant_value_t js_dataview_getFloat64(ant_t *js, ant_value_t *args, int nargs); 243 - static ant_value_t js_dataview_setFloat64(ant_t *js, ant_value_t *args, int nargs); 244 - static ant_value_t js_typedarray_toString(ant_t *js, ant_value_t *args, int nargs); 245 - static ant_value_t js_buffer_slice(ant_t *js, ant_value_t *args, int nargs); 246 - static ant_value_t js_buffer_toString(ant_t *js, ant_value_t *args, int nargs); 247 - static ant_value_t js_buffer_toBase64(ant_t *js, ant_value_t *args, int nargs); 248 - static ant_value_t js_buffer_write(ant_t *js, ant_value_t *args, int nargs); 249 - 250 224 ArrayBufferData *create_array_buffer_data(size_t length) { 251 225 ArrayBufferData *data = ant_calloc(sizeof(ArrayBufferData) + length); 252 226 if (!data) return NULL; ··· 1305 1279 } 1306 1280 1307 1281 // DataView.prototype.getUint8(byteOffset) 1282 + static ant_value_t js_dataview_getInt8(ant_t *js, ant_value_t *args, int nargs) { 1283 + if (nargs < 1) return js_mkerr(js, "getInt8 requires byteOffset"); 1284 + 1285 + ant_value_t this_val = js_getthis(js); 1286 + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); 1287 + 1288 + if (vtype(dv_data_val) != T_NUM) { 1289 + return js_mkerr(js, "Not a DataView"); 1290 + } 1291 + 1292 + DataViewData *dv = (DataViewData *)(uintptr_t)js_getnum(dv_data_val); 1293 + size_t offset = (size_t)js_getnum(args[0]); 1294 + 1295 + if (offset >= dv->byte_length) { 1296 + return js_mkerr(js, "Offset out of bounds"); 1297 + } 1298 + 1299 + int8_t value = (int8_t)dv->buffer->data[dv->byte_offset + offset]; 1300 + return js_mknum((double)value); 1301 + } 1302 + 1303 + // DataView.prototype.setInt8(byteOffset, value) 1304 + static ant_value_t js_dataview_setInt8(ant_t *js, ant_value_t *args, int nargs) { 1305 + if (nargs < 2) return js_mkerr(js, "setInt8 requires byteOffset and value"); 1306 + 1307 + ant_value_t this_val = js_getthis(js); 1308 + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); 1309 + 1310 + if (vtype(dv_data_val) != T_NUM) { 1311 + return js_mkerr(js, "Not a DataView"); 1312 + } 1313 + 1314 + DataViewData *dv = (DataViewData *)(uintptr_t)js_getnum(dv_data_val); 1315 + size_t offset = (size_t)js_getnum(args[0]); 1316 + int8_t value = (int8_t)js_to_int32(js_getnum(args[1])); 1317 + 1318 + if (offset >= dv->byte_length) { 1319 + return js_mkerr(js, "Offset out of bounds"); 1320 + } 1321 + 1322 + dv->buffer->data[dv->byte_offset + offset] = (uint8_t)value; 1323 + return js_mkundef(); 1324 + } 1325 + 1326 + // DataView.prototype.getUint8(byteOffset) 1308 1327 static ant_value_t js_dataview_getUint8(ant_t *js, ant_value_t *args, int nargs) { 1309 1328 if (nargs < 1) return js_mkerr(js, "getUint8 requires byteOffset"); 1310 1329 ··· 1380 1399 return js_mknum((double)value); 1381 1400 } 1382 1401 1402 + // DataView.prototype.getUint16(byteOffset, littleEndian) 1403 + static ant_value_t js_dataview_getUint16(ant_t *js, ant_value_t *args, int nargs) { 1404 + if (nargs < 1) return js_mkerr(js, "getUint16 requires byteOffset"); 1405 + 1406 + ant_value_t this_val = js_getthis(js); 1407 + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); 1408 + 1409 + if (vtype(dv_data_val) != T_NUM) { 1410 + return js_mkerr(js, "Not a DataView"); 1411 + } 1412 + 1413 + DataViewData *dv = (DataViewData *)(uintptr_t)js_getnum(dv_data_val); 1414 + size_t offset = (size_t)js_getnum(args[0]); 1415 + bool little_endian = (nargs > 1 && js_truthy(js, args[1])); 1416 + 1417 + if (offset + 2 > dv->byte_length) { 1418 + return js_mkerr(js, "Offset out of bounds"); 1419 + } 1420 + 1421 + uint8_t *ptr = dv->buffer->data + dv->byte_offset + offset; 1422 + uint16_t value; 1423 + 1424 + if (little_endian) value = (uint16_t)(ptr[0] | (ptr[1] << 8)); 1425 + else value = (uint16_t)((ptr[0] << 8) | ptr[1]); 1426 + 1427 + return js_mknum((double)value); 1428 + } 1429 + 1430 + // DataView.prototype.setUint16(byteOffset, value, littleEndian) 1431 + static ant_value_t js_dataview_setUint16(ant_t *js, ant_value_t *args, int nargs) { 1432 + if (nargs < 2) return js_mkerr(js, "setUint16 requires byteOffset and value"); 1433 + 1434 + ant_value_t this_val = js_getthis(js); 1435 + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); 1436 + 1437 + if (vtype(dv_data_val) != T_NUM) { 1438 + return js_mkerr(js, "Not a DataView"); 1439 + } 1440 + 1441 + DataViewData *dv = (DataViewData *)(uintptr_t)js_getnum(dv_data_val); 1442 + size_t offset = (size_t)js_getnum(args[0]); 1443 + uint16_t value = (uint16_t)js_to_uint32(js_getnum(args[1])); 1444 + bool little_endian = (nargs > 2 && js_truthy(js, args[2])); 1445 + 1446 + if (offset + 2 > dv->byte_length) { 1447 + return js_mkerr(js, "Offset out of bounds"); 1448 + } 1449 + 1450 + uint8_t *ptr = dv->buffer->data + dv->byte_offset + offset; 1451 + 1452 + if (little_endian) { 1453 + ptr[0] = (uint8_t)(value & 0xFF); 1454 + ptr[1] = (uint8_t)((value >> 8) & 0xFF); 1455 + } else { 1456 + ptr[0] = (uint8_t)((value >> 8) & 0xFF); 1457 + ptr[1] = (uint8_t)(value & 0xFF); 1458 + } 1459 + 1460 + return js_mkundef(); 1461 + } 1462 + 1383 1463 // DataView.prototype.getInt32(byteOffset, littleEndian) 1384 1464 static ant_value_t js_dataview_getInt32(ant_t *js, ant_value_t *args, int nargs) { 1385 1465 if (nargs < 1) return js_mkerr(js, "getInt32 requires byteOffset"); ··· 1497 1577 return js_mkerr(js, "Offset out of bounds"); 1498 1578 } 1499 1579 1580 + uint8_t *ptr = dv->buffer->data + dv->byte_offset + offset; 1581 + 1582 + if (little_endian) { 1583 + ptr[0] = (uint8_t)(value & 0xFF); 1584 + ptr[1] = (uint8_t)((value >> 8) & 0xFF); 1585 + ptr[2] = (uint8_t)((value >> 16) & 0xFF); 1586 + ptr[3] = (uint8_t)((value >> 24) & 0xFF); 1587 + } else { 1588 + ptr[0] = (uint8_t)((value >> 24) & 0xFF); 1589 + ptr[1] = (uint8_t)((value >> 16) & 0xFF); 1590 + ptr[2] = (uint8_t)((value >> 8) & 0xFF); 1591 + ptr[3] = (uint8_t)(value & 0xFF); 1592 + } 1593 + 1594 + return js_mkundef(); 1595 + } 1596 + 1597 + // DataView.prototype.getUint32(byteOffset, littleEndian) 1598 + static ant_value_t js_dataview_getUint32(ant_t *js, ant_value_t *args, int nargs) { 1599 + if (nargs < 1) return js_mkerr(js, "getUint32 requires byteOffset"); 1600 + 1601 + ant_value_t this_val = js_getthis(js); 1602 + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); 1603 + 1604 + if (vtype(dv_data_val) != T_NUM) { 1605 + return js_mkerr(js, "Not a DataView"); 1606 + } 1607 + 1608 + DataViewData *dv = (DataViewData *)(uintptr_t)js_getnum(dv_data_val); 1609 + size_t offset = (size_t)js_getnum(args[0]); 1610 + bool little_endian = (nargs > 1 && js_truthy(js, args[1])); 1611 + 1612 + if (offset + 4 > dv->byte_length) { 1613 + return js_mkerr(js, "Offset out of bounds"); 1614 + } 1615 + 1616 + uint8_t *ptr = dv->buffer->data + dv->byte_offset + offset; 1617 + uint32_t value; 1618 + 1619 + if (little_endian) value = (uint32_t)(ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)); 1620 + else value = (uint32_t)((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]); 1621 + 1622 + return js_mknum((double)value); 1623 + } 1624 + 1625 + // DataView.prototype.setUint32(byteOffset, value, littleEndian) 1626 + static ant_value_t js_dataview_setUint32(ant_t *js, ant_value_t *args, int nargs) { 1627 + if (nargs < 2) return js_mkerr(js, "setUint32 requires byteOffset and value"); 1628 + 1629 + ant_value_t this_val = js_getthis(js); 1630 + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); 1631 + 1632 + if (vtype(dv_data_val) != T_NUM) { 1633 + return js_mkerr(js, "Not a DataView"); 1634 + } 1635 + 1636 + DataViewData *dv = (DataViewData *)(uintptr_t)js_getnum(dv_data_val); 1637 + size_t offset = (size_t)js_getnum(args[0]); 1638 + 1639 + uint32_t value = js_to_uint32(js_getnum(args[1])); 1640 + bool little_endian = (nargs > 2 && js_truthy(js, args[2])); 1641 + 1642 + if (offset + 4 > dv->byte_length) return js_mkerr(js, "Offset out of bounds"); 1500 1643 uint8_t *ptr = dv->buffer->data + dv->byte_offset + offset; 1501 1644 1502 1645 if (little_endian) { ··· 2267 2410 ant_value_t dataview_proto = js_mkobj(js); 2268 2411 js_set_proto_init(dataview_proto, object_proto); 2269 2412 2413 + js_set(js, dataview_proto, "getInt8", js_mkfun(js_dataview_getInt8)); 2414 + js_set(js, dataview_proto, "setInt8", js_mkfun(js_dataview_setInt8)); 2270 2415 js_set(js, dataview_proto, "getUint8", js_mkfun(js_dataview_getUint8)); 2271 2416 js_set(js, dataview_proto, "setUint8", js_mkfun(js_dataview_setUint8)); 2272 2417 js_set(js, dataview_proto, "getInt16", js_mkfun(js_dataview_getInt16)); 2273 2418 js_set(js, dataview_proto, "setInt16", js_mkfun(js_dataview_setInt16)); 2419 + js_set(js, dataview_proto, "getUint16", js_mkfun(js_dataview_getUint16)); 2420 + js_set(js, dataview_proto, "setUint16", js_mkfun(js_dataview_setUint16)); 2274 2421 js_set(js, dataview_proto, "getInt32", js_mkfun(js_dataview_getInt32)); 2275 2422 js_set(js, dataview_proto, "setInt32", js_mkfun(js_dataview_setInt32)); 2423 + js_set(js, dataview_proto, "getUint32", js_mkfun(js_dataview_getUint32)); 2424 + js_set(js, dataview_proto, "setUint32", js_mkfun(js_dataview_setUint32)); 2276 2425 js_set(js, dataview_proto, "getFloat32", js_mkfun(js_dataview_getFloat32)); 2277 2426 js_set(js, dataview_proto, "setFloat32", js_mkfun(js_dataview_setFloat32)); 2278 2427 js_set(js, dataview_proto, "getFloat64", js_mkfun(js_dataview_getFloat64));
+107
src/modules/child_process.c
··· 1189 1189 return cp->promise; 1190 1190 } 1191 1191 1192 + static ant_value_t exec_file_close_callback(ant_t *js, ant_value_t *args, int nargs) { 1193 + ant_value_t fn = js_getcurrentfunc(js); 1194 + ant_value_t ctx = js_get_slot(fn, SLOT_DATA); 1195 + 1196 + ant_value_t callback = js_get(js, ctx, "callback"); 1197 + ant_value_t child = js_get(js, ctx, "child"); 1198 + 1199 + ant_value_t stdout_val = js_get(js, child, "stdoutText"); 1200 + ant_value_t stderr_val = js_get(js, child, "stderrText"); 1201 + ant_value_t exit_code_val = js_get(js, child, "exitCode"); 1202 + ant_value_t cb_args[3]; 1203 + 1204 + if (!is_callable(callback)) return js_mkundef(); 1205 + 1206 + if (vtype(exit_code_val) == T_NUM && (int)js_getnum(exit_code_val) != 0) { 1207 + char err_msg[256]; 1208 + snprintf(err_msg, sizeof(err_msg), "Command failed with exit code %d", (int)js_getnum(exit_code_val)); 1209 + cb_args[0] = js_make_error_silent(js, JS_ERR_GENERIC, err_msg); 1210 + } else cb_args[0] = js_mknull(); 1211 + 1212 + cb_args[1] = stdout_val; 1213 + cb_args[2] = stderr_val; 1214 + 1215 + ant_value_t result = sv_vm_call(js->vm, js, callback, js_mkundef(), cb_args, 3, NULL, false); 1216 + if (vtype(result) == T_ERR) log_listener_error(js, "execFile", result); 1217 + return js_mkundef(); 1218 + } 1219 + 1220 + static ant_value_t builtin_execFile(ant_t *js, ant_value_t *args, int nargs) { 1221 + ant_value_t argv = js_mkundef(); 1222 + ant_value_t options = js_mkundef(); 1223 + ant_value_t callback = js_mkundef(); 1224 + 1225 + ant_value_t spawn_args[3]; 1226 + ant_value_t child; 1227 + 1228 + if (nargs < 1) return js_mkerr(js, "execFile() requires a file"); 1229 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "File must be a string"); 1230 + 1231 + if (nargs >= 2 && is_callable(args[nargs - 1])) { 1232 + callback = args[nargs - 1]; 1233 + nargs--; 1234 + } 1235 + 1236 + if (nargs >= 2) { 1237 + if (vtype(args[1]) == T_ARR) { 1238 + argv = args[1]; 1239 + if (nargs >= 3 && is_special_object(args[2])) options = args[2]; 1240 + } else if (is_special_object(args[1])) options = args[1]; 1241 + } 1242 + 1243 + spawn_args[0] = args[0]; 1244 + spawn_args[1] = argv; 1245 + spawn_args[2] = options; 1246 + 1247 + child = builtin_spawn(js, spawn_args, 3); 1248 + if (vtype(child) != T_OBJ || !is_callable(callback)) return child; 1249 + 1250 + ant_value_t ctx = js_mkobj(js); 1251 + js_set(js, ctx, "callback", callback); 1252 + js_set(js, ctx, "child", child); 1253 + 1254 + // TODO: reduce duck-typing 1255 + ant_value_t close_listener = js_heavy_mkfun(js, exec_file_close_callback, ctx); 1256 + ant_value_t once_fn = js_get(js, child, "once"); 1257 + ant_value_t once_args[2] = { js_mkstr(js, "close", 5), close_listener }; 1258 + 1259 + ant_value_t once_result = sv_vm_call(js->vm, js, once_fn, child, once_args, 2, NULL, false); 1260 + if (vtype(once_result) == T_ERR) return once_result; 1261 + 1262 + return child; 1263 + } 1264 + 1192 1265 static ant_value_t builtin_execSync(ant_t *js, ant_value_t *args, int nargs) { 1193 1266 if (nargs < 1) return js_mkerr(js, "execSync() requires a command"); 1194 1267 if (vtype(args[0]) != T_STR) return js_mkerr(js, "Command must be a string"); ··· 1539 1612 } 1540 1613 #endif 1541 1614 1615 + static ant_value_t builtin_execFileSync(ant_t *js, ant_value_t *args, int nargs) { 1616 + ant_value_t argv = js_mkundef(); 1617 + ant_value_t options = js_mkundef(); 1618 + 1619 + ant_value_t spawn_args[3]; 1620 + ant_value_t result; 1621 + ant_value_t status; 1622 + 1623 + if (nargs < 1) return js_mkerr(js, "execFileSync() requires a file"); 1624 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "File must be a string"); 1625 + 1626 + if (nargs >= 2) { 1627 + if (vtype(args[1]) == T_ARR) { 1628 + argv = args[1]; 1629 + if (nargs >= 3 && is_special_object(args[2])) options = args[2]; 1630 + } else if (is_special_object(args[1])) options = args[1]; 1631 + } 1632 + 1633 + spawn_args[0] = args[0]; 1634 + spawn_args[1] = argv; 1635 + spawn_args[2] = options; 1636 + 1637 + result = builtin_spawnSync(js, spawn_args, 3); 1638 + if (vtype(result) != T_OBJ) return result; 1639 + 1640 + status = js_get(js, result, "status"); 1641 + if (vtype(status) == T_NUM && (int)js_getnum(status) != 0) 1642 + return js_mkerr(js, "Command failed with exit code %d", (int)js_getnum(status)); 1643 + 1644 + return js_get(js, result, "stdout"); 1645 + } 1646 + 1542 1647 static ant_value_t builtin_fork(ant_t *js, ant_value_t *args, int nargs) { 1543 1648 if (nargs < 1) return js_mkerr(js, "fork() requires a module path"); 1544 1649 if (vtype(args[0]) != T_STR) return js_mkerr(js, "Module path must be a string"); ··· 1598 1703 1599 1704 js_set(js, lib, "spawn", js_mkfun(builtin_spawn)); 1600 1705 js_set(js, lib, "exec", js_mkfun(builtin_exec)); 1706 + js_set(js, lib, "execFile", js_mkfun(builtin_execFile)); 1601 1707 js_set(js, lib, "execSync", js_mkfun(builtin_execSync)); 1708 + js_set(js, lib, "execFileSync", js_mkfun(builtin_execFileSync)); 1602 1709 js_set(js, lib, "spawnSync", js_mkfun(builtin_spawnSync)); 1603 1710 js_set(js, lib, "fork", js_mkfun(builtin_fork)); 1604 1711 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "child_process", 13));
+385 -10
src/modules/crypto.c
··· 1 1 #include <sodium.h> 2 2 #include <string.h> 3 3 #include <time.h> 4 + #include <openssl/evp.h> 4 5 5 6 #pragma GCC diagnostic push 6 7 #pragma GCC diagnostic ignored "-Wimplicit-int-conversion" ··· 14 15 #endif 15 16 16 17 #include "ant.h" 18 + #include "base64.h" 17 19 #include "errors.h" 18 20 #include "runtime.h" 19 21 #include "modules/crypto.h" 20 22 #include "modules/buffer.h" 21 23 #include "modules/symbol.h" 22 24 25 + typedef enum { 26 + CRYPTO_TEXT_UTF8 = 0, 27 + CRYPTO_TEXT_HEX, 28 + CRYPTO_TEXT_BASE64, 29 + CRYPTO_TEXT_LATIN1 30 + } crypto_text_encoding_t; 31 + 32 + typedef struct { 33 + EVP_MD_CTX *ctx; 34 + const EVP_MD *md; 35 + unsigned char digest[EVP_MAX_MD_SIZE]; 36 + unsigned int digest_len; 37 + bool finalized; 38 + } ant_hash_state_t; 39 + 40 + static ant_value_t crypto_make_buffer(ant_t *js, const uint8_t *data, size_t len) { 41 + ArrayBufferData *buffer = create_array_buffer_data(len); 42 + if (!buffer) return js_mkerr(js, "Failed to allocate Buffer"); 43 + if (len > 0 && data) memcpy(buffer->data, data, len); 44 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, len, "Buffer"); 45 + } 46 + 47 + static bool crypto_get_mutable_bytes(ant_value_t value, uint8_t **out, size_t *len) { 48 + ant_value_t slot = js_get_slot(value, SLOT_BUFFER); 49 + if (vtype(slot) == T_TYPEDARRAY) { 50 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); 51 + if (!ta || !ta->buffer || ta->buffer->is_detached) return false; 52 + *out = ta->buffer->data + ta->byte_offset; 53 + *len = ta->byte_length; 54 + return true; 55 + } 56 + 57 + if (vtype(slot) == T_NUM) { 58 + ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(slot); 59 + if (!ab || ab->is_detached) return false; 60 + *out = ab->data; 61 + *len = ab->length; 62 + return true; 63 + } 64 + 65 + return false; 66 + } 67 + 68 + static crypto_text_encoding_t crypto_parse_encoding(const char *str, size_t len) { 69 + if (len == 3 && strncasecmp(str, "hex", 3) == 0) return CRYPTO_TEXT_HEX; 70 + if (len == 6 && strncasecmp(str, "base64", 6) == 0) return CRYPTO_TEXT_BASE64; 71 + if (len == 9 && strncasecmp(str, "base64url", 9) == 0) return CRYPTO_TEXT_BASE64; 72 + 73 + if ( 74 + (len == 5 && strncasecmp(str, "ascii", 5) == 0) || 75 + (len == 6 && strncasecmp(str, "latin1", 6) == 0) || 76 + (len == 6 && strncasecmp(str, "binary", 6) == 0) 77 + ) return CRYPTO_TEXT_LATIN1; 78 + 79 + return CRYPTO_TEXT_UTF8; 80 + } 81 + 82 + static uint8_t crypto_hex_nibble(char ch) { 83 + if (ch >= '0' && ch <= '9') return (uint8_t)(ch - '0'); 84 + if (ch >= 'a' && ch <= 'f') return (uint8_t)(10 + ch - 'a'); 85 + if (ch >= 'A' && ch <= 'F') return (uint8_t)(10 + ch - 'A'); 86 + return 0xFFu; 87 + } 88 + 89 + static uint8_t *crypto_decode_hex(const char *str, size_t len, size_t *out_len) { 90 + if ((len & 1u) != 0) return NULL; 91 + 92 + uint8_t *buf = malloc(len / 2u); 93 + if (!buf) return NULL; 94 + 95 + for (size_t i = 0; i < len; i += 2u) { 96 + uint8_t hi = crypto_hex_nibble(str[i]); 97 + uint8_t lo = crypto_hex_nibble(str[i + 1u]); 98 + if (hi == 0xFFu || lo == 0xFFu) { 99 + free(buf); 100 + return NULL; 101 + } 102 + buf[i / 2u] = (uint8_t)((hi << 4u) | lo); 103 + } 104 + 105 + *out_len = len / 2u; 106 + return buf; 107 + } 108 + 109 + static ant_value_t crypto_get_input_bytes( 110 + ant_t *js, ant_value_t value, ant_value_t encoding_val, 111 + const uint8_t **out_bytes, size_t *out_len, uint8_t **owned 112 + ) { 113 + const uint8_t *bytes = NULL; 114 + size_t len = 0; 115 + uint8_t *buf = NULL; 116 + 117 + ant_value_t string_val; 118 + size_t str_len = 0; 119 + 120 + const char *str = NULL; 121 + crypto_text_encoding_t enc = CRYPTO_TEXT_UTF8; 122 + 123 + if (buffer_source_get_bytes(js, value, &bytes, &len)) { 124 + *out_bytes = bytes; 125 + *out_len = len; 126 + *owned = NULL; 127 + return js_mkundef(); 128 + } 129 + 130 + string_val = js_tostring_val(js, value); 131 + if (is_err(string_val)) return string_val; 132 + 133 + str = js_getstr(js, string_val, &str_len); 134 + if (!str) return js_mkerr(js, "Failed to convert hash input to string"); 135 + 136 + if (vtype(encoding_val) == T_STR) { 137 + size_t enc_len = 0; 138 + const char *enc_str = js_getstr(js, encoding_val, &enc_len); 139 + if (enc_str) enc = crypto_parse_encoding(enc_str, enc_len); 140 + } 141 + 142 + switch (enc) { 143 + case CRYPTO_TEXT_HEX: 144 + buf = crypto_decode_hex(str, str_len, &len); 145 + if (!buf) return js_mkerr(js, "Invalid hex string"); 146 + bytes = buf; 147 + break; 148 + case CRYPTO_TEXT_BASE64: 149 + buf = ant_base64_decode(str, str_len, &len); 150 + if (!buf) return js_mkerr(js, "Invalid base64 string"); 151 + bytes = buf; 152 + break; 153 + case CRYPTO_TEXT_LATIN1: 154 + buf = malloc(str_len); 155 + if (!buf) return js_mkerr(js, "Out of memory"); 156 + for (size_t i = 0; i < str_len; i++) buf[i] = (uint8_t)str[i]; 157 + bytes = buf; 158 + len = str_len; 159 + break; 160 + case CRYPTO_TEXT_UTF8: 161 + default: 162 + bytes = (const uint8_t *)str; 163 + len = str_len; 164 + break; 165 + } 166 + 167 + *out_bytes = bytes; 168 + *out_len = len; 169 + *owned = buf; 170 + return js_mkundef(); 171 + } 172 + 173 + static ant_hash_state_t *crypto_get_hash_state(ant_value_t value) { 174 + ant_value_t slot = js_get_slot(value, SLOT_DATA); 175 + if (vtype(slot) != T_NUM) return NULL; 176 + 177 + ant_hash_state_t *state = (ant_hash_state_t *)(uintptr_t)js_getnum(slot); 178 + return (state && state->ctx) ? state : NULL; 179 + } 180 + 181 + static ant_value_t crypto_require_hash_state( 182 + ant_t *js, ant_value_t value, ant_hash_state_t **out_state 183 + ) { 184 + ant_hash_state_t *state = crypto_get_hash_state(value); 185 + if (!state) return js_mkerr(js, "Invalid Hash state"); 186 + *out_state = state; 187 + return js_mkundef(); 188 + } 189 + 190 + static ant_value_t crypto_digest_result( 191 + ant_t *js, const uint8_t *digest, size_t digest_len, ant_value_t encoding_val 192 + ) { 193 + if (vtype(encoding_val) != T_STR) return crypto_make_buffer(js, digest, digest_len); 194 + 195 + size_t enc_len = 0; 196 + const char *enc = js_getstr(js, encoding_val, &enc_len); 197 + if (!enc) return crypto_make_buffer(js, digest, digest_len); 198 + 199 + crypto_text_encoding_t encoding = crypto_parse_encoding(enc, enc_len); 200 + 201 + if (encoding == CRYPTO_TEXT_HEX) { 202 + char *hex = malloc(digest_len * 2u + 1u); 203 + if (!hex) return js_mkerr(js, "Out of memory"); 204 + for (size_t i = 0; i < digest_len; i++) { 205 + snprintf(hex + (i * 2u), 3, "%02x", digest[i]); 206 + } 207 + ant_value_t result = js_mkstr(js, hex, digest_len * 2u); 208 + free(hex); 209 + return result; 210 + } 211 + 212 + if (encoding == CRYPTO_TEXT_BASE64) { 213 + size_t out_len = 0; 214 + char *encoded = ant_base64_encode(digest, digest_len, &out_len); 215 + if (!encoded) return js_mkerr(js, "Failed to encode base64"); 216 + 217 + if (enc_len == 9 && strncasecmp(enc, "base64url", 9) == 0) { 218 + for (size_t i = 0; i < out_len; i++) { 219 + if (encoded[i] == '+') encoded[i] = '-'; 220 + else if (encoded[i] == '/') encoded[i] = '_'; 221 + } 222 + while (out_len > 0 && encoded[out_len - 1u] == '=') out_len--; 223 + } 224 + 225 + ant_value_t result = js_mkstr(js, encoded, out_len); 226 + free(encoded); 227 + 228 + return result; 229 + } 230 + 231 + return crypto_make_buffer(js, digest, digest_len); 232 + } 233 + 23 234 int ensure_crypto_init(void) { 24 235 static int crypto_initialized = 0; 25 236 ··· 83 294 } 84 295 85 296 randombytes_buf(random_bytes, length); 86 - 87 - ant_value_t array = js_mkobj(js); 88 - js_set(js, array, "length", js_mknum((double)length)); 89 - 90 - for (int i = 0; i < length; i++) { 91 - char key[16]; 92 - snprintf(key, sizeof(key), "%d", i); 93 - js_set(js, array, key, js_mknum((double)random_bytes[i])); 94 - } 297 + ant_value_t array = crypto_make_buffer(js, random_bytes, (size_t)length); 298 + free(random_bytes); 95 299 96 - free(random_bytes); 97 300 return array; 98 301 } 99 302 ··· 167 370 return args[0]; 168 371 } 169 372 373 + static ant_value_t js_crypto_random_fill_sync(ant_t *js, ant_value_t *args, int nargs) { 374 + if (nargs < 1) return js_mkerr(js, "randomFillSync requires a target"); 375 + if (ensure_crypto_init() < 0) return js_mkerr(js, "libsodium initialization failed"); 376 + 377 + uint8_t *bytes = NULL; 378 + size_t len = 0; 379 + if (!crypto_get_mutable_bytes(args[0], &bytes, &len)) { 380 + return js_mkerr(js, "randomFillSync target must be a Buffer, TypedArray, or ArrayBuffer"); 381 + } 382 + 383 + size_t offset = 0; 384 + if (nargs >= 2 && vtype(args[1]) != T_UNDEF) { 385 + double num = js_to_number(js, args[1]); 386 + if (num < 0) return js_mkerr(js, "randomFillSync offset must be non-negative"); 387 + offset = (size_t)num; 388 + } 389 + 390 + size_t size = len - offset; 391 + if (nargs >= 3 && vtype(args[2]) != T_UNDEF) { 392 + double num = js_to_number(js, args[2]); 393 + if (num < 0) return js_mkerr(js, "randomFillSync size must be non-negative"); 394 + size = (size_t)num; 395 + } 396 + 397 + if (offset > len || size > len - offset) { 398 + return js_mkerr(js, "randomFillSync range is out of bounds"); 399 + } 400 + 401 + randombytes_buf(bytes + offset, size); 402 + return args[0]; 403 + } 404 + 405 + static ant_value_t js_hash_update(ant_t *js, ant_value_t *args, int nargs) { 406 + ant_value_t this_val = js_getthis(js); 407 + ant_hash_state_t *state = NULL; 408 + 409 + const uint8_t *bytes = NULL; 410 + size_t len = 0; 411 + 412 + uint8_t *owned = NULL; 413 + ant_value_t err; 414 + 415 + err = crypto_require_hash_state(js, this_val, &state); 416 + if (is_err(err)) return err; 417 + if (state->finalized) return js_mkerr(js, "Hash digest already called"); 418 + if (nargs < 1) return js_mkerr(js, "Hash.update requires data"); 419 + 420 + ant_value_t encoding = (nargs >= 2) ? args[1] : js_mkundef(); 421 + err = crypto_get_input_bytes(js, args[0], encoding, &bytes, &len, &owned); 422 + if (is_err(err)) goto cleanup; 423 + 424 + if (EVP_DigestUpdate(state->ctx, bytes, len) != 1) { 425 + err = js_mkerr(js, "Hash update failed"); 426 + goto cleanup; 427 + } 428 + 429 + err = this_val; 430 + 431 + cleanup: 432 + if (owned) free(owned); 433 + return err; 434 + } 435 + 436 + static ant_value_t js_hash_digest(ant_t *js, ant_value_t *args, int nargs) { 437 + ant_hash_state_t *state = NULL; 438 + ant_value_t err = crypto_require_hash_state(js, js_getthis(js), &state); 439 + 440 + if (is_err(err)) return err; 441 + if (state->finalized) return js_mkerr(js, "Hash digest already called"); 442 + 443 + if (EVP_DigestFinal_ex(state->ctx, state->digest, &state->digest_len) != 1) { 444 + return js_mkerr(js, "Hash digest failed"); 445 + } 446 + 447 + state->finalized = true; 448 + EVP_MD_CTX_free(state->ctx); 449 + state->ctx = NULL; 450 + 451 + return crypto_digest_result(js, state->digest, state->digest_len, nargs >= 1 ? args[0] : js_mkundef()); 452 + } 453 + 454 + static ant_value_t js_crypto_create_hash(ant_t *js, ant_value_t *args, int nargs) { 455 + if (nargs < 1) return js_mkerr(js, "createHash requires an algorithm"); 456 + 457 + ant_value_t algo_val = js_tostring_val(js, args[0]); 458 + if (is_err(algo_val)) return algo_val; 459 + 460 + size_t algo_len = 0; 461 + const char *algo = js_getstr(js, algo_val, &algo_len); 462 + if (!algo || algo_len == 0) return js_mkerr(js, "Invalid hash algorithm"); 463 + 464 + char *algo_name = strndup(algo, algo_len); 465 + if (!algo_name) return js_mkerr(js, "Out of memory"); 466 + 467 + const EVP_MD *md = EVP_get_digestbyname(algo_name); 468 + free(algo_name); 469 + if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported hash algorithm"); 470 + 471 + ant_hash_state_t *state = calloc(1, sizeof(*state)); 472 + if (!state) return js_mkerr(js, "Out of memory"); 473 + 474 + state->ctx = EVP_MD_CTX_new(); 475 + state->md = md; 476 + if (!state->ctx || EVP_DigestInit_ex(state->ctx, md, NULL) != 1) { 477 + if (state->ctx) EVP_MD_CTX_free(state->ctx); 478 + free(state); 479 + return js_mkerr(js, "Failed to initialize hash"); 480 + } 481 + 482 + ant_value_t obj = js_mkobj(js); 483 + js_set(js, obj, "update", js_mkfun(js_hash_update)); 484 + js_set(js, obj, "digest", js_mkfun(js_hash_digest)); 485 + 486 + js_set_slot(obj, SLOT_DATA, ANT_PTR(state)); 487 + js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, "Hash", 4)); 488 + 489 + return obj; 490 + } 491 + 492 + static ant_value_t js_crypto_hash(ant_t *js, ant_value_t *args, int nargs) { 493 + ant_value_t algo_val; 494 + ant_value_t output_encoding; 495 + 496 + const char *algo; 497 + size_t algo_len = 0; 498 + char *algo_name = NULL; 499 + 500 + const EVP_MD *md = NULL; 501 + const uint8_t *bytes = NULL; 502 + 503 + size_t len = 0; 504 + uint8_t *owned = NULL; 505 + 506 + unsigned char digest[EVP_MAX_MD_SIZE]; 507 + unsigned int digest_len = 0; 508 + ant_value_t err; 509 + 510 + if (nargs < 2) return js_mkerr(js, "hash requires algorithm and data"); 511 + output_encoding = nargs >= 3 ? args[2] : js_mkundef(); 512 + 513 + algo_val = js_tostring_val(js, args[0]); 514 + if (is_err(algo_val)) return algo_val; 515 + 516 + algo = js_getstr(js, algo_val, &algo_len); 517 + if (!algo || algo_len == 0) return js_mkerr(js, "Invalid hash algorithm"); 518 + 519 + algo_name = strndup(algo, algo_len); 520 + if (!algo_name) return js_mkerr(js, "Out of memory"); 521 + 522 + md = EVP_get_digestbyname(algo_name); 523 + free(algo_name); 524 + if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported hash algorithm"); 525 + 526 + err = crypto_get_input_bytes(js, args[1], js_mkundef(), &bytes, &len, &owned); 527 + if (is_err(err)) goto cleanup; 528 + 529 + if (EVP_Digest(bytes, len, digest, &digest_len, md, NULL) != 1) { 530 + err = js_mkerr(js, "Hash failed"); 531 + goto cleanup; 532 + } 533 + 534 + err = crypto_digest_result(js, digest, digest_len, output_encoding); 535 + 536 + cleanup: 537 + if (owned) free(owned); 538 + return err; 539 + } 540 + 170 541 static ant_value_t create_crypto_obj(ant_t *js) { 171 542 ant_value_t crypto_obj = js_mkobj(js); 172 543 173 544 js_set(js, crypto_obj, "random", js_mkfun(js_crypto_random)); 174 545 js_set(js, crypto_obj, "randomBytes", js_mkfun(js_crypto_random_bytes)); 546 + js_set(js, crypto_obj, "randomFillSync", js_mkfun(js_crypto_random_fill_sync)); 175 547 js_set(js, crypto_obj, "randomUUID", js_mkfun(js_crypto_random_uuid)); 176 548 js_set(js, crypto_obj, "randomUUIDv7", js_mkfun(js_crypto_random_uuidv7)); 177 549 js_set(js, crypto_obj, "getRandomValues", js_mkfun(js_crypto_get_random_values)); ··· 191 563 ant_value_t webcrypto = create_crypto_obj(js); 192 564 193 565 js_set(js, lib, "webcrypto", webcrypto); 566 + js_set(js, lib, "hash", js_mkfun(js_crypto_hash)); 567 + js_set(js, lib, "createHash", js_mkfun(js_crypto_create_hash)); 194 568 js_set(js, lib, "randomBytes", js_mkfun(js_crypto_random_bytes)); 569 + js_set(js, lib, "randomFillSync", js_mkfun(js_crypto_random_fill_sync)); 195 570 js_set(js, lib, "randomUUID", js_mkfun(js_crypto_random_uuid)); 196 571 js_set(js, lib, "getRandomValues", js_mkfun(js_crypto_get_random_values)); 197 572 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "crypto", 6));
+31 -13
src/modules/events.c
··· 542 542 static bool eventemitter_add_listener_impl( 543 543 ant_t *js, 544 544 ant_value_t target, const char *event_type, 545 - ant_value_t listener, bool once 545 + ant_value_t listener, bool once, bool prepend 546 546 ) { 547 547 EventType *evt = NULL; 548 548 EventListenerEntry entry = {0}; ··· 559 559 entry.signal = js_mkundef(); 560 560 entry.once = once; 561 561 entry.capture = false; 562 - utarray_push_back(evt->listeners, &entry); 562 + 563 + if (!prepend || utarray_len(evt->listeners) == 0) utarray_push_back(evt->listeners, &entry); 564 + else { 565 + utarray_push_back(evt->listeners, &entry); 566 + EventListenerEntry *items = (EventListenerEntry *)utarray_eltptr(evt->listeners, 0); 567 + if (!items) return false; 568 + memmove(items + 1, items, (utarray_len(evt->listeners) - 1u) * sizeof(*items)); 569 + items[0] = entry; 570 + } 563 571 564 572 return true; 565 573 } ··· 634 642 ant_value_t target, const char *event_type, 635 643 ant_value_t listener, bool once 636 644 ) { 637 - return eventemitter_add_listener_impl(js, target, event_type, listener, once); 645 + return eventemitter_add_listener_impl(js, target, event_type, listener, once, false); 638 646 } 639 647 640 - static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once) { 648 + static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once, bool prepend) { 641 649 const char *event_type = NULL; 642 650 643 651 if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)"); 644 652 event_type = js_getstr(js, args[0], NULL); 645 653 646 654 if (!event_type) return js_mkerr(js, "event must be a string"); 647 - if (!eventemitter_add_listener_impl(js, js_getthis(js), event_type, args[1], once)) 655 + if (!eventemitter_add_listener_impl(js, js_getthis(js), event_type, args[1], once, prepend)) 648 656 return js_mkerr(js, "listener must be a function"); 649 657 650 658 return js_getthis(js); 651 659 } 652 660 653 661 static ant_value_t js_eventemitter_on(ant_t *js, ant_value_t *args, int nargs) { 654 - return js_eventemitter_add(js, args, nargs, false); 662 + return js_eventemitter_add(js, args, nargs, false, false); 655 663 } 656 664 657 665 static ant_value_t js_eventemitter_once(ant_t *js, ant_value_t *args, int nargs) { 658 - return js_eventemitter_add(js, args, nargs, true); 666 + return js_eventemitter_add(js, args, nargs, true, false); 667 + } 668 + 669 + static ant_value_t js_eventemitter_prepend_listener(ant_t *js, ant_value_t *args, int nargs) { 670 + return js_eventemitter_add(js, args, nargs, false, true); 671 + } 672 + 673 + static ant_value_t js_eventemitter_prepend_once_listener(ant_t *js, ant_value_t *args, int nargs) { 674 + return js_eventemitter_add(js, args, nargs, true, true); 659 675 } 660 676 661 677 static ant_value_t js_eventemitter_removeAllListeners(ant_t *js, ant_value_t *args, int nargs) { ··· 680 696 static ant_value_t js_eventemitter_eventNames(ant_t *js, ant_value_t *args, int nargs) { 681 697 ant_value_t this_obj = js_getthis(js); 682 698 ant_value_t result = js_mkarr(js); 699 + 683 700 EventType **events = get_or_create_emitter_events(js, this_obj); 684 701 if (events && *events) { 685 702 EventType *evt, *tmp; 686 - HASH_ITER(hh, *events, evt, tmp) { 687 - if (utarray_len(evt->listeners) > 0) 688 - js_arr_push(js, result, js_mkstr(js, evt->event_type, strlen(evt->event_type))); 689 - } 703 + HASH_ITER(hh, *events, evt, tmp) if (utarray_len(evt->listeners) > 0) js_arr_push( 704 + js, result, js_mkstr(js, evt->event_type, strlen(evt->event_type)) 705 + ); 690 706 } 707 + 691 708 return result; 692 709 } 693 710 ··· 699 716 js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on)); 700 717 js_set(js, eventemitter_proto, "addListener", js_mkfun(js_eventemitter_on)); 701 718 js_set(js, eventemitter_proto, "once", js_mkfun(js_eventemitter_once)); 719 + js_set(js, eventemitter_proto, "prependListener", js_mkfun(js_eventemitter_prepend_listener)); 720 + js_set(js, eventemitter_proto, "prependOnceListener", js_mkfun(js_eventemitter_prepend_once_listener)); 702 721 js_set(js, eventemitter_proto, "off", js_mkfun(js_eventemitter_off)); 703 722 js_set(js, eventemitter_proto, "removeListener", js_mkfun(js_eventemitter_off)); 704 723 js_set(js, eventemitter_proto, "emit", js_mkfun(js_eventemitter_emit)); ··· 712 731 js_set_descriptor(js, eventemitter_ctor, "name", 4, 0); 713 732 714 733 ant_value_t ctor_fn = js_obj_to_func_ex(eventemitter_ctor, SV_CALL_IS_DEFAULT_CTOR); 715 - js_set(js, lib, "EventEmitter", ctor_fn); 716 - js_set(js, lib, "default", ctor_fn); 734 + js_set_module_default(js, lib, ctor_fn, "EventEmitter"); 717 735 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "events", 6)); 718 736 719 737 return lib;
+1142 -162
src/modules/fs.c
··· 4 4 #include <stdio.h> 5 5 #include <stdlib.h> 6 6 #include <string.h> 7 + #include <stdbool.h> 7 8 #include <sys/stat.h> 8 9 #include <fcntl.h> 9 10 #include <uthash.h> ··· 11 12 #include <errno.h> 12 13 13 14 #include "ant.h" 15 + #include "ptr.h" 14 16 #include "utf8.h" 15 17 #include "utils.h" 16 18 #include "base64.h" 17 19 #include "errors.h" 20 + #include "watch.h" 18 21 #include "internal.h" 19 22 #include "runtime.h" 20 23 #include "descriptors.h" 21 24 25 + #include "gc/roots.h" 22 26 #include "gc/modules.h" 27 + #include "silver/engine.h" 23 28 24 29 #include "modules/fs.h" 25 30 #include "modules/buffer.h" 31 + #include "modules/events.h" 26 32 #include "modules/symbol.h" 27 33 28 34 #define fs_err_code(js, code, op, path) ({ \ ··· 42 48 FS_ENC_ASCII, 43 49 } fs_encoding_t; 44 50 51 + typedef enum { 52 + FS_OP_READ, 53 + FS_OP_WRITE, 54 + FS_OP_UNLINK, 55 + FS_OP_MKDIR, 56 + FS_OP_RMDIR, 57 + FS_OP_STAT, 58 + FS_OP_READ_BYTES, 59 + FS_OP_EXISTS, 60 + FS_OP_READDIR, 61 + FS_OP_ACCESS, 62 + FS_OP_REALPATH, 63 + FS_OP_WRITE_FD, 64 + FS_OP_OPEN, 65 + FS_OP_CLOSE 66 + } fs_op_type_t; 67 + 68 + typedef struct fs_request_s { 69 + uv_fs_t uv_req; 70 + ant_t *js; 71 + ant_value_t promise; 72 + 73 + char *path; 74 + char *data; 75 + char *error_msg; 76 + size_t data_len; 77 + 78 + fs_op_type_t op_type; 79 + fs_encoding_t encoding; 80 + uv_file fd; 81 + 82 + int completed; 83 + int failed; 84 + int recursive; 85 + int error_code; 86 + } fs_request_t; 87 + 88 + typedef enum { 89 + FS_WATCH_MODE_EVENT = 0, 90 + FS_WATCH_MODE_STAT 91 + } fs_watch_mode_t; 92 + 93 + typedef union { 94 + uv_fs_event_t event; 95 + uv_fs_poll_t poll; 96 + } fs_watcher_handle_t; 97 + 98 + typedef struct fs_watcher_s { 99 + ant_t *js; 100 + ant_value_t obj; 101 + ant_value_t callback; 102 + fs_watcher_handle_t handle; 103 + uv_stat_t last_stat; 104 + 105 + struct fs_watcher_s *next_active; 106 + char *path; 107 + 108 + bool last_stat_valid; 109 + bool in_active_list; 110 + bool closing; 111 + bool handle_closed; 112 + bool finalized; 113 + bool persistent; 114 + 115 + fs_watch_mode_t mode; 116 + } fs_watcher_t; 117 + 118 + typedef struct { 119 + bool persistent; 120 + bool recursive; 121 + ant_value_t listener; 122 + } fs_watch_options_t; 123 + 124 + typedef struct { 125 + bool persistent; 126 + unsigned int interval_ms; 127 + ant_value_t listener; 128 + } fs_watchfile_options_t; 129 + 130 + static ant_value_t g_fswatcher_proto = 0; 131 + static ant_value_t g_fswatcher_ctor = 0; 132 + 133 + static fs_watcher_t *active_watchers = NULL; 134 + static UT_array *pending_requests = NULL; 135 + 136 + enum { FS_WATCHER_NATIVE_TAG = 0x46535754u }; // FSWT 137 + 138 + static fs_watcher_t *fs_watcher_data(ant_value_t value) { 139 + if (!js_check_native_tag(value, FS_WATCHER_NATIVE_TAG)) return NULL; 140 + return (fs_watcher_t *)js_get_native_ptr(value); 141 + } 142 + 143 + static void fs_add_active_watcher(fs_watcher_t *watcher) { 144 + if (!watcher || watcher->in_active_list) return; 145 + watcher->next_active = active_watchers; 146 + active_watchers = watcher; 147 + watcher->in_active_list = true; 148 + } 149 + 150 + static void fs_remove_active_watcher(fs_watcher_t *watcher) { 151 + fs_watcher_t **it = NULL; 152 + if (!watcher || !watcher->in_active_list) return; 153 + 154 + for (it = &active_watchers; *it; it = &(*it)->next_active) { 155 + if (*it != watcher) continue; 156 + *it = watcher->next_active; 157 + watcher->next_active = NULL; 158 + watcher->in_active_list = false; 159 + return; 160 + } 161 + } 162 + 163 + static ant_value_t fs_call_value( 164 + ant_t *js, 165 + ant_value_t fn, 166 + ant_value_t this_val, 167 + ant_value_t *args, 168 + int nargs 169 + ) { 170 + ant_value_t saved_this = js->this_val; 171 + ant_value_t result = js_mkundef(); 172 + 173 + js->this_val = this_val; 174 + if (vtype(fn) == T_CFUNC) 175 + result = ((ant_value_t (*)(ant_t *, ant_value_t *, int))vdata(fn))(js, args, nargs); 176 + else result = sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, false); 177 + js->this_val = saved_this; 178 + 179 + return result; 180 + } 181 + 182 + static ant_value_t fs_stats_object_new( 183 + ant_t *js, 184 + mode_t mode, 185 + double size, 186 + double uid, 187 + double gid 188 + ) { 189 + ant_value_t stat_obj = js_mkobj(js); 190 + ant_value_t proto = js_get_ctor_proto(js, "Stats", 5); 191 + 192 + if (is_object_type(proto) || is_special_object(proto)) 193 + js_set_proto_init(stat_obj, proto); 194 + 195 + js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)mode)); 196 + js_set(js, stat_obj, "size", js_mknum(size)); 197 + js_set(js, stat_obj, "mode", js_mknum((double)mode)); 198 + js_set(js, stat_obj, "uid", js_mknum(uid)); 199 + js_set(js, stat_obj, "gid", js_mknum(gid)); 200 + 201 + return stat_obj; 202 + } 203 + 204 + static ant_value_t fs_stats_object_from_uv(ant_t *js, const uv_stat_t *st) { 205 + if (!st) return fs_stats_object_new(js, 0, 0.0, 0.0, 0.0); 206 + return fs_stats_object_new( 207 + js, (mode_t)st->st_mode, 208 + (double)st->st_size, (double)st->st_uid, (double)st->st_gid 209 + ); 210 + } 211 + 212 + static ant_value_t fs_stats_object_from_posix(ant_t *js, const struct stat *st) { 213 + if (!st) return fs_stats_object_new(js, 0, 0.0, 0.0, 0.0); 214 + return fs_stats_object_new( 215 + js, st->st_mode, 216 + (double)st->st_size, (double)st->st_uid, (double)st->st_gid 217 + ); 218 + } 219 + 220 + static bool fs_stat_path_sync(const char *path, uv_stat_t *out) { 221 + uv_fs_t req; 222 + int rc = 0; 223 + 224 + if (!path || !out) return false; 225 + memset(out, 0, sizeof(*out)); 226 + 227 + rc = uv_fs_stat(NULL, &req, path, NULL); 228 + if (rc < 0) { 229 + uv_fs_req_cleanup(&req); 230 + return false; 231 + } 232 + 233 + *out = req.statbuf; 234 + uv_fs_req_cleanup(&req); 235 + return true; 236 + } 237 + 238 + static const char *fs_watch_basename(const char *path) { 239 + const char *name = NULL; 240 + 241 + if (!path || !*path) return NULL; 242 + name = strrchr(path, '/'); 243 + #ifdef _WIN32 244 + { 245 + const char *alt = strrchr(path, '\\'); 246 + if (!name || (alt && alt > name)) name = alt; 247 + } 248 + #endif 249 + return name ? name + 1 : path; 250 + } 251 + 252 + static ant_value_t fs_watch_error(ant_t *js, int status, const char *path) { 253 + ant_value_t props = js_mkobj(js); 254 + const char *code = uv_err_name(status); 255 + js_set(js, props, "code", js_mkstr(js, code, strlen(code))); 256 + 257 + return js_mkerr_props( 258 + js, JS_ERR_TYPE, 259 + props, "%s: %s", 260 + path ? path : "watch", 261 + uv_strerror(status) 262 + ); 263 + } 264 + 265 + static void fs_watcher_free(fs_watcher_t *watcher) { 266 + if (!watcher) return; 267 + free(watcher->path); 268 + free(watcher); 269 + } 270 + 271 + static uv_handle_t *fs_watcher_uv_handle(fs_watcher_t *watcher) { 272 + if (!watcher) return NULL; 273 + if (watcher->mode == FS_WATCH_MODE_STAT) return (uv_handle_t *)&watcher->handle.poll; 274 + return (uv_handle_t *)&watcher->handle.event; 275 + } 276 + 277 + static void fs_watcher_on_handle_closed(uv_handle_t *handle) { 278 + fs_watcher_t *watcher = (fs_watcher_t *)handle->data; 279 + 280 + if (!watcher) return; 281 + watcher->handle_closed = true; 282 + watcher->closing = false; 283 + if (watcher->finalized) fs_watcher_free(watcher); 284 + } 285 + 286 + static void fs_watcher_close_native(fs_watcher_t *watcher) { 287 + uv_handle_t *handle = NULL; 288 + 289 + if (!watcher) return; 290 + if (watcher->closing || watcher->handle_closed) return; 291 + 292 + handle = fs_watcher_uv_handle(watcher); 293 + if (!handle) return; 294 + 295 + fs_remove_active_watcher(watcher); 296 + if (watcher->mode == FS_WATCH_MODE_STAT) uv_fs_poll_stop(&watcher->handle.poll); 297 + else ant_watch_stop(&watcher->handle.event); 298 + watcher->closing = true; 299 + uv_close(handle, fs_watcher_on_handle_closed); 300 + } 301 + 302 + static void fs_watcher_emit_error(fs_watcher_t *watcher, int status) { 303 + ant_value_t args[1]; 304 + 305 + if (!watcher || vtype(watcher->obj) != T_OBJ) return; 306 + args[0] = fs_watch_error(watcher->js, status, watcher->path); 307 + eventemitter_emit_args(watcher->js, watcher->obj, "error", args, 1); 308 + } 309 + 310 + static void fs_watcher_emit_change(fs_watcher_t *watcher, const char *filename, int events) { 311 + ant_value_t args[2]; 312 + const char *event_name = "change"; 313 + const char *name = filename; 314 + 315 + if (!watcher || vtype(watcher->obj) != T_OBJ) return; 316 + if ((events & UV_RENAME) != 0) event_name = "rename"; 317 + if (!name || !*name) name = fs_watch_basename(watcher->path); 318 + 319 + args[0] = js_mkstr(watcher->js, event_name, strlen(event_name)); 320 + args[1] = name ? js_mkstr(watcher->js, name, strlen(name)) : js_mkundef(); 321 + eventemitter_emit_args(watcher->js, watcher->obj, "change", args, 2); 322 + } 323 + 324 + static void fs_watcher_invoke_watchfile_stats( 325 + fs_watcher_t *watcher, 326 + const uv_stat_t *curr, 327 + const uv_stat_t *prev 328 + ) { 329 + uv_stat_t curr_stat; 330 + uv_stat_t prev_stat; 331 + ant_value_t args[2]; 332 + 333 + if (!watcher || !is_callable(watcher->callback)) return; 334 + 335 + memset(&curr_stat, 0, sizeof(curr_stat)); 336 + memset(&prev_stat, 0, sizeof(prev_stat)); 337 + if (curr) curr_stat = *curr; 338 + if (prev) prev_stat = *prev; 339 + 340 + watcher->last_stat = curr_stat; 341 + watcher->last_stat_valid = curr != NULL; 342 + 343 + args[0] = fs_stats_object_from_uv(watcher->js, &curr_stat); 344 + args[1] = fs_stats_object_from_uv(watcher->js, &prev_stat); 345 + fs_call_value(watcher->js, watcher->callback, js_mkundef(), args, 2); 346 + } 347 + 348 + static void fs_watcher_on_event( 349 + uv_fs_event_t *handle, 350 + const char *filename, 351 + int events, 352 + int status 353 + ) { 354 + fs_watcher_t *watcher = (fs_watcher_t *)handle->data; 355 + 356 + if (!watcher) return; 357 + if (status < 0) { 358 + fs_watcher_emit_error(watcher, status); 359 + return; 360 + } 361 + 362 + if ((events & (UV_CHANGE | UV_RENAME)) == 0) return; 363 + fs_watcher_emit_change(watcher, filename, events); 364 + } 365 + 366 + static void fs_watcher_on_poll( 367 + uv_fs_poll_t *handle, 368 + int status, 369 + const uv_stat_t *prev, 370 + const uv_stat_t *curr 371 + ) { 372 + fs_watcher_t *watcher = (fs_watcher_t *)handle->data; 373 + uv_stat_t missing = {0}; 374 + 375 + if (!watcher) return; 376 + if (status < 0 && status != UV_ENOENT) return; 377 + if (status == UV_ENOENT) { 378 + if (watcher->last_stat_valid) fs_watcher_invoke_watchfile_stats(watcher, &missing, &watcher->last_stat); 379 + else fs_watcher_invoke_watchfile_stats(watcher, &missing, &missing); 380 + return; 381 + } 382 + 383 + fs_watcher_invoke_watchfile_stats(watcher, curr, prev); 384 + } 385 + 386 + static ant_value_t js_fswatcher_close(ant_t *js, ant_value_t *args, int nargs) { 387 + fs_watcher_t *watcher = fs_watcher_data(js->this_val); 388 + 389 + if (!watcher) return js->this_val; 390 + fs_watcher_close_native(watcher); 391 + return js->this_val; 392 + } 393 + 394 + static ant_value_t js_fswatcher_ref(ant_t *js, ant_value_t *args, int nargs) { 395 + fs_watcher_t *watcher = fs_watcher_data(js->this_val); 396 + uv_handle_t *handle = NULL; 397 + 398 + if (!watcher || watcher->handle_closed) return js->this_val; 399 + handle = fs_watcher_uv_handle(watcher); 400 + if (handle) uv_ref(handle); 401 + watcher->persistent = true; 402 + return js->this_val; 403 + } 404 + 405 + static ant_value_t js_fswatcher_unref(ant_t *js, ant_value_t *args, int nargs) { 406 + fs_watcher_t *watcher = fs_watcher_data(js->this_val); 407 + uv_handle_t *handle = NULL; 408 + 409 + if (!watcher || watcher->handle_closed) return js->this_val; 410 + handle = fs_watcher_uv_handle(watcher); 411 + if (handle) uv_unref(handle); 412 + watcher->persistent = false; 413 + return js->this_val; 414 + } 415 + 416 + static ant_value_t js_fswatcher_ctor(ant_t *js, ant_value_t *args, int nargs) { 417 + return js_mkerr_typed(js, JS_ERR_TYPE, "FSWatcher cannot be constructed directly"); 418 + } 419 + 420 + static void fs_watcher_finalize(ant_t *js, ant_object_t *obj) { 421 + fs_watcher_t *watcher = NULL; 422 + if (!obj) return; 423 + 424 + watcher = (fs_watcher_t *)obj->native.ptr; 425 + obj->native.ptr = NULL; 426 + if (!watcher) return; 427 + 428 + watcher->finalized = true; 429 + fs_watcher_close_native(watcher); 430 + if (watcher->handle_closed) fs_watcher_free(watcher); 431 + } 432 + 433 + static void fs_init_watch_constructors(ant_t *js) { 434 + ant_value_t events = 0; 435 + ant_value_t ee_ctor = 0; 436 + ant_value_t ee_proto = 0; 437 + 438 + if (g_fswatcher_proto && g_fswatcher_ctor) return; 439 + 440 + events = events_library(js); 441 + ee_ctor = js_get(js, events, "EventEmitter"); 442 + ee_proto = js_get(js, ee_ctor, "prototype"); 443 + 444 + g_fswatcher_proto = js_mkobj(js); 445 + js_set_proto_init(g_fswatcher_proto, ee_proto); 446 + 447 + js_set(js, g_fswatcher_proto, "close", js_mkfun(js_fswatcher_close)); 448 + js_set(js, g_fswatcher_proto, "ref", js_mkfun(js_fswatcher_ref)); 449 + js_set(js, g_fswatcher_proto, "unref", js_mkfun(js_fswatcher_unref)); 450 + 451 + js_set_sym(js, g_fswatcher_proto, get_toStringTag_sym(), js_mkstr(js, "FSWatcher", 9)); 452 + g_fswatcher_ctor = js_make_ctor(js, js_fswatcher_ctor, g_fswatcher_proto, "FSWatcher", 9); 453 + } 454 + 455 + static bool fs_parse_watch_options(ant_t *js, ant_value_t *args, int nargs, fs_watch_options_t *out) { 456 + ant_value_t options = js_mkundef(); 457 + ant_value_t persistent_val = js_mkundef(); 458 + 459 + if (!out) return false; 460 + 461 + memset(out, 0, sizeof(*out)); 462 + out->persistent = true; 463 + out->listener = js_mkundef(); 464 + 465 + if (nargs > 1) { 466 + if (is_callable(args[1])) out->listener = args[1]; 467 + else options = args[1]; 468 + } 469 + 470 + if (nargs > 2 && is_callable(args[2])) out->listener = args[2]; 471 + if (vtype(options) == T_UNDEF || vtype(options) == T_NULL || vtype(options) == T_STR) return true; 472 + if (vtype(options) != T_OBJ) return false; 473 + 474 + persistent_val = js_get(js, options, "persistent"); 475 + if (vtype(persistent_val) != T_UNDEF) out->persistent = js_truthy(js, persistent_val); 476 + out->recursive = js_truthy(js, js_get(js, options, "recursive")); 477 + return true; 478 + } 479 + 480 + static bool fs_parse_watchfile_options( 481 + ant_t *js, 482 + ant_value_t *args, 483 + int nargs, 484 + fs_watchfile_options_t *out 485 + ) { 486 + ant_value_t options = js_mkundef(); 487 + ant_value_t persistent_val = js_mkundef(); 488 + 489 + if (!out) return false; 490 + 491 + memset(out, 0, sizeof(*out)); 492 + out->persistent = true; 493 + out->interval_ms = 5007; 494 + out->listener = js_mkundef(); 495 + 496 + if (nargs > 1) { 497 + if (is_callable(args[1])) { 498 + out->listener = args[1]; 499 + return true; 500 + } 501 + options = args[1]; 502 + } 503 + 504 + if (nargs > 2 && is_callable(args[2])) out->listener = args[2]; 505 + if (!is_callable(out->listener)) return false; 506 + if (vtype(options) == T_UNDEF || vtype(options) == T_NULL) return true; 507 + if (vtype(options) != T_OBJ) return false; 508 + 509 + persistent_val = js_get(js, options, "persistent"); 510 + if (vtype(persistent_val) != T_UNDEF) out->persistent = js_truthy(js, persistent_val); 511 + { 512 + ant_value_t interval_val = js_get(js, options, "interval"); 513 + if (vtype(interval_val) == T_NUM && js_getnum(interval_val) > 0) 514 + out->interval_ms = (unsigned int)js_getnum(interval_val); 515 + } 516 + return true; 517 + } 518 + 519 + static fs_watcher_t *fs_watcher_new(ant_t *js, fs_watch_mode_t mode) { 520 + fs_watcher_t *watcher = calloc(1, sizeof(*watcher)); 521 + 522 + if (!watcher) return NULL; 523 + watcher->js = js; 524 + watcher->obj = js_mkundef(); 525 + watcher->callback = js_mkundef(); 526 + watcher->persistent = true; 527 + watcher->mode = mode; 528 + 529 + return watcher; 530 + } 531 + 532 + static int fs_watcher_start_event(fs_watcher_t *watcher, const char *path, bool persistent, bool recursive) { 533 + unsigned int flags = 0; 534 + 535 + if (!watcher || !path) return UV_EINVAL; 536 + #ifdef UV_FS_EVENT_RECURSIVE 537 + if (recursive) flags |= UV_FS_EVENT_RECURSIVE; 538 + #else 539 + (void)recursive; 540 + #endif 541 + 542 + watcher->persistent = persistent; 543 + return ant_watch_start( 544 + uv_default_loop(), 545 + &watcher->handle.event, 546 + path, fs_watcher_on_event, 547 + watcher, flags, &watcher->path 548 + ); 549 + } 550 + 551 + static int fs_watcher_start_poll(fs_watcher_t *watcher, const char *path, bool persistent, unsigned int interval_ms) { 552 + int rc = 0; 553 + 554 + if (!watcher || !path) return UV_EINVAL; 555 + 556 + watcher->path = ant_watch_resolve_path(path); 557 + if (!watcher->path) return UV_ENOMEM; 558 + 559 + rc = uv_fs_poll_init(uv_default_loop(), &watcher->handle.poll); 560 + if (rc != 0) goto fail; 561 + 562 + watcher->handle.poll.data = watcher; 563 + rc = uv_fs_poll_start(&watcher->handle.poll, fs_watcher_on_poll, watcher->path, interval_ms); 564 + if (rc != 0) goto fail; 565 + 566 + watcher->persistent = persistent; 567 + return 0; 568 + 569 + fail: 570 + free(watcher->path); 571 + watcher->path = NULL; 572 + return rc; 573 + } 574 + 575 + static ant_value_t fs_watcher_make_object(ant_t *js, fs_watcher_t *watcher) { 576 + ant_value_t obj = 0; 577 + 578 + if (!watcher) return js_mkerr(js, "Out of memory"); 579 + fs_init_watch_constructors(js); 580 + 581 + obj = js_mkobj(js); 582 + js_set_proto_init(obj, g_fswatcher_proto); 583 + js_set_native_ptr(obj, watcher); 584 + js_set_native_tag(obj, FS_WATCHER_NATIVE_TAG); 585 + js_set_finalizer(obj, fs_watcher_finalize); 586 + watcher->obj = obj; 587 + 588 + return obj; 589 + } 590 + 591 + static void fs_request_fail(fs_request_t *req, int uv_code) { 592 + req->failed = 1; 593 + req->error_code = uv_code; 594 + if (req->error_msg) free(req->error_msg); 595 + req->error_msg = strdup(uv_strerror(uv_code)); 596 + } 597 + 45 598 static ant_value_t fs_coerce_path(ant_t *js, ant_value_t arg) { 46 599 if (vtype(arg) == T_STR) return arg; 47 600 if (is_object_type(arg)) { ··· 51 604 return js_mkundef(); 52 605 } 53 606 607 + static int fs_remove_path_recursive(const char *path, bool recursive, bool force) { 608 + uv_fs_t req; 609 + 610 + int result = uv_fs_lstat(NULL, &req, path, NULL); 611 + if (result < 0) { 612 + uv_fs_req_cleanup(&req); 613 + return (force && result == UV_ENOENT) ? 0 : result; 614 + } 615 + 616 + uv_stat_t statbuf = req.statbuf; 617 + uv_fs_req_cleanup(&req); 618 + 619 + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { 620 + result = uv_fs_unlink(NULL, &req, path, NULL); 621 + uv_fs_req_cleanup(&req); 622 + return (force && result == UV_ENOENT) ? 0 : result; 623 + } 624 + 625 + if (!recursive) return UV_EISDIR; 626 + result = uv_fs_scandir(NULL, &req, path, 0, NULL); 627 + 628 + if (result < 0) { 629 + uv_fs_req_cleanup(&req); 630 + return (force && result == UV_ENOENT) ? 0 : result; 631 + } 632 + 633 + for (;;) { 634 + uv_dirent_t entry; 635 + result = uv_fs_scandir_next(&req, &entry); 636 + if (result == UV_EOF) break; 637 + if (result < 0) { 638 + uv_fs_req_cleanup(&req); 639 + return result; 640 + } 641 + 642 + size_t path_len = strlen(path); 643 + size_t name_len = strlen(entry.name); 644 + 645 + bool needs_sep = path_len > 0 && path[path_len - 1] != '/'; 646 + char *child = malloc(path_len + (needs_sep ? 1u : 0u) + name_len + 1u); 647 + if (!child) { 648 + uv_fs_req_cleanup(&req); 649 + return UV_ENOMEM; 650 + } 651 + 652 + memcpy(child, path, path_len); 653 + if (needs_sep) child[path_len++] = '/'; 654 + memcpy(child + path_len, entry.name, name_len + 1u); 655 + 656 + result = fs_remove_path_recursive(child, true, force); 657 + free(child); 658 + if (result < 0) { 659 + uv_fs_req_cleanup(&req); 660 + return result; 661 + } 662 + } 663 + 664 + uv_fs_req_cleanup(&req); 665 + result = uv_fs_rmdir(NULL, &req, path, NULL); 666 + uv_fs_req_cleanup(&req); 667 + return (force && result == UV_ENOENT) ? 0 : result; 668 + } 669 + 670 + static bool fs_parse_rm_options(ant_t *js, ant_value_t options, bool *recursive_out, bool *force_out) { 671 + if (recursive_out) *recursive_out = false; 672 + if (force_out) *force_out = false; 673 + 674 + if (vtype(options) == T_UNDEF || vtype(options) == T_NULL) return true; 675 + if (vtype(options) != T_OBJ) return false; 676 + 677 + if (recursive_out) *recursive_out = js_truthy(js, js_get(js, options, "recursive")); 678 + if (force_out) *force_out = js_truthy(js, js_get(js, options, "force")); 679 + return true; 680 + } 681 + 682 + static ant_value_t fs_rm_impl(ant_t *js, ant_value_t *args, int nargs, bool return_promise) { 683 + ant_value_t promise = 0; 684 + ant_value_t path_val; 685 + size_t path_len = 0; 686 + const char *path = NULL; 687 + bool recursive = false; 688 + bool force = false; 689 + int result; 690 + 691 + if (return_promise) promise = js_mkpromise(js); 692 + if (nargs < 1) { 693 + ant_value_t err = js_mkerr(js, "rm() requires a path argument"); 694 + if (!return_promise) return err; 695 + js_reject_promise(js, promise, err); 696 + return promise; 697 + } 698 + 699 + path_val = fs_coerce_path(js, args[0]); 700 + if (vtype(path_val) != T_STR) { 701 + ant_value_t err = js_mkerr(js, "rm() path must be a string"); 702 + if (!return_promise) return err; 703 + js_reject_promise(js, promise, err); 704 + return promise; 705 + } 706 + 707 + path = js_getstr(js, path_val, &path_len); 708 + if (!path) { 709 + ant_value_t err = js_mkerr(js, "Failed to get path string"); 710 + if (!return_promise) return err; 711 + js_reject_promise(js, promise, err); 712 + return promise; 713 + } 714 + 715 + if (!fs_parse_rm_options(js, nargs >= 2 ? args[1] : js_mkundef(), &recursive, &force)) { 716 + ant_value_t err = js_mkerr_typed(js, JS_ERR_TYPE, "rm() options must be an object"); 717 + if (!return_promise) return err; 718 + js_reject_promise(js, promise, err); 719 + return promise; 720 + } 721 + 722 + result = fs_remove_path_recursive(path, recursive, force); 723 + if (result < 0) { 724 + ant_value_t err = js_mkerr(js, "%s", uv_strerror(result)); 725 + if (!return_promise) return err; 726 + js_reject_promise(js, promise, err); 727 + return promise; 728 + } 729 + 730 + if (!return_promise) return js_mkundef(); 731 + js_resolve_promise(js, promise, js_mkundef()); 732 + return promise; 733 + } 734 + 54 735 static fs_encoding_t parse_encoding(ant_t *js, ant_value_t arg) { 55 736 size_t len; 56 737 const char *str = NULL; ··· 137 818 } 138 819 } 139 820 140 - typedef enum { 141 - FS_OP_READ, 142 - FS_OP_WRITE, 143 - FS_OP_UNLINK, 144 - FS_OP_MKDIR, 145 - FS_OP_RMDIR, 146 - FS_OP_STAT, 147 - FS_OP_READ_BYTES, 148 - FS_OP_EXISTS, 149 - FS_OP_READDIR, 150 - FS_OP_ACCESS, 151 - FS_OP_REALPATH, 152 - FS_OP_WRITE_FD, 153 - FS_OP_OPEN, 154 - FS_OP_CLOSE 155 - } fs_op_type_t; 156 - 157 - typedef struct fs_request_s { 158 - uv_fs_t uv_req; 159 - ant_t *js; 160 - ant_value_t promise; 161 - char *path; 162 - char *data; 163 - char *error_msg; 164 - size_t data_len; 165 - fs_op_type_t op_type; 166 - fs_encoding_t encoding; 167 - uv_file fd; 168 - int completed; 169 - int failed; 170 - int recursive; 171 - } fs_request_t; 172 - 173 - static UT_array *pending_requests = NULL; 174 - 175 821 static void free_fs_request(fs_request_t *req) { 176 822 if (!req) return; 177 - 823 + 178 824 if (req->path) free(req->path); 179 825 if (req->data) free(req->data); 180 826 if (req->error_msg) free(req->error_msg); ··· 204 850 205 851 static void complete_request(fs_request_t *req) { 206 852 if (req->failed) { 207 - const char *err_msg = req->error_msg ? req->error_msg : "Unknown error"; 208 - ant_value_t reject_value = js_mkerr(req->js, "%s", err_msg); 853 + const char *err_msg = req->error_msg 854 + ? req->error_msg 855 + : "Unknown error"; 856 + 857 + ant_value_t props = js_mkobj(req->js); 858 + ant_value_t reject_value; 859 + 860 + if (req->error_code) { 861 + const char *code = uv_err_name(req->error_code); 862 + js_set(req->js, props, "code", js_mkstr(req->js, code, strlen(code))); 863 + js_set(req->js, props, "errno", js_mknum((double)req->error_code)); 864 + if (req->path) js_set(req->js, props, "path", js_mkstr(req->js, req->path, strlen(req->path))); 865 + reject_value = js_mkerr_props(req->js, JS_ERR_TYPE, props, "%s", err_msg); 866 + } else reject_value = js_mkerr(req->js, "%s", err_msg); 867 + 209 868 if (is_err(reject_value)) { 210 869 reject_value = req->js->thrown_value; 211 - if (vtype(reject_value) == T_UNDEF) { 870 + if (vtype(reject_value) == T_UNDEF) 212 871 reject_value = js_mkstr(req->js, err_msg, strlen(err_msg)); 213 - } 214 872 req->js->thrown_exists = false; 215 873 req->js->thrown_value = js_mkundef(); 216 874 } ··· 236 894 fs_request_t *req = (fs_request_t *)uv_req->data; 237 895 238 896 if (uv_req->result < 0) { 239 - req->failed = 1; 240 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 897 + fs_request_fail(req, (int)uv_req->result); 241 898 req->completed = 1; 242 899 complete_request(req); 243 900 return; ··· 257 914 fs_request_t *req = (fs_request_t *)uv_req->data; 258 915 259 916 if (uv_req->result < 0) { 260 - req->failed = 1; 261 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 917 + fs_request_fail(req, (int)uv_req->result); 262 918 req->completed = 1; 263 919 complete_request(req); 264 920 return; ··· 271 927 int stat_result = uv_fs_fstat(uv_default_loop(), &stat_req, req->fd, NULL); 272 928 273 929 if (stat_result < 0) { 274 - req->failed = 1; 275 - req->error_msg = strdup(uv_strerror(stat_result)); 930 + fs_request_fail(req, stat_result); 276 931 req->completed = 1; 277 932 uv_fs_t close_req; 278 933 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); ··· 301 956 int read_result = uv_fs_read(uv_default_loop(), uv_req, req->fd, &buf, 1, 0, on_read_complete); 302 957 303 958 if (read_result < 0) { 304 - req->failed = 1; 305 - req->error_msg = strdup(uv_strerror(read_result)); 959 + fs_request_fail(req, read_result); 306 960 req->completed = 1; 307 961 uv_fs_t close_req; 308 962 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); ··· 316 970 fs_request_t *req = (fs_request_t *)uv_req->data; 317 971 318 972 if (uv_req->result < 0) { 319 - req->failed = 1; 320 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 973 + fs_request_fail(req, (int)uv_req->result); 321 974 } 322 975 323 976 uv_fs_t close_req; ··· 332 985 fs_request_t *req = (fs_request_t *)uv_req->data; 333 986 334 987 if (uv_req->result < 0) { 335 - req->failed = 1; 336 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 988 + fs_request_fail(req, (int)uv_req->result); 337 989 req->completed = 1; 338 990 complete_request(req); 339 991 return; ··· 346 998 int write_result = uv_fs_write(uv_default_loop(), uv_req, req->fd, &buf, 1, 0, on_write_complete); 347 999 348 1000 if (write_result < 0) { 349 - req->failed = 1; 350 - req->error_msg = strdup(uv_strerror(write_result)); 1001 + fs_request_fail(req, write_result); 351 1002 req->completed = 1; 352 1003 uv_fs_t close_req; 353 1004 uv_fs_close(uv_default_loop(), &close_req, req->fd, NULL); ··· 361 1012 fs_request_t *req = (fs_request_t *)uv_req->data; 362 1013 363 1014 if (uv_req->result < 0) { 364 - req->failed = 1; 365 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 1015 + fs_request_fail(req, (int)uv_req->result); 366 1016 } 367 1017 368 1018 uv_fs_req_cleanup(uv_req); ··· 375 1025 376 1026 if (uv_req->result < 0) { 377 1027 if (!req->recursive || uv_req->result != UV_EEXIST) { 378 - req->failed = 1; 379 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 1028 + fs_request_fail(req, (int)uv_req->result); 380 1029 }} 381 1030 382 1031 uv_fs_req_cleanup(uv_req); ··· 388 1037 fs_request_t *req = (fs_request_t *)uv_req->data; 389 1038 390 1039 if (uv_req->result < 0) { 391 - req->failed = 1; 392 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 1040 + fs_request_fail(req, (int)uv_req->result); 393 1041 } 394 1042 395 1043 uv_fs_req_cleanup(uv_req); ··· 401 1049 fs_request_t *req = (fs_request_t *)uv_req->data; 402 1050 403 1051 if (uv_req->result < 0) { 404 - req->failed = 1; 405 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 1052 + fs_request_fail(req, (int)uv_req->result); 406 1053 req->completed = 1; 407 1054 complete_request(req); 408 1055 return; 409 1056 } 410 1057 411 - ant_value_t stat_obj = js_mkobj(req->js); 412 - ant_value_t proto = js_get_ctor_proto(req->js, "Stats", 5); 413 - if (is_object_type(proto)) js_set_proto_init(stat_obj, proto); 414 - 415 - uv_stat_t *st = &uv_req->statbuf; 416 - js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)st->st_mode)); 417 - js_set(req->js, stat_obj, "size", js_mknum((double)st->st_size)); 418 - js_set(req->js, stat_obj, "mode", js_mknum((double)st->st_mode)); 419 - js_set(req->js, stat_obj, "uid", js_mknum((double)st->st_uid)); 420 - js_set(req->js, stat_obj, "gid", js_mknum((double)st->st_gid)); 1058 + ant_value_t stat_obj = fs_stats_object_from_uv(req->js, &uv_req->statbuf); 421 1059 422 1060 req->completed = 1; 423 1061 js_resolve_promise(req->js, req->promise, stat_obj); ··· 429 1067 fs_request_t *req = (fs_request_t *)uv_req->data; 430 1068 431 1069 if (uv_req->result < 0 || !uv_req->ptr) { 432 - req->failed = 1; 433 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 1070 + fs_request_fail(req, (int)uv_req->result); 434 1071 req->completed = 1; 435 1072 complete_request(req); 436 1073 return; ··· 464 1101 fs_request_t *req = (fs_request_t *)uv_req->data; 465 1102 466 1103 if (uv_req->result < 0) { 467 - req->failed = 1; 468 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 1104 + fs_request_fail(req, (int)uv_req->result); 469 1105 req->completed = 1; 470 1106 complete_request(req); 471 1107 return; ··· 481 1117 fs_request_t *req = (fs_request_t *)uv_req->data; 482 1118 483 1119 if (uv_req->result < 0) { 484 - req->failed = 1; 485 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 1120 + fs_request_fail(req, (int)uv_req->result); 486 1121 req->completed = 1; 487 1122 complete_request(req); 488 1123 return; ··· 631 1266 int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_RDONLY, 0, on_open_for_read); 632 1267 633 1268 if (result < 0) { 634 - req->failed = 1; 635 - req->error_msg = strdup(uv_strerror(result)); 1269 + fs_request_fail(req, result); 636 1270 req->completed = 1; 637 1271 complete_request(req); 638 1272 } ··· 663 1297 int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_RDONLY, 0, on_open_for_read); 664 1298 665 1299 if (result < 0) { 666 - req->failed = 1; 667 - req->error_msg = strdup(uv_strerror(result)); 1300 + fs_request_fail(req, result); 668 1301 req->completed = 1; 669 1302 complete_request(req); 670 1303 } ··· 863 1496 int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, O_WRONLY | O_CREAT | O_TRUNC, 0644, on_open_for_write); 864 1497 865 1498 if (result < 0) { 866 - req->failed = 1; 867 - req->error_msg = strdup(uv_strerror(result)); 1499 + fs_request_fail(req, result); 868 1500 req->completed = 1; 869 1501 complete_request(req); 870 1502 } ··· 919 1551 int result = uv_fs_unlink(uv_default_loop(), &req->uv_req, req->path, on_unlink_complete); 920 1552 921 1553 if (result < 0) { 922 - req->failed = 1; 923 - req->error_msg = strdup(uv_strerror(result)); 1554 + fs_request_fail(req, result); 924 1555 req->completed = 1; 925 1556 complete_request(req); 926 1557 } ··· 1024 1655 req->completed = 1; 1025 1656 complete_request(req); 1026 1657 } else { 1027 - req->failed = 1; 1028 - req->error_msg = strdup(uv_strerror(result)); 1658 + fs_request_fail(req, result); 1029 1659 req->completed = 1; 1030 1660 complete_request(req); 1031 1661 } ··· 1085 1715 int result = uv_fs_rmdir(uv_default_loop(), &req->uv_req, req->path, on_rmdir_complete); 1086 1716 1087 1717 if (result < 0) { 1088 - req->failed = 1; 1089 - req->error_msg = strdup(uv_strerror(result)); 1718 + fs_request_fail(req, result); 1090 1719 req->completed = 1; 1091 1720 complete_request(req); 1092 1721 } ··· 1094 1723 return req->promise; 1095 1724 } 1096 1725 1726 + static ant_value_t builtin_fs_rmSync(ant_t *js, ant_value_t *args, int nargs) { 1727 + return fs_rm_impl(js, args, nargs, false); 1728 + } 1729 + 1730 + static ant_value_t builtin_fs_rm(ant_t *js, ant_value_t *args, int nargs) { 1731 + return fs_rm_impl(js, args, nargs, true); 1732 + } 1733 + 1097 1734 static ant_value_t stat_isFile(ant_t *js, ant_value_t *args, int nargs) { 1098 1735 ant_value_t this = js_getthis(js); 1099 1736 ant_value_t mode_val = js_get_slot(this, SLOT_DATA); ··· 1125 1762 } 1126 1763 1127 1764 static ant_value_t create_stats_object(ant_t *js, struct stat *st) { 1128 - ant_value_t stat_obj = js_mkobj(js); 1129 - ant_value_t proto = js_get_ctor_proto(js, "Stats", 5); 1130 - if (is_special_object(proto)) js_set_proto_init(stat_obj, proto); 1131 - 1132 - js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)st->st_mode)); 1133 - js_set(js, stat_obj, "size", js_mknum((double)st->st_size)); 1134 - js_set(js, stat_obj, "mode", js_mknum((double)st->st_mode)); 1135 - js_set(js, stat_obj, "uid", js_mknum((double)st->st_uid)); 1136 - js_set(js, stat_obj, "gid", js_mknum((double)st->st_gid)); 1137 - 1138 - return stat_obj; 1765 + return fs_stats_object_from_posix(js, st); 1139 1766 } 1140 1767 1141 1768 static const char *errno_to_code(int err_num) { ··· 1161 1788 1162 1789 static ant_value_t builtin_fs_statSync(ant_t *js, ant_value_t *args, int nargs) { 1163 1790 if (nargs < 1) return js_mkerr(js, "statSync() requires a path argument"); 1164 - 1165 1791 if (vtype(args[0]) != T_STR) return js_mkerr(js, "statSync() path must be a string"); 1166 1792 1167 1793 size_t path_len; ··· 1186 1812 1187 1813 static ant_value_t builtin_fs_stat(ant_t *js, ant_value_t *args, int nargs) { 1188 1814 if (nargs < 1) return js_mkerr(js, "stat() requires a path argument"); 1189 - 1190 1815 if (vtype(args[0]) != T_STR) return js_mkerr(js, "stat() path must be a string"); 1191 1816 1192 1817 size_t path_len; ··· 1207 1832 int result = uv_fs_stat(uv_default_loop(), &req->uv_req, req->path, on_stat_complete); 1208 1833 1209 1834 if (result < 0) { 1210 - req->failed = 1; 1211 - req->error_msg = strdup(uv_strerror(result)); 1835 + fs_request_fail(req, result); 1836 + req->completed = 1; 1837 + complete_request(req); 1838 + } 1839 + 1840 + return req->promise; 1841 + } 1842 + 1843 + static ant_value_t builtin_fs_lstatSync(ant_t *js, ant_value_t *args, int nargs) { 1844 + if (nargs < 1) return js_mkerr(js, "lstatSync() requires a path argument"); 1845 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "lstatSync() path must be a string"); 1846 + 1847 + size_t path_len; 1848 + char *path = js_getstr(js, args[0], &path_len); 1849 + if (!path) return js_mkerr(js, "Failed to get path string"); 1850 + 1851 + char *path_cstr = strndup(path, path_len); 1852 + if (!path_cstr) return js_mkerr(js, "Out of memory"); 1853 + 1854 + struct stat st; 1855 + int result = lstat(path_cstr, &st); 1856 + 1857 + if (result != 0) { 1858 + const char *code = errno_to_code(errno); 1859 + ant_value_t err = fs_err_code(js, code, "lstat", path_cstr); 1860 + free(path_cstr); return err; 1861 + } 1862 + 1863 + free(path_cstr); 1864 + return create_stats_object(js, &st); 1865 + } 1866 + 1867 + static ant_value_t builtin_fs_lstat(ant_t *js, ant_value_t *args, int nargs) { 1868 + if (nargs < 1) return js_mkerr(js, "lstat() requires a path argument"); 1869 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "lstat() path must be a string"); 1870 + 1871 + size_t path_len; 1872 + char *path = js_getstr(js, args[0], &path_len); 1873 + if (!path) return js_mkerr(js, "Failed to get path string"); 1874 + 1875 + 1876 + fs_request_t *req = calloc(1, sizeof(fs_request_t)); 1877 + if (!req) return js_mkerr(js, "Out of memory"); 1878 + 1879 + req->js = js; 1880 + req->op_type = FS_OP_STAT; 1881 + req->promise = js_mkpromise(js); 1882 + req->path = strndup(path, path_len); 1883 + req->uv_req.data = req; 1884 + 1885 + utarray_push_back(pending_requests, &req); 1886 + int result = uv_fs_lstat(uv_default_loop(), &req->uv_req, req->path, on_stat_complete); 1887 + 1888 + if (result < 0) { 1889 + fs_request_fail(req, result); 1212 1890 req->completed = 1; 1213 1891 complete_request(req); 1214 1892 } ··· 1218 1896 1219 1897 static ant_value_t builtin_fs_existsSync(ant_t *js, ant_value_t *args, int nargs) { 1220 1898 if (nargs < 1) return js_mkerr(js, "existsSync() requires a path argument"); 1221 - 1222 1899 if (vtype(args[0]) != T_STR) return js_mkerr(js, "existsSync() path must be a string"); 1223 1900 1224 1901 size_t path_len; ··· 1237 1914 1238 1915 static ant_value_t builtin_fs_exists(ant_t *js, ant_value_t *args, int nargs) { 1239 1916 if (nargs < 1) return js_mkerr(js, "exists() requires a path argument"); 1240 - 1241 1917 if (vtype(args[0]) != T_STR) return js_mkerr(js, "exists() path must be a string"); 1242 1918 1243 1919 size_t path_len; ··· 1269 1945 1270 1946 static ant_value_t builtin_fs_accessSync(ant_t *js, ant_value_t *args, int nargs) { 1271 1947 if (nargs < 1) return js_mkerr(js, "accessSync() requires a path argument"); 1272 - 1273 1948 if (vtype(args[0]) != T_STR) return js_mkerr(js, "accessSync() path must be a string"); 1274 1949 1275 1950 size_t path_len; ··· 1297 1972 1298 1973 static ant_value_t builtin_fs_access(ant_t *js, ant_value_t *args, int nargs) { 1299 1974 if (nargs < 1) return js_mkerr(js, "access() requires a path argument"); 1300 - 1301 1975 if (vtype(args[0]) != T_STR) return js_mkerr(js, "access() path must be a string"); 1302 1976 1303 1977 size_t path_len; ··· 1323 1997 int result = uv_fs_access(uv_default_loop(), &req->uv_req, req->path, mode, on_access_complete); 1324 1998 1325 1999 if (result < 0) { 1326 - req->failed = 1; 1327 - req->error_msg = strdup(uv_strerror(result)); 2000 + fs_request_fail(req, result); 1328 2001 req->completed = 1; 1329 2002 complete_request(req); 1330 2003 } ··· 1378 2051 int result = uv_fs_realpath(uv_default_loop(), &req->uv_req, req->path, on_realpath_complete); 1379 2052 1380 2053 if (result < 0) { 1381 - req->failed = 1; 1382 - req->error_msg = strdup(uv_strerror(result)); 2054 + fs_request_fail(req, result); 1383 2055 req->completed = 1; 1384 2056 complete_request(req); 1385 2057 } ··· 1389 2061 1390 2062 static ant_value_t builtin_fs_readdirSync(ant_t *js, ant_value_t *args, int nargs) { 1391 2063 if (nargs < 1) return js_mkerr(js, "readdirSync() requires a path argument"); 1392 - 1393 2064 if (vtype(args[0]) != T_STR) return js_mkerr(js, "readdirSync() path must be a string"); 1394 2065 1395 2066 size_t path_len; ··· 1423 2094 1424 2095 static ant_value_t builtin_fs_readdir(ant_t *js, ant_value_t *args, int nargs) { 1425 2096 if (nargs < 1) return js_mkerr(js, "readdir() requires a path argument"); 1426 - 1427 2097 if (vtype(args[0]) != T_STR) return js_mkerr(js, "readdir() path must be a string"); 1428 2098 1429 2099 size_t path_len; ··· 1444 2114 int result = uv_fs_scandir(uv_default_loop(), &req->uv_req, req->path, 0, on_readdir_complete); 1445 2115 1446 2116 if (result < 0) { 1447 - req->failed = 1; 1448 - req->error_msg = strdup(uv_strerror(result)); 2117 + fs_request_fail(req, result); 1449 2118 req->completed = 1; 1450 2119 complete_request(req); 1451 2120 } ··· 1457 2126 fs_request_t *req = (fs_request_t *)uv_req->data; 1458 2127 1459 2128 if (uv_req->result < 0) { 1460 - req->failed = 1; 1461 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 2129 + fs_request_fail(req, (int)uv_req->result); 1462 2130 req->completed = 1; 1463 2131 complete_request(req); 1464 2132 return; ··· 1488 2156 int64_t position = -1; 1489 2157 1490 2158 if (nargs >= 3) { 1491 - if (vtype(args[2]) == T_OBJ) { 1492 - ant_value_t off_val = js_get(js, args[2], "offset"); 1493 - ant_value_t len_val = js_get(js, args[2], "length"); 1494 - ant_value_t pos_val = js_get(js, args[2], "position"); 1495 - if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); 1496 - if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); 1497 - else length = buf_len - offset; 1498 - if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); 1499 - } else if (vtype(args[2]) == T_NUM) { 1500 - offset = (size_t)js_getnum(args[2]); 1501 - length = buf_len - offset; 1502 - if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 1503 - if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 1504 - } 1505 - } 2159 + if (vtype(args[2]) == T_OBJ) { 2160 + ant_value_t off_val = js_get(js, args[2], "offset"); 2161 + ant_value_t len_val = js_get(js, args[2], "length"); 2162 + ant_value_t pos_val = js_get(js, args[2], "position"); 2163 + if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); 2164 + if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); 2165 + else length = buf_len - offset; 2166 + if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); 2167 + } else if (vtype(args[2]) == T_NUM) { 2168 + offset = (size_t)js_getnum(args[2]); 2169 + length = buf_len - offset; 2170 + if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 2171 + if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 2172 + }} 1506 2173 1507 2174 if (offset > buf_len) return js_mkerr(js, "offset is out of bounds"); 1508 2175 if (offset + length > buf_len) return js_mkerr(js, "length extends beyond buffer"); ··· 1611 2278 size_t length = buf_len; 1612 2279 1613 2280 if (nargs >= 3) { 1614 - if (vtype(args[2]) == T_OBJ) { 1615 - ant_value_t off_val = js_get(js, args[2], "offset"); 1616 - ant_value_t len_val = js_get(js, args[2], "length"); 1617 - ant_value_t pos_val = js_get(js, args[2], "position"); 1618 - if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); 1619 - if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); 1620 - else length = buf_len - offset; 1621 - if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); 1622 - } else if (vtype(args[2]) == T_NUM) { 1623 - offset = (size_t)js_getnum(args[2]); 1624 - length = buf_len - offset; 1625 - if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 1626 - if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 1627 - } 1628 - } 2281 + if (vtype(args[2]) == T_OBJ) { 2282 + ant_value_t off_val = js_get(js, args[2], "offset"); 2283 + ant_value_t len_val = js_get(js, args[2], "length"); 2284 + ant_value_t pos_val = js_get(js, args[2], "position"); 2285 + if (vtype(off_val) == T_NUM) offset = (size_t)js_getnum(off_val); 2286 + if (vtype(len_val) == T_NUM) length = (size_t)js_getnum(len_val); 2287 + else length = buf_len - offset; 2288 + if (vtype(pos_val) == T_NUM) position = (int64_t)js_getnum(pos_val); 2289 + } else if (vtype(args[2]) == T_NUM) { 2290 + offset = (size_t)js_getnum(args[2]); 2291 + length = buf_len - offset; 2292 + if (nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 2293 + if (nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 2294 + }} 1629 2295 1630 2296 if (offset > buf_len) return js_mkerr(js, "offset is out of bounds"); 1631 2297 if (offset + length > buf_len) return js_mkerr(js, "length extends beyond buffer"); ··· 1656 2322 int result = uv_fs_write(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_write_fd_complete); 1657 2323 1658 2324 if (result < 0) { 1659 - req->failed = 1; 1660 - req->error_msg = strdup(uv_strerror(result)); 2325 + fs_request_fail(req, result); 1661 2326 req->completed = 1; 1662 2327 complete_request(req); 1663 2328 } ··· 1758 2423 int result = uv_fs_write(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_write_fd_complete); 1759 2424 1760 2425 if (result < 0) { 1761 - req->failed = 1; 1762 - req->error_msg = strdup(uv_strerror(result)); 2426 + fs_request_fail(req, result); 1763 2427 req->completed = 1; 1764 2428 complete_request(req); 1765 2429 } ··· 1830 2494 fs_request_t *req = (fs_request_t *)uv_req->data; 1831 2495 1832 2496 if (uv_req->result < 0) { 1833 - req->failed = 1; 1834 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 2497 + fs_request_fail(req, (int)uv_req->result); 1835 2498 req->completed = 1; 1836 2499 complete_request(req); 1837 2500 return; ··· 1867 2530 int result = uv_fs_open(uv_default_loop(), &req->uv_req, req->path, flags, mode, on_open_fd_complete); 1868 2531 1869 2532 if (result < 0) { 1870 - req->failed = 1; 1871 - req->error_msg = strdup(uv_strerror(result)); 2533 + fs_request_fail(req, result); 1872 2534 req->completed = 1; 1873 2535 complete_request(req); 1874 2536 } ··· 1880 2542 fs_request_t *req = (fs_request_t *)uv_req->data; 1881 2543 1882 2544 if (uv_req->result < 0) { 1883 - req->failed = 1; 1884 - req->error_msg = strdup(uv_strerror((int)uv_req->result)); 2545 + fs_request_fail(req, (int)uv_req->result); 1885 2546 req->completed = 1; 1886 2547 complete_request(req); 1887 2548 return; ··· 1912 2573 int result = uv_fs_close(uv_default_loop(), &req->uv_req, req->fd, on_close_fd_complete); 1913 2574 1914 2575 if (result < 0) { 1915 - req->failed = 1; 1916 - req->error_msg = strdup(uv_strerror(result)); 2576 + fs_request_fail(req, result); 1917 2577 req->completed = 1; 1918 2578 complete_request(req); 1919 2579 } ··· 1921 2581 return req->promise; 1922 2582 } 1923 2583 2584 + static ant_value_t builtin_fs_watch(ant_t *js, ant_value_t *args, int nargs) { 2585 + ant_value_t path_val = 0; 2586 + ant_value_t watcher_obj = 0; 2587 + fs_watch_options_t opts; 2588 + fs_watcher_t *watcher = NULL; 2589 + uv_handle_t *handle = NULL; 2590 + const char *path = NULL; 2591 + size_t path_len = 0; 2592 + int rc = 0; 2593 + 2594 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "watch() requires a path argument"); 2595 + if (!fs_parse_watch_options(js, args, nargs, &opts)) 2596 + return js_mkerr_typed(js, JS_ERR_TYPE, "watch() options must be a string, object, or callback"); 2597 + 2598 + path_val = fs_coerce_path(js, args[0]); 2599 + if (vtype(path_val) != T_STR) return js_mkerr_typed(js, JS_ERR_TYPE, "watch() path must be a string or URL"); 2600 + 2601 + path = js_getstr(js, path_val, &path_len); 2602 + if (!path || path_len == 0) return js_mkerr(js, "watch() path must not be empty"); 2603 + 2604 + watcher = fs_watcher_new(js, FS_WATCH_MODE_EVENT); 2605 + if (!watcher) return js_mkerr(js, "Out of memory"); 2606 + 2607 + watcher_obj = fs_watcher_make_object(js, watcher); 2608 + if (is_err(watcher_obj)) { 2609 + fs_watcher_free(watcher); 2610 + return watcher_obj; 2611 + } 2612 + 2613 + rc = fs_watcher_start_event(watcher, path, opts.persistent, opts.recursive); 2614 + if (rc != 0) { 2615 + js_set_native_ptr(watcher_obj, NULL); 2616 + fs_watcher_free(watcher); 2617 + return fs_watch_error(js, rc, path); 2618 + } 2619 + 2620 + fs_add_active_watcher(watcher); 2621 + if (!opts.persistent) { 2622 + handle = fs_watcher_uv_handle(watcher); 2623 + if (handle) uv_unref(handle); 2624 + } 2625 + if (is_callable(opts.listener)) 2626 + eventemitter_add_listener(js, watcher_obj, "change", opts.listener, false); 2627 + return watcher_obj; 2628 + } 2629 + 2630 + static ant_value_t builtin_fs_watchFile(ant_t *js, ant_value_t *args, int nargs) { 2631 + ant_value_t path_val = 0; 2632 + fs_watchfile_options_t opts; 2633 + 2634 + fs_watcher_t *watcher = NULL; 2635 + uv_handle_t *handle = NULL; 2636 + 2637 + const char *path = NULL; 2638 + size_t path_len = 0; 2639 + int rc = 0; 2640 + 2641 + if (nargs < 2) 2642 + return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() requires a path and listener"); 2643 + if (!fs_parse_watchfile_options(js, args, nargs, &opts)) 2644 + return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() requires a listener callback"); 2645 + 2646 + path_val = fs_coerce_path(js, args[0]); 2647 + if (vtype(path_val) != T_STR) return js_mkerr_typed(js, JS_ERR_TYPE, "watchFile() path must be a string or URL"); 2648 + 2649 + path = js_getstr(js, path_val, &path_len); 2650 + if (!path || path_len == 0) return js_mkerr(js, "watchFile() path must not be empty"); 2651 + 2652 + watcher = fs_watcher_new(js, FS_WATCH_MODE_STAT); 2653 + if (!watcher) return js_mkerr(js, "Out of memory"); 2654 + 2655 + watcher->callback = opts.listener; 2656 + watcher->last_stat_valid = fs_stat_path_sync(path, &watcher->last_stat); 2657 + 2658 + rc = fs_watcher_start_poll(watcher, path, opts.persistent, opts.interval_ms); 2659 + if (rc != 0) { 2660 + fs_watcher_free(watcher); 2661 + return fs_watch_error(js, rc, path); 2662 + } 2663 + 2664 + fs_add_active_watcher(watcher); 2665 + if (!opts.persistent) { 2666 + handle = fs_watcher_uv_handle(watcher); 2667 + if (handle) uv_unref(handle); 2668 + } 2669 + return js_mkundef(); 2670 + } 2671 + 2672 + static ant_value_t builtin_fs_unwatchFile(ant_t *js, ant_value_t *args, int nargs) { 2673 + ant_value_t path_val = 0; 2674 + ant_value_t listener = js_mkundef(); 2675 + 2676 + char *resolved = NULL; 2677 + fs_watcher_t *watcher = NULL; 2678 + fs_watcher_t *next = NULL; 2679 + 2680 + const char *path = NULL; 2681 + size_t path_len = 0; 2682 + 2683 + if (nargs < 1) return js_mkundef(); 2684 + 2685 + path_val = fs_coerce_path(js, args[0]); 2686 + if (vtype(path_val) != T_STR) return js_mkundef(); 2687 + 2688 + path = js_getstr(js, path_val, &path_len); 2689 + if (!path || path_len == 0) return js_mkundef(); 2690 + if (nargs > 1 && is_callable(args[1])) listener = args[1]; 2691 + 2692 + resolved = ant_watch_resolve_path(path); 2693 + if (!resolved) return js_mkundef(); 2694 + 2695 + for (watcher = active_watchers; watcher; watcher = next) { 2696 + next = watcher->next_active; 2697 + if (watcher->mode != FS_WATCH_MODE_STAT) continue; 2698 + if (!watcher->path || strcmp(watcher->path, resolved) != 0) continue; 2699 + if (is_callable(listener) && watcher->callback != listener) continue; 2700 + fs_watcher_close_native(watcher); 2701 + } 2702 + 2703 + free(resolved); 2704 + return js_mkundef(); 2705 + } 2706 + 1924 2707 void init_fs_module(void) { 1925 2708 utarray_new(pending_requests, &ut_ptr_icd); 1926 2709 ··· 1942 2725 js_set(js, glob, "Stats", js_obj_to_func(stats_ctor)); 1943 2726 } 1944 2727 2728 + static ant_value_t fs_callback_success_handler(ant_t *js, ant_value_t *args, int nargs) { 2729 + ant_value_t ctx = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 2730 + ant_value_t callback = js_get_slot(ctx, SLOT_DATA); 2731 + 2732 + if (!is_callable(callback)) return js_mkundef(); 2733 + 2734 + if (js_truthy(js, js_get(js, ctx, "existsStyle"))) { 2735 + ant_value_t cb_args[1] = { nargs > 0 ? args[0] : js_false }; 2736 + fs_call_value(js, callback, js_mkundef(), cb_args, 1); 2737 + return js_mkundef(); 2738 + } 2739 + 2740 + ant_value_t cb_args[2] = { js_mknull(), nargs > 0 ? args[0] : js_mkundef() }; 2741 + fs_call_value(js, callback, js_mkundef(), cb_args, 2); 2742 + return js_mkundef(); 2743 + } 2744 + 2745 + static ant_value_t fs_callback_error_handler(ant_t *js, ant_value_t *args, int nargs) { 2746 + ant_value_t ctx = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 2747 + ant_value_t callback = js_get_slot(ctx, SLOT_DATA); 2748 + ant_value_t cb_args[1]; 2749 + 2750 + if (!is_callable(callback)) return js_mkundef(); 2751 + 2752 + if (js_truthy(js, js_get(js, ctx, "existsStyle"))) { 2753 + cb_args[0] = js_false; 2754 + fs_call_value(js, callback, js_mkundef(), cb_args, 1); 2755 + return js_mkundef(); 2756 + } 2757 + 2758 + cb_args[0] = nargs > 0 ? args[0] : js_mkundef(); 2759 + fs_call_value(js, callback, js_mkundef(), cb_args, 1); 2760 + return js_mkundef(); 2761 + } 2762 + 2763 + static void fs_callback_emit_success( 2764 + ant_t *js, 2765 + ant_value_t callback, 2766 + bool exists_style, 2767 + ant_value_t value 2768 + ) { 2769 + if (exists_style) { 2770 + ant_value_t cb_args[1] = { value }; 2771 + fs_call_value(js, callback, js_mkundef(), cb_args, 1); 2772 + return; 2773 + } 2774 + 2775 + ant_value_t cb_args[2] = { js_mknull(), value }; 2776 + fs_call_value(js, callback, js_mkundef(), cb_args, 2); 2777 + } 2778 + 2779 + static void fs_callback_emit_error( 2780 + ant_t *js, 2781 + ant_value_t callback, 2782 + bool exists_style, 2783 + ant_value_t error 2784 + ) { 2785 + ant_value_t cb_args[1]; 2786 + 2787 + cb_args[0] = exists_style ? js_false : error; 2788 + fs_call_value(js, callback, js_mkundef(), cb_args, 1); 2789 + } 2790 + 2791 + static ant_value_t fs_callback_attach_promise( 2792 + ant_t *js, 2793 + ant_value_t promise, 2794 + ant_value_t callback, 2795 + bool exists_style 2796 + ) { 2797 + GC_ROOT_SAVE(root_mark, js); 2798 + ant_value_t success_ctx = js_mkobj(js); 2799 + ant_value_t error_ctx = js_mkobj(js); 2800 + ant_value_t success_fn = js_mkundef(); 2801 + ant_value_t error_fn = js_mkundef(); 2802 + 2803 + GC_ROOT_PIN(js, promise); 2804 + GC_ROOT_PIN(js, callback); 2805 + GC_ROOT_PIN(js, success_ctx); 2806 + GC_ROOT_PIN(js, error_ctx); 2807 + 2808 + js_set_slot(success_ctx, SLOT_DATA, callback); 2809 + js_set_slot(error_ctx, SLOT_DATA, callback); 2810 + if (exists_style) { 2811 + js_set(js, success_ctx, "existsStyle", js_true); 2812 + js_set(js, error_ctx, "existsStyle", js_true); 2813 + } 2814 + 2815 + success_fn = js_heavy_mkfun(js, fs_callback_success_handler, success_ctx); 2816 + error_fn = js_heavy_mkfun(js, fs_callback_error_handler, error_ctx); 2817 + GC_ROOT_PIN(js, success_fn); 2818 + GC_ROOT_PIN(js, error_fn); 2819 + 2820 + js_promise_then(js, promise, success_fn, error_fn); 2821 + GC_ROOT_RESTORE(js, root_mark); 2822 + return js_mkundef(); 2823 + } 2824 + 2825 + static ant_value_t fs_callback_wrapper_call(ant_t *js, ant_value_t *args, int nargs) { 2826 + GC_ROOT_SAVE(root_mark, js); 2827 + ant_value_t wrapper = js_getcurrentfunc(js); 2828 + ant_value_t config = js_get_slot(wrapper, SLOT_DATA); 2829 + ant_value_t original = js_get_slot(config, SLOT_DATA); 2830 + 2831 + ant_value_t callback = js_mkundef(); 2832 + ant_value_t result = js_mkundef(); 2833 + ant_value_t this_arg = js_getthis(js); 2834 + ant_value_t ex = js_mkundef(); 2835 + 2836 + bool exists_style = false; 2837 + int call_nargs = nargs; 2838 + 2839 + GC_ROOT_PIN(js, original); 2840 + GC_ROOT_PIN(js, this_arg); 2841 + 2842 + exists_style = js_truthy(js, js_get(js, config, "existsStyle")); 2843 + if (nargs > 0 && is_callable(args[nargs - 1])) { 2844 + callback = args[nargs - 1]; 2845 + GC_ROOT_PIN(js, callback); 2846 + call_nargs--; 2847 + } 2848 + 2849 + result = fs_call_value(js, original, this_arg, args, call_nargs); 2850 + GC_ROOT_PIN(js, result); 2851 + 2852 + if (!is_callable(callback)) { 2853 + GC_ROOT_RESTORE(js, root_mark); 2854 + return result; 2855 + } 2856 + 2857 + if (is_err(result) || js->thrown_exists) { 2858 + ex = js->thrown_exists ? js->thrown_value : result; 2859 + GC_ROOT_PIN(js, ex); 2860 + js->thrown_exists = false; 2861 + js->thrown_value = js_mkundef(); 2862 + js->thrown_stack = js_mkundef(); 2863 + fs_callback_emit_error(js, callback, exists_style, ex); 2864 + GC_ROOT_RESTORE(js, root_mark); 2865 + return js_mkundef(); 2866 + } 2867 + 2868 + if (vtype(result) != T_PROMISE) { 2869 + fs_callback_emit_success(js, callback, exists_style, result); 2870 + GC_ROOT_RESTORE(js, root_mark); 2871 + return js_mkundef(); 2872 + } 2873 + 2874 + GC_ROOT_RESTORE(js, root_mark); 2875 + return fs_callback_attach_promise(js, result, callback, exists_style); 2876 + } 2877 + 2878 + static ant_value_t fs_make_callback_wrapper(ant_t *js, ant_value_t original, bool exists_style) { 2879 + ant_value_t config = js_mkobj(js); 2880 + 2881 + js_set_slot(config, SLOT_DATA, original); 2882 + if (exists_style) js_set(js, config, "existsStyle", js_true); 2883 + return js_heavy_mkfun(js, fs_callback_wrapper_call, config); 2884 + } 2885 + 1945 2886 static void fs_set_promise_methods(ant_t *js, ant_value_t lib) { 1946 2887 js_set(js, lib, "readFile", js_mkfun(builtin_fs_readFile)); 1947 2888 js_set(js, lib, "open", js_mkfun(builtin_fs_open_fd)); ··· 1949 2890 js_set(js, lib, "writeFile", js_mkfun(builtin_fs_writeFile)); 1950 2891 js_set(js, lib, "write", js_mkfun(builtin_fs_write_fd)); 1951 2892 js_set(js, lib, "writev", js_mkfun(builtin_fs_writev_fd)); 2893 + js_set(js, lib, "rm", js_mkfun(builtin_fs_rm)); 1952 2894 js_set(js, lib, "unlink", js_mkfun(builtin_fs_unlink)); 1953 2895 js_set(js, lib, "mkdir", js_mkfun(builtin_fs_mkdir)); 1954 2896 js_set(js, lib, "rmdir", js_mkfun(builtin_fs_rmdir)); 1955 2897 js_set(js, lib, "stat", js_mkfun(builtin_fs_stat)); 2898 + js_set(js, lib, "lstat", js_mkfun(builtin_fs_lstat)); 1956 2899 js_set(js, lib, "exists", js_mkfun(builtin_fs_exists)); 1957 2900 js_set(js, lib, "access", js_mkfun(builtin_fs_access)); 1958 2901 js_set(js, lib, "readdir", js_mkfun(builtin_fs_readdir)); 1959 2902 js_set(js, lib, "realpath", js_mkfun(builtin_fs_realpath)); 2903 + } 2904 + 2905 + static void fs_set_callback_compatible_methods(ant_t *js, ant_value_t lib) { 2906 + js_set(js, lib, "readFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readFile), false)); 2907 + js_set(js, lib, "open", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_open_fd), false)); 2908 + js_set(js, lib, "close", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_close_fd), false)); 2909 + js_set(js, lib, "writeFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_writeFile), false)); 2910 + js_set(js, lib, "write", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_write_fd), false)); 2911 + js_set(js, lib, "writev", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_writev_fd), false)); 2912 + js_set(js, lib, "rm", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rm), false)); 2913 + js_set(js, lib, "unlink", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_unlink), false)); 2914 + js_set(js, lib, "mkdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_mkdir), false)); 2915 + js_set(js, lib, "rmdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rmdir), false)); 2916 + js_set(js, lib, "stat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_stat), false)); 2917 + js_set(js, lib, "lstat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_lstat), false)); 2918 + js_set(js, lib, "exists", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_exists), true)); 2919 + js_set(js, lib, "access", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_access), false)); 2920 + js_set(js, lib, "readdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readdir), false)); 2921 + js_set(js, lib, "realpath", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_realpath), false)); 1960 2922 } 1961 2923 1962 2924 static ant_value_t fs_make_constants(ant_t *js) { ··· 1988 2950 ant_value_t fs_library(ant_t *js) { 1989 2951 ant_value_t lib = js_mkobj(js); 1990 2952 ant_value_t realpath_sync = js_mkfun(builtin_fs_realpathSync); 1991 - fs_set_promise_methods(js, lib); 2953 + 2954 + fs_set_callback_compatible_methods(js, lib); 2955 + fs_init_watch_constructors(js); 1992 2956 1993 2957 js_set(js, lib, "readFileSync", js_mkfun(builtin_fs_readFileSync)); 1994 2958 js_set(js, lib, "readSync", js_mkfun(builtin_fs_readSync)); ··· 2001 2965 js_set(js, lib, "appendFileSync", js_mkfun(builtin_fs_appendFileSync)); 2002 2966 js_set(js, lib, "copyFileSync", js_mkfun(builtin_fs_copyFileSync)); 2003 2967 js_set(js, lib, "renameSync", js_mkfun(builtin_fs_renameSync)); 2968 + js_set(js, lib, "rmSync", js_mkfun(builtin_fs_rmSync)); 2004 2969 js_set(js, lib, "unlinkSync", js_mkfun(builtin_fs_unlinkSync)); 2005 2970 js_set(js, lib, "mkdirSync", js_mkfun(builtin_fs_mkdirSync)); 2006 2971 js_set(js, lib, "rmdirSync", js_mkfun(builtin_fs_rmdirSync)); 2007 2972 js_set(js, lib, "statSync", js_mkfun(builtin_fs_statSync)); 2973 + js_set(js, lib, "lstatSync", js_mkfun(builtin_fs_lstatSync)); 2008 2974 js_set(js, lib, "existsSync", js_mkfun(builtin_fs_existsSync)); 2009 2975 js_set(js, lib, "accessSync", js_mkfun(builtin_fs_accessSync)); 2010 2976 js_set(js, lib, "readdirSync", js_mkfun(builtin_fs_readdirSync)); 2011 2977 js_set(js, lib, "realpathSync", realpath_sync); 2978 + js_set(js, lib, "watch", js_mkfun(builtin_fs_watch)); 2979 + js_set(js, lib, "watchFile", js_mkfun(builtin_fs_watchFile)); 2980 + js_set(js, lib, "unwatchFile", js_mkfun(builtin_fs_unwatchFile)); 2981 + js_set(js, lib, "FSWatcher", g_fswatcher_ctor); 2012 2982 js_set(js, realpath_sync, "native", realpath_sync); 2013 2983 2014 2984 js_set_getter_desc( ··· 2039 3009 } 2040 3010 2041 3011 void gc_mark_fs(ant_t *js, gc_mark_fn mark) { 3012 + fs_watcher_t *watcher = NULL; 3013 + 3014 + if (g_fswatcher_proto) mark(js, g_fswatcher_proto); 3015 + if (g_fswatcher_ctor) mark(js, g_fswatcher_ctor); 2042 3016 if (!pending_requests) return; 3017 + 2043 3018 unsigned int len = utarray_len(pending_requests); 2044 3019 for (unsigned int i = 0; i < len; i++) { 2045 3020 fs_request_t **reqp = (fs_request_t **)utarray_eltptr(pending_requests, i); 2046 3021 if (reqp && *reqp) { mark(js, (*reqp)->promise); } 3022 + } 3023 + 3024 + for (watcher = active_watchers; watcher; watcher = watcher->next_active) { 3025 + if (vtype(watcher->obj) == T_OBJ) mark(js, watcher->obj); 3026 + if (vtype(watcher->callback) != T_UNDEF) mark(js, watcher->callback); 2047 3027 } 2048 3028 }
+1
src/modules/module.c
··· 40 40 ant_value_t default_export = js_get_slot(ns, SLOT_DEFAULT); 41 41 if (vtype(default_export) != T_UNDEF) return default_export; 42 42 } 43 + 43 44 return ns; 44 45 } 45 46
+42 -8
src/modules/net.c
··· 93 93 static double g_default_auto_select_family_attempt_timeout = 250.0; 94 94 95 95 enum { 96 - NET_SERVER_NATIVE_TAG = 0x4e455453u, 97 - NET_SOCKET_NATIVE_TAG = 0x4e45544bu, 96 + NET_SERVER_NATIVE_TAG = 0x4e455453u, // NETS 97 + NET_SOCKET_NATIVE_TAG = 0x4e45544bu, // NETK 98 98 }; 99 99 100 100 static ant_value_t net_not_implemented(ant_t *js, const char *what); ··· 595 595 *out = "0.0.0.0"; 596 596 return true; 597 597 } 598 - if (strcmp(input, "localhost") == 0) { 599 - *out = "127.0.0.1"; 600 - return true; 601 - } 602 598 *out = input; 603 599 return true; 604 600 } ··· 865 861 if (!server || !server->listening) return out; 866 862 if (server->path) return js_mkstr(js, server->path, strlen(server->path)); 867 863 864 + struct sockaddr_storage saddr; 865 + int saddr_len = sizeof(saddr); 866 + 867 + if (uv_tcp_getsockname(&server->listener.handle.tcp, (struct sockaddr *)&saddr, &saddr_len) == 0) { 868 + char addr_str[INET6_ADDRSTRLEN] = {0}; 869 + const char *family = "IPv4"; 870 + int port = server->port; 871 + 872 + if (saddr.ss_family == AF_INET) { 873 + struct sockaddr_in *sa = (struct sockaddr_in *)&saddr; 874 + inet_ntop(AF_INET, &sa->sin_addr, addr_str, sizeof(addr_str)); 875 + port = ntohs(sa->sin_port); 876 + } else if (saddr.ss_family == AF_INET6) { 877 + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&saddr; 878 + inet_ntop(AF_INET6, &sa6->sin6_addr, addr_str, sizeof(addr_str)); 879 + port = ntohs(sa6->sin6_port); 880 + family = "IPv6"; 881 + } 882 + 883 + out = js_mkobj(js); 884 + js_set(js, out, "port", js_mknum((double)port)); 885 + js_set(js, out, "family", js_mkstr(js, family, strlen(family))); 886 + js_set(js, out, "address", js_mkstr(js, addr_str, strlen(addr_str))); 887 + 888 + return out; 889 + } 890 + 891 + struct in6_addr addr6; 868 892 out = js_mkobj(js); 869 893 js_set(js, out, "port", js_mknum(server->port)); 870 - js_set(js, out, "family", js_mkstr(js, "IPv4", 4)); 871 - js_set(js, out, "address", js_mkstr(js, server->host, strlen(server->host))); 894 + 895 + if (server->host && inet_pton(AF_INET6, server->host, &addr6) == 1) { 896 + char normalized[INET6_ADDRSTRLEN]; 897 + inet_ntop(AF_INET6, &addr6, normalized, sizeof(normalized)); 898 + js_set(js, out, "family", js_mkstr(js, "IPv6", 4)); 899 + js_set(js, out, "address", js_mkstr(js, normalized, strlen(normalized))); 900 + } else { 901 + const char *h = server->host ? server->host : "0.0.0.0"; 902 + js_set(js, out, "family", js_mkstr(js, "IPv4", 4)); 903 + js_set(js, out, "address", js_mkstr(js, h, strlen(h))); 904 + } 905 + 872 906 return out; 873 907 } 874 908
+7
src/modules/process.c
··· 342 342 } 343 343 } 344 344 345 + bool process_has_event_listeners(const char *event_type) { 346 + ProcessEventType *evt = NULL; 347 + if (!event_type) return false; 348 + HASH_FIND_STR(process_events, event_type, evt); 349 + return evt && evt->listener_count > 0; 350 + } 351 + 345 352 static void emit_stdio_event(ProcessEventType **events, const char *event_type, ant_value_t *args, int nargs) { 346 353 if (!rt->js) return; 347 354 ProcessEventType *evt = NULL;
+55 -14
src/modules/readline.c
··· 1 + // TODO: cleanup module, make cleaner 2 + 1 3 #include <compat.h> // IWYU pragma: keep 2 4 3 5 #include <stdlib.h> ··· 199 201 } 200 202 } 201 203 204 + static bool rl_has_event_listener(rl_interface_t *iface, const char *event_type) { 205 + RLEventType *evt = NULL; 206 + if (!iface || !event_type) return false; 207 + HASH_FIND_STR(iface->events, event_type, evt); 208 + return evt != NULL && evt->listener_count > 0; 209 + } 210 + 202 211 static ant_value_t get_history_array(ant_t *js, rl_interface_t *iface) { 203 212 ant_value_t arr = js_mkarr(js); 204 - for (int i = 0; i < iface->history.count; i++) { 205 - js_arr_push(js, arr, js_mkstr(js, iface->history.lines[i], strlen(iface->history.lines[i]))); 206 - } 213 + for (int i = 0; i < iface->history.count; i++) js_arr_push( 214 + js, arr, js_mkstr(js, iface->history.lines[i], strlen(iface->history.lines[i])) 215 + ); 207 216 return arr; 208 217 } 209 218 ··· 522 531 } else if (c == 127 || c == 8) { 523 532 handle_backspace(iface); 524 533 } else if (c == 3) { 525 - emit_event(js, iface, "SIGINT", NULL, 0); 534 + if (rl_has_event_listener(iface, "SIGINT")) { 535 + emit_event(js, iface, "SIGINT", NULL, 0); 536 + } else if (process_has_event_listeners("SIGINT")) { 537 + ant_value_t sig_arg = js_mkstr(js, "SIGINT", 6); 538 + emit_process_event("SIGINT", &sig_arg, 1); 539 + } else raise(SIGINT); 526 540 } else if (c == 4) { 527 541 if (iface->line_len == 0) { 528 542 emit_event(js, iface, "close", NULL, 0); 529 543 iface->closed = true; 530 544 uv_read_stop(stream); 531 - } else { 532 - handle_delete(iface); 533 - } 545 + } else handle_delete(iface); 534 546 } else if (c == 1) { 535 547 iface->line_pos = 0; 536 548 refresh_line(iface); ··· 562 574 563 575 if (!iface->tty_initialized) { 564 576 uv_loop_t *loop = uv_default_loop(); 565 - 566 577 int is_tty = uv_guess_handle(STDIN_FILENO) == UV_TTY; 567 578 568 579 if (is_tty) { 569 - if (uv_tty_init(loop, &iface->tty_in, STDIN_FILENO, 1) != 0) { 570 - return; 571 - } 580 + if (uv_tty_init(loop, &iface->tty_in, STDIN_FILENO, 1) != 0) return; 572 581 uv_tty_set_mode(&iface->tty_in, UV_TTY_MODE_RAW); 573 582 } else { 574 - if (uv_tty_init(loop, &iface->tty_in, STDIN_FILENO, 1) != 0) { 575 - return; 576 - } 583 + if (uv_tty_init(loop, &iface->tty_in, STDIN_FILENO, 1) != 0) return; 577 584 } 578 585 579 586 iface->tty_in.data = iface; ··· 698 705 return this_obj; 699 706 } 700 707 708 + static ant_value_t rl_interface_remove_all_listeners(ant_t *js, ant_value_t *args, int nargs) { 709 + ant_value_t this_obj = js_getthis(js); 710 + rl_interface_t *iface = get_interface(js, this_obj); 711 + 712 + RLEventType *evt = NULL; 713 + RLEventType *tmp = NULL; 714 + char *event = NULL; 715 + 716 + if (!iface) return js_mkerr(js, "Invalid Interface"); 717 + if (nargs < 1 || vtype(args[0]) == T_UNDEF) { 718 + HASH_ITER(hh, iface->events, evt, tmp) { 719 + HASH_DEL(iface->events, evt); 720 + free(evt->event_type); 721 + free(evt); 722 + } 723 + return this_obj; 724 + } 725 + 726 + event = js_getstr(js, args[0], NULL); 727 + if (!event) return this_obj; 728 + 729 + HASH_FIND_STR(iface->events, event, evt); 730 + if (!evt) return this_obj; 731 + 732 + HASH_DEL(iface->events, evt); 733 + free(evt->event_type); 734 + free(evt); 735 + 736 + return this_obj; 737 + } 738 + 701 739 static ant_value_t rl_interface_emit(ant_t *js, ant_value_t *args, int nargs) { 702 740 ant_value_t this_obj = js_getthis(js); 703 741 rl_interface_t *iface = get_interface(js, this_obj); ··· 1162 1200 js_set(js, obj, "off", js_mkfun(rl_interface_off)); 1163 1201 js_set(js, obj, "addListener", js_mkfun(rl_interface_on)); 1164 1202 js_set(js, obj, "removeListener", js_mkfun(rl_interface_off)); 1203 + js_set(js, obj, "removeAllListeners", js_mkfun(rl_interface_remove_all_listeners)); 1165 1204 js_set(js, obj, "emit", js_mkfun(rl_interface_emit)); 1166 1205 1167 1206 js_set(js, obj, "close", js_mkfun(rl_interface_close)); ··· 1181 1220 js_set(js, obj, "terminal", js_bool(iface->terminal)); 1182 1221 js_set_sym(js, obj, get_asyncIterator_sym(), js_mkfun(rl_interface_async_iterator)); 1183 1222 js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, "Interface", 9)); 1223 + 1224 + start_reading(iface); 1184 1225 1185 1226 return obj; 1186 1227 }
+139 -36
src/modules/regex.c
··· 1 + // TODO: cleanup module, make cleaner 2 + 1 3 #include <stdlib.h> 2 4 #include <string.h> 3 5 #include <stdio.h> ··· 65 67 return c == 'w' || c == 'W' || c == 'd' || c == 'D' || c == 's' || c == 'S'; 66 68 } 67 69 68 - size_t js_to_pcre2_pattern(const char *src, size_t src_len, char *dst, size_t dst_size) { 70 + static size_t v_close_bracket(const char *src, size_t src_len, size_t open) { 71 + int depth = 0; 72 + for (size_t i = open; i < src_len; i++) { 73 + if (src[i] == '\\' && i + 1 < src_len) { i++; continue; } 74 + if (src[i] == '[') depth++; 75 + else if (src[i] == ']') { if (--depth == 0) return i; } 76 + } 77 + return src_len; 78 + } 79 + 80 + static size_t v_translate_part(const char *p, size_t len, char *out, size_t out_size) { 81 + if (len && p[0] == '[') return js_to_pcre2_pattern(p, len, out, out_size, false); 82 + char tmp[1024]; 83 + if (len >= sizeof(tmp) - 2) return 0; 84 + tmp[0] = '['; memcpy(tmp + 1, p, len); tmp[len + 1] = ']'; 85 + return js_to_pcre2_pattern(tmp, len + 2, out, out_size, false); 86 + } 87 + 88 + static int v_set_op(const char *src, size_t start, size_t end, size_t *op_pos) { 89 + int depth = 0; 90 + for (size_t i = start; i < end; ) { 91 + if (src[i] == '\\' && i + 1 < end) { 92 + char n = src[i + 1]; 93 + if ((n == 'p' || n == 'P') && i + 2 < end && src[i + 2] == '{') { 94 + i += 3; while (i < end && src[i] != '}') i++; if (i < end) i++; continue; 95 + } 96 + if ((n == 'u' || n == 'x') && i + 2 < end && src[i + 2] == '{') { 97 + i += 3; while (i < end && src[i] != '}') i++; if (i < end) i++; continue; 98 + } 99 + i += 2; continue; 100 + } 101 + if (src[i] == '[') { depth++; i++; continue; } 102 + if (src[i] == ']') { if (depth > 0) { depth--; i++; continue; } break; } 103 + if (!depth && i + 1 < end) { 104 + if (src[i] == '&' && src[i+1] == '&') { *op_pos = i; return 1; } 105 + if (src[i] == '-' && src[i+1] == '-') { *op_pos = i; return 2; } 106 + } 107 + i++; 108 + } 109 + return 0; 110 + } 111 + 112 + size_t js_to_pcre2_pattern(const char *src, size_t src_len, char *dst, size_t dst_size, bool v_flag) { 69 113 size_t di = 0; 70 - bool in_charclass = false; 114 + int charclass_depth = 0; 71 115 72 116 #define OUT(ch) do { if (di < dst_size - 1) dst[di++] = (ch); } while(0) 73 117 74 118 for (size_t si = 0; si < src_len && di < dst_size - 1; si++) { 75 - if (src[si] == '[' && !in_charclass) { 76 - in_charclass = true; 119 + if (src[si] == '[') { 120 + if (v_flag && charclass_depth == 0) { 121 + size_t close = v_close_bracket(src, src_len, si); 122 + size_t op_pos; 123 + int op_type = v_set_op(src, si + 1, close, &op_pos); 124 + if (op_type && close < src_len) { 125 + char ao[1024], bo[1024]; 126 + size_t aol = v_translate_part(&src[si + 1], op_pos - si - 1, ao, sizeof(ao)); 127 + size_t bol = v_translate_part(&src[op_pos + 2], close - op_pos - 2, bo, sizeof(bo)); 128 + const char *la = op_type == 1 ? ao : bo, *ra = op_type == 1 ? bo : ao; 129 + size_t ll = op_type == 1 ? aol : bol, rl = op_type == 1 ? bol : aol; 130 + OUT('('); OUT('?'); OUT(op_type == 1 ? '=' : '!'); 131 + for (size_t k = 0; k < ll; k++) OUT(la[k]); 132 + OUT(')'); 133 + for (size_t k = 0; k < rl; k++) OUT(ra[k]); 134 + si = close; 135 + continue; 136 + } 137 + } 138 + charclass_depth++; 77 139 OUT('['); 78 140 continue; 79 141 } 80 - if (src[si] == ']' && in_charclass) { 81 - in_charclass = false; 142 + if (src[si] == ']' && charclass_depth > 0) { 143 + charclass_depth--; 82 144 OUT(']'); 83 145 continue; 84 146 } 85 147 86 - if (in_charclass && src[si] == '-' && si > 0 && src[si - 1] != '[' && 148 + if (charclass_depth > 0 && src[si] == '-' && si > 0 && src[si - 1] != '[' && 87 149 si + 1 < src_len && src[si + 1] != ']') { 88 150 bool prev_is_shorthand = (si >= 2 && src[si - 2] == '\\' && is_class_shorthand(src[si - 1])); 89 151 bool next_is_shorthand = (si + 2 < src_len && src[si + 1] == '\\' && is_class_shorthand(src[si + 2])); ··· 227 289 }; 228 290 bool has_eq = (memchr(prop, '=', prop_len) != NULL); 229 291 bool has_colon = (memchr(prop, ':', prop_len) != NULL); 292 + if (!has_eq && !has_colon && next == 'p' && charclass_depth == 0) { 293 + static const struct { const char *name; const char *exp; } sprops[] = { 294 + {"Emoji_Keycap_Sequence", 295 + "(?:\\x{23}\\x{fe0f}\\x{20e3}|\\x{2a}\\x{fe0f}\\x{20e3}|[\\x{30}-\\x{39}]\\x{fe0f}\\x{20e3})"}, 296 + {"RGI_Emoji", 297 + "(?:[\\x{1f1e6}-\\x{1f1ff}]{2}|(?:\\p{Emoji}[\\x{1f3fb}-\\x{1f3ff}]?\\x{200d})+\\p{Emoji}[\\x{1f3fb}-\\x{1f3ff}]?|\\p{Emoji}[\\x{1f3fb}-\\x{1f3ff}]|\\p{Emoji}\\x{fe0f}?)"}, 298 + }; 299 + for (size_t m = 0; m < sizeof(sprops)/sizeof(sprops[0]); m++) { 300 + if (strlen(sprops[m].name) == prop_len && memcmp(sprops[m].name, prop, prop_len) == 0) { 301 + for (const char *r = sprops[m].exp; *r && di < dst_size - 1; r++) OUT(*r); 302 + si = brace_end; 303 + goto next_char; 304 + } 305 + } 306 + } 230 307 if (has_eq || has_colon) { 231 308 char sep = has_eq ? '=' : ':'; 232 309 const char *val = memchr(prop, sep, prop_len); ··· 247 324 } 248 325 } 249 326 } 327 + if (!has_eq && !has_colon) { 328 + static const struct { const char *name; const char *range; } rangeprops[] = { 329 + {"ASCII", "\\x{0}-\\x{7f}"}, 330 + {"Any", "\\x{0}-\\x{10ffff}"}, 331 + }; 332 + for (size_t m = 0; m < sizeof(rangeprops)/sizeof(rangeprops[0]); m++) { 333 + if (strlen(rangeprops[m].name) == prop_len && memcmp(rangeprops[m].name, prop, prop_len) == 0) { 334 + if (charclass_depth > 0) { 335 + for (const char *r = rangeprops[m].range; *r; r++) OUT(*r); 336 + } else { 337 + OUT('['); if (next == 'P') OUT('^'); 338 + for (const char *r = rangeprops[m].range; *r; r++) OUT(*r); 339 + OUT(']'); 340 + } 341 + si = brace_end; 342 + goto next_char; 343 + } 344 + } 345 + } 250 346 const char *replacement = NULL; 251 347 if (!has_eq && !has_colon) { 252 348 for (size_t m = 0; m < sizeof(gc_map)/sizeof(gc_map[0]); m++) { ··· 270 366 } 271 367 } 272 368 } 273 - if (extra_range && !in_charclass) { 369 + if (extra_range && charclass_depth == 0) { 274 370 const char *pfx = (next == 'p') ? "(?:\\p{" : "(?:\\P{"; 275 371 for (const char *r = pfx; *r; r++) OUT(*r); 276 372 for (size_t k = brace_start; k < brace_end; k++) OUT(src[k]); ··· 316 412 : js_setprop(js, obj, js_mkstr(js, key, klen), val)) 317 413 318 414 static void regexp_init_flags(ant_t *js, ant_value_t obj, const char *fstr, ant_offset_t flen, bool is_new) { 319 - bool g = false, i = false, m = false, s = false, u = false, y = false; 415 + bool d = false, g = false, i = false, m = false; 416 + bool s = false, u = false, v = false, y = false; 417 + 320 418 for (ant_offset_t k = 0; k < flen; k++) { 419 + if (fstr[k] == 'd') d = true; 321 420 if (fstr[k] == 'g') g = true; 322 421 if (fstr[k] == 'i') i = true; 323 422 if (fstr[k] == 'm') m = true; 324 423 if (fstr[k] == 's') s = true; 325 424 if (fstr[k] == 'u') u = true; 425 + if (fstr[k] == 'v') v = true; 326 426 if (fstr[k] == 'y') y = true; 327 427 } 328 428 329 - char sorted[8]; int si = 0; 429 + char sorted[10]; int si = 0; 430 + if (d) sorted[si++] = 'd'; 330 431 if (g) sorted[si++] = 'g'; 331 432 if (i) sorted[si++] = 'i'; 332 433 if (m) sorted[si++] = 'm'; 333 434 if (s) sorted[si++] = 's'; 334 435 if (u) sorted[si++] = 'u'; 436 + if (v) sorted[si++] = 'v'; 335 437 if (y) sorted[si++] = 'y'; 336 438 337 439 REGEXP_SET_PROP(js, obj, "flags", 5, js_mkstr(js, sorted, si), is_new); 440 + REGEXP_SET_PROP(js, obj, "hasIndices", 10, mkval(T_BOOL, d ? 1 : 0), is_new); 338 441 REGEXP_SET_PROP(js, obj, "global", 6, mkval(T_BOOL, g ? 1 : 0), is_new); 339 442 REGEXP_SET_PROP(js, obj, "ignoreCase", 10, mkval(T_BOOL, i ? 1 : 0), is_new); 340 443 REGEXP_SET_PROP(js, obj, "multiline", 9, mkval(T_BOOL, m ? 1 : 0), is_new); 341 444 REGEXP_SET_PROP(js, obj, "dotAll", 6, mkval(T_BOOL, s ? 1 : 0), is_new); 342 445 REGEXP_SET_PROP(js, obj, "unicode", 7, mkval(T_BOOL, u ? 1 : 0), is_new); 446 + REGEXP_SET_PROP(js, obj, "unicodeSets", 11, mkval(T_BOOL, v ? 1 : 0), is_new); 343 447 REGEXP_SET_PROP(js, obj, "sticky", 6, mkval(T_BOOL, y ? 1 : 0), is_new); 344 448 REGEXP_SET_PROP(js, obj, "lastIndex", 9, tov(0), is_new); 345 449 } ··· 461 565 ant_offset_t plen, poff = vstr(js, source_val, &plen); 462 566 const char *pattern_ptr = (char *)(uintptr_t)(poff); 463 567 464 - bool ignore_case = false, multiline = false, dotall = false, sticky = false, unicode = false; 568 + bool ignore_case = false, multiline = false, dotall = false, sticky = false, unicode = false, v_flag = false; 465 569 ant_offset_t flags_off = lkp(js, regexp_obj, "flags", 5); 466 570 if (flags_off != 0) { 467 571 ant_value_t flags_val = js_propref_load(js, flags_off); ··· 474 578 if (flags_str[i] == 's') dotall = true; 475 579 if (flags_str[i] == 'y') sticky = true; 476 580 if (flags_str[i] == 'u') unicode = true; 581 + if (flags_str[i] == 'v') v_flag = true; 477 582 } 478 583 } 479 584 } 480 585 481 586 char pcre2_pattern[4096]; 482 - size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, plen, pcre2_pattern, sizeof(pcre2_pattern)); 587 + size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, plen, pcre2_pattern, sizeof(pcre2_pattern), v_flag); 483 588 484 589 uint32_t options = PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_UNSET_BACKREF | PCRE2_DUPNAMES; 485 590 if (ignore_case) options |= PCRE2_CASELESS; ··· 859 964 return mkval(T_BOOL, vtype(result) != T_NULL ? 1 : 0); 860 965 } 861 966 862 - ant_value_t builtin_regexp_flags_getter(ant_t *js, ant_value_t *args, int nargs) { 967 + static ant_value_t builtin_regexp_flags_getter(ant_t *js, ant_value_t *args, int nargs) { 863 968 (void)args; (void)nargs; 864 969 ant_value_t rx = js->this_val; 865 970 if (!is_object_type(rx)) ··· 883 988 return js_mkstr(js, buf, n); 884 989 } 885 990 886 - ant_value_t builtin_regexp_symbol_match(ant_t *js, ant_value_t *args, int nargs) { 991 + static ant_value_t builtin_regexp_symbol_match(ant_t *js, ant_value_t *args, int nargs) { 887 992 ant_value_t rx = js->this_val; 888 993 if (!is_object_type(rx)) 889 994 return js_mkerr_typed(js, JS_ERR_TYPE, "RegExp.prototype[@@match] called on non-object"); ··· 932 1037 } 933 1038 } 934 1039 935 - ant_value_t builtin_regexp_symbol_replace(ant_t *js, ant_value_t *args, int nargs) { 1040 + static ant_value_t builtin_regexp_symbol_replace(ant_t *js, ant_value_t *args, int nargs) { 936 1041 ant_value_t rx = js->this_val; 937 1042 if (!is_object_type(rx)) 938 1043 return js_mkerr_typed(js, JS_ERR_TYPE, "RegExp.prototype[@@replace] called on non-object"); ··· 1072 1177 return ret; 1073 1178 } 1074 1179 1075 - ant_value_t builtin_regexp_symbol_search(ant_t *js, ant_value_t *args, int nargs) { 1180 + static ant_value_t builtin_regexp_symbol_search(ant_t *js, ant_value_t *args, int nargs) { 1076 1181 ant_value_t rx = js->this_val; 1077 1182 if (!is_object_type(rx)) 1078 1183 return js_mkerr_typed(js, JS_ERR_TYPE, "RegExp.prototype[@@search] called on non-object"); ··· 1098 1203 return vtype(idx) == T_NUM ? idx : tov(-1); 1099 1204 } 1100 1205 1101 - ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs) { 1206 + static ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs) { 1102 1207 ant_value_t rx = js_getthis(js); 1103 1208 if (!is_object_type(rx)) 1104 1209 return js_mkerr_typed(js, JS_ERR_TYPE, "RegExp.prototype[@@split] called on non-object"); ··· 1228 1333 return mkval(T_ARR, vdata(A)); 1229 1334 } 1230 1335 1231 - ant_value_t do_regex_match_pcre2( 1232 - ant_t *js, const char *pattern_ptr, ant_offset_t pattern_len, 1233 - const char *str_ptr, ant_offset_t str_len, 1234 - bool global_flag, bool ignore_case, bool multiline 1235 - ) { 1336 + ant_value_t do_regex_match_pcre2(ant_t *js, regex_match_args_t args) { 1236 1337 char pcre2_pattern[4096]; 1237 - size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, pattern_len, pcre2_pattern, sizeof(pcre2_pattern)); 1338 + size_t pcre2_len = js_to_pcre2_pattern(args.pattern_ptr, args.pattern_len, pcre2_pattern, sizeof(pcre2_pattern), false); 1238 1339 1239 1340 uint32_t options = PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_UNSET_BACKREF | PCRE2_DUPNAMES; 1240 - if (ignore_case) options |= PCRE2_CASELESS; 1241 - if (multiline) options |= PCRE2_MULTILINE; 1341 + if (args.ignore_case) options |= PCRE2_CASELESS; 1342 + if (args.multiline) options |= PCRE2_MULTILINE; 1242 1343 1243 1344 int errcode; 1244 1345 PCRE2_SIZE erroffset; ··· 1259 1360 PCRE2_SIZE pos = 0; 1260 1361 int match_count = 0; 1261 1362 1262 - while (pos <= str_len) { 1263 - int rc = pcre2_match(re, (PCRE2_SPTR)str_ptr, str_len, pos, 0, match_data, NULL); 1363 + while (pos <= (PCRE2_SIZE)args.str_len) { 1364 + int rc = pcre2_match(re, (PCRE2_SPTR)args.str_ptr, args.str_len, pos, 0, match_data, NULL); 1264 1365 if (rc < 0) break; 1265 1366 1266 1367 PCRE2_SIZE *ovector = pcre2_get_ovector_pointer(match_data); 1267 1368 PCRE2_SIZE match_start = ovector[0]; 1268 1369 PCRE2_SIZE match_end = ovector[1]; 1269 1370 1270 - if (global_flag) { 1271 - ant_value_t match_str = js_mkstr(js, str_ptr + match_start, match_end - match_start); 1371 + if (args.global) { 1372 + ant_value_t match_str = js_mkstr(js, args.str_ptr + match_start, match_end - match_start); 1272 1373 if (is_err(match_str)) { 1273 1374 pcre2_match_data_free(match_data); 1274 1375 pcre2_code_free(re); ··· 1282 1383 if (start == PCRE2_UNSET) { 1283 1384 js_arr_push(js, result_arr, js_mkundef()); 1284 1385 } else { 1285 - ant_value_t match_str = js_mkstr(js, str_ptr + start, end - start); 1386 + ant_value_t match_str = js_mkstr(js, args.str_ptr + start, end - start); 1286 1387 if (is_err(match_str)) { 1287 1388 pcre2_match_data_free(match_data); 1288 1389 pcre2_code_free(re); ··· 1295 1396 } 1296 1397 match_count++; 1297 1398 1298 - if (!global_flag) break; 1399 + if (!args.global) break; 1299 1400 if (match_start == match_end) { 1300 1401 pos = match_end + 1; 1301 1402 } else { pos = match_end; } ··· 1537 1638 const char *str_ptr = (char *)(uintptr_t)(str_off); 1538 1639 1539 1640 char pcre2_pattern[4096]; 1540 - size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, pattern_len, pcre2_pattern, sizeof(pcre2_pattern)); 1641 + size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, pattern_len, pcre2_pattern, sizeof(pcre2_pattern), false); 1541 1642 1542 1643 uint32_t options = PCRE2_UTF | PCRE2_UCP | PCRE2_MATCH_UNSET_BACKREF | PCRE2_DUPNAMES; 1543 1644 if (ignore_case) options |= PCRE2_CASELESS; ··· 1624 1725 ant_offset_t str_len, str_off = vstr(js, str, &str_len); 1625 1726 const char *str_ptr = (char *)(uintptr_t)(str_off); 1626 1727 1627 - ant_value_t result = do_regex_match_pcre2( 1628 - js, pattern_ptr, pattern_len, 1629 - str_ptr, str_len, global_flag, ignore_case, multiline 1630 - ); 1728 + ant_value_t result = do_regex_match_pcre2(js, (regex_match_args_t){ 1729 + .pattern_ptr = pattern_ptr, .pattern_len = pattern_len, 1730 + .str_ptr = str_ptr, .str_len = str_len, 1731 + .global = global_flag, .ignore_case = ignore_case, .multiline = multiline, 1732 + }); 1631 1733 1632 1734 if (!global_flag && vtype(result) == T_ARR) { 1633 1735 js_setprop(js, result, js_mkstr(js, "input", 5), str); ··· 1660 1762 js_set_sym(js, regexp_proto, get_match_sym(), js_mkfun(builtin_regexp_symbol_match)); 1661 1763 js_set_sym(js, regexp_proto, get_replace_sym(), js_mkfun(builtin_regexp_symbol_replace)); 1662 1764 js_set_sym(js, regexp_proto, get_search_sym(), js_mkfun(builtin_regexp_symbol_search)); 1765 + js_set_sym(js, regexp_proto, get_toStringTag_sym(), js_mkstr(js, "RegExp", 6)); 1663 1766 js_set_getter_desc(js, regexp_proto, "flags", 5, js_mkfun(builtin_regexp_flags_getter), JS_DESC_C); 1664 1767 js_setprop(js, regexp_proto, js_mkstr(js, "compile", 7), js_mkfun(builtin_regexp_compile)); 1665 1768
+54 -16
src/modules/timer.c
··· 60 60 static void add_timer_entry(timer_entry_t *entry) { 61 61 entry->next = timer_state.timers; 62 62 entry->prev = NULL; 63 - if (timer_state.timers) { 64 - timer_state.timers->prev = entry; 65 - } 63 + if (timer_state.timers) timer_state.timers->prev = entry; 66 64 timer_state.timers = entry; 67 65 } 68 66 ··· 73 71 } 74 72 75 73 static int timer_entry_is_registered(timer_entry_t *entry) { 76 - for (timer_entry_t *it = timer_state.timers; it != NULL; it = it->next) { 74 + for (timer_entry_t *it = timer_state.timers; it != NULL; it = it->next) 77 75 if (it == entry) return 1; 78 - } 79 76 return 0; 80 77 } 81 78 79 + static timer_entry_t *find_timer_entry_by_id(int timer_id) { 80 + for (timer_entry_t *entry = timer_state.timers; entry != NULL; entry = entry->next) 81 + if (entry->timer_id == timer_id) return entry; 82 + return NULL; 83 + } 84 + 82 85 static int timer_copy_args(timer_entry_t *entry, ant_value_t *args, int nargs) { 83 86 entry->nargs = nargs > 2 ? nargs - 2 : 0; 84 87 if (entry->nargs > 0) { ··· 90 93 } 91 94 92 95 static ant_value_t timer_to_primitive(ant_t *js, ant_value_t *args, int nargs) { 93 - return js_get(js, js_getthis(js), "id"); 96 + return js_get_slot(js_getthis(js), SLOT_DATA); 97 + } 98 + 99 + static ant_value_t js_timer_ref(ant_t *js, ant_value_t *args, int nargs) { 100 + ant_value_t this_obj = js_getthis(js); 101 + timer_entry_t *entry = find_timer_entry_by_id((int)js_getnum(js_get_slot(this_obj, SLOT_DATA))); 102 + if (entry && !entry->closed && !uv_is_closing((uv_handle_t *)&entry->handle)) 103 + uv_ref((uv_handle_t *)&entry->handle); 104 + return this_obj; 105 + } 106 + 107 + static ant_value_t js_timer_unref(ant_t *js, ant_value_t *args, int nargs) { 108 + ant_value_t this_obj = js_getthis(js); 109 + timer_entry_t *entry = find_timer_entry_by_id((int)js_getnum(js_get_slot(this_obj, SLOT_DATA))); 110 + if (entry && !entry->closed && !uv_is_closing((uv_handle_t *)&entry->handle)) 111 + uv_unref((uv_handle_t *)&entry->handle); 112 + return this_obj; 113 + } 114 + 115 + static ant_value_t js_timer_has_ref(ant_t *js, ant_value_t *args, int nargs) { 116 + timer_entry_t *entry = find_timer_entry_by_id((int)js_getnum(js_get_slot(js_getthis(js), SLOT_DATA))); 117 + if (!entry || entry->closed || uv_is_closing((uv_handle_t *)&entry->handle)) return js_false; 118 + return js_bool(uv_has_ref((const uv_handle_t *)&entry->handle) != 0); 94 119 } 95 120 96 121 static ant_value_t timer_make_object(ant_t *js, int id, double delay_ms, int is_interval, ant_value_t callback) { 97 122 ant_value_t obj = js_mkobj(js); 98 - 99 - js_set(js, obj, "id", js_mknum((double)id)); 123 + 100 124 js_set(js, obj, "delay", js_mknum(delay_ms)); 101 125 js_set(js, obj, "repeat", is_interval ? js_mknum(delay_ms) : js_mknull()); 126 + 102 127 js_set(js, obj, "callback", callback); 128 + js_set_descriptor(js, obj, "callback", 8, JS_DESC_W | JS_DESC_C); 103 129 104 - js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, is_interval ? "Interval" : "Timeout", is_interval ? 8 : 7)); 130 + js_set(js, obj, "ref", js_mkfun(js_timer_ref)); 131 + js_set_descriptor(js, obj, "ref", 3, JS_DESC_W | JS_DESC_C); 132 + 133 + js_set(js, obj, "unref", js_mkfun(js_timer_unref)); 134 + js_set_descriptor(js, obj, "unref", 5, JS_DESC_W | JS_DESC_C); 135 + 136 + js_set(js, obj, "hasRef", js_mkfun(js_timer_has_ref)); 137 + js_set_descriptor(js, obj, "hasRef", 6, JS_DESC_W | JS_DESC_C); 138 + 139 + js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, is_interval 140 + ? "Interval" 141 + : "Timeout", is_interval ? 8 : 7) 142 + ); 143 + 144 + js_set_slot(obj, SLOT_DATA, js_mknum((double)id)); 105 145 js_set_sym(js, obj, get_toPrimitive_sym(), js_mkfun(timer_to_primitive)); 106 - 146 + 107 147 return obj; 108 148 } 109 149 110 150 static int timer_id_from_arg(ant_t *js, ant_value_t arg) { 111 151 if (vtype(arg) == T_NUM) return (int)js_getnum(arg); 112 - return (int)js_getnum(js_get(js, arg, "id")); 152 + return (int)js_getnum(js_get_slot(arg, SLOT_DATA)); 113 153 } 114 154 115 155 static void timer_close_cb(uv_handle_t *h) { ··· 143 183 sv_vm_call(js->vm, js, entry->callback, js_mkundef(), entry->args, entry->nargs, NULL, false); 144 184 process_microtasks(js); 145 185 146 - if (!entry->is_interval) { 147 - if (!uv_is_closing((uv_handle_t *)&entry->handle)) { 148 - uv_close((uv_handle_t *)&entry->handle, timer_close_cb); 149 - } 150 - } 186 + if (!entry->is_interval) if (!uv_is_closing((uv_handle_t *)&entry->handle)) uv_close( 187 + (uv_handle_t *)&entry->handle, timer_close_cb 188 + ); 151 189 } 152 190 153 191 // setTimeout(callback, delay, ...args)
+1 -1
src/modules/tls.c
··· 23 23 bool closed; 24 24 } ant_tls_context_wrap_t; 25 25 26 - enum { TLS_CONTEXT_NATIVE_TAG = 0x544c5343u }; 26 + enum { TLS_CONTEXT_NATIVE_TAG = 0x544c5343u }; // TLSC 27 27 static ant_value_t g_tls_context_proto = 0; 28 28 29 29 static void tls_context_free(ant_tls_context_wrap_t *wrap) {
+214
src/modules/util.c
··· 1 + // TODO: split module into smaller files 2 + 1 3 #include <compat.h> // IWYU pragma: keep 2 4 3 5 #include <ctype.h> ··· 254 256 return out; 255 257 } 256 258 259 + static inline bool util_env_is_inline_ws(char ch) { 260 + return ch == ' ' || ch == '\t' || ch == '\r'; 261 + } 262 + 263 + static inline bool util_env_is_ident_start(char ch) { 264 + return 265 + (ch >= 'A' && ch <= 'Z') || 266 + (ch >= 'a' && ch <= 'z') || 267 + ch == '_'; 268 + } 269 + 270 + static inline bool util_env_is_ident_continue(char ch) { 271 + return util_env_is_ident_start(ch) || (ch >= '0' && ch <= '9'); 272 + } 273 + 274 + static void util_env_skip_inline_ws(const char *src, size_t len, size_t *cursor) { 275 + while (*cursor < len && util_env_is_inline_ws(src[*cursor])) (*cursor)++; 276 + } 277 + 278 + static void util_env_skip_line(const char *src, size_t len, size_t *cursor) { 279 + while (*cursor < len && src[*cursor] != '\n') (*cursor)++; 280 + if (*cursor < len && src[*cursor] == '\n') (*cursor)++; 281 + } 282 + 283 + static bool util_env_consume_export(const char *src, size_t len, size_t *cursor) { 284 + if (*cursor + 6 > len) return false; 285 + if (memcmp(src + *cursor, "export", 6) != 0) return false; 286 + 287 + if ( 288 + *cursor + 6 < len && 289 + !util_env_is_inline_ws(src[*cursor + 6]) && 290 + src[*cursor + 6] != '\n' 291 + ) return false; 292 + 293 + *cursor += 6; 294 + util_env_skip_inline_ws(src, len, cursor); 295 + return true; 296 + } 297 + 298 + static bool util_env_parse_key( 299 + const char *src, size_t len, size_t *cursor, 300 + size_t *key_start, size_t *key_end 301 + ) { 302 + if (*cursor >= len || !util_env_is_ident_start(src[*cursor])) return false; 303 + *key_start = *cursor; (*cursor)++; 304 + 305 + while (*cursor < len && util_env_is_ident_continue(src[*cursor])) (*cursor)++; 306 + *key_end = *cursor; 307 + 308 + return true; 309 + } 310 + 311 + static void util_env_set_entry( 312 + ant_t *js, ant_value_t obj, const char *key, 313 + size_t key_len, ant_value_t value 314 + ) { 315 + ant_value_t key_str = js_mkstr(js, key, key_len); 316 + js_setprop(js, obj, key_str, value); 317 + } 318 + 319 + static ant_value_t util_env_parse_quoted_value( 320 + ant_t *js, const char *src, 321 + size_t len, size_t *cursor 322 + ) { 323 + util_sb_t sb = {0}; 324 + ant_value_t value = js_mkstr(js, "", 0); 325 + char quote; 326 + 327 + if (*cursor >= len) return value; 328 + quote = src[(*cursor)++]; 329 + 330 + while (*cursor < len) { 331 + char ch = src[(*cursor)++]; 332 + if (ch == quote) goto done; 333 + if (ch != '\\' || *cursor >= len) { 334 + util_sb_append_c(&sb, ch); 335 + continue; 336 + } 337 + 338 + char esc = src[(*cursor)++]; 339 + if (quote != '"') { 340 + util_sb_append_c(&sb, esc); 341 + continue; 342 + } 343 + 344 + switch (esc) { 345 + case 'n': util_sb_append_c(&sb, '\n'); break; 346 + case 'r': util_sb_append_c(&sb, '\r'); break; 347 + case 't': util_sb_append_c(&sb, '\t'); break; 348 + case '\\': util_sb_append_c(&sb, '\\'); break; 349 + case '"': util_sb_append_c(&sb, '"'); break; 350 + default: util_sb_append_c(&sb, esc); break; 351 + } 352 + } 353 + 354 + done: 355 + value = js_mkstr(js, sb.buf ? sb.buf : "", sb.len); 356 + free(sb.buf); 357 + 358 + while (*cursor < len && src[*cursor] != '\n') { 359 + if (src[*cursor] == '#') break; 360 + (*cursor)++; 361 + } 362 + 363 + return value; 364 + } 365 + 366 + static ant_value_t util_env_parse_unquoted_value( 367 + ant_t *js, const char *src, 368 + size_t len, size_t *cursor 369 + ) { 370 + size_t value_start = *cursor; 371 + size_t value_end = *cursor; 372 + bool saw_space = false; 373 + 374 + while (*cursor < len && src[*cursor] != '\n' && src[*cursor] != '\r') { 375 + if (src[*cursor] == '#') { 376 + if (*cursor == value_start || saw_space) goto done; 377 + } 378 + 379 + saw_space = util_env_is_inline_ws(src[*cursor]); 380 + (*cursor)++; 381 + value_end = *cursor; 382 + } 383 + 384 + done: 385 + while (value_start < value_end && util_env_is_inline_ws(src[value_start])) { 386 + value_start++; 387 + } 388 + while (value_end > value_start && util_env_is_inline_ws(src[value_end - 1])) { 389 + value_end--; 390 + } 391 + 392 + return js_mkstr(js, src + value_start, value_end - value_start); 393 + } 394 + 395 + static ant_value_t util_parse_env(ant_t *js, ant_value_t *args, int nargs) { 396 + ant_value_t out = js_mkobj(js); 397 + if (nargs < 1) return out; 398 + 399 + char cbuf[512]; 400 + js_cstr_t cstr = js_to_cstr(js, args[0], cbuf, sizeof(cbuf)); 401 + const char *src = cstr.ptr; 402 + size_t len = strlen(src); 403 + size_t i = 0; 404 + 405 + if (len >= 3 && 406 + (unsigned char)src[0] == 0xEF && 407 + (unsigned char)src[1] == 0xBB && 408 + (unsigned char)src[2] == 0xBF) { 409 + i = 3; 410 + } 411 + 412 + while (i < len) { 413 + size_t key_start = 0; 414 + size_t key_end = 0; 415 + ant_value_t value = js_mkstr(js, "", 0); 416 + 417 + util_env_skip_inline_ws(src, len, &i); 418 + if (i >= len) break; 419 + if (src[i] == '\n') { 420 + i++; 421 + continue; 422 + } 423 + if (src[i] == '#') goto skip_line; 424 + 425 + util_env_consume_export(src, len, &i); 426 + if (!util_env_parse_key(src, len, &i, &key_start, &key_end)) goto skip_line; 427 + 428 + util_env_skip_inline_ws(src, len, &i); 429 + if (i >= len || src[i] != '=') goto skip_line; 430 + i++; 431 + util_env_skip_inline_ws(src, len, &i); 432 + 433 + if (i < len && (src[i] == '"' || src[i] == '\'' || src[i] == '`')) { 434 + value = util_env_parse_quoted_value(js, src, len, &i); 435 + } else { 436 + value = util_env_parse_unquoted_value(js, src, len, &i); 437 + } 438 + 439 + util_env_set_entry(js, out, src + key_start, key_end - key_start, value); 440 + 441 + skip_line: 442 + util_env_skip_line(src, len, &i); 443 + } 444 + 445 + if (cstr.needs_free) free((void *)cstr.ptr); 446 + return out; 447 + } 448 + 257 449 static const util_style_entry_t *util_find_style(const char *name) { 258 450 for (size_t i = 0; i < sizeof(util_styles) / sizeof(util_styles[0]); i++) { 259 451 if (strcmp(name, util_styles[i].name) == 0) return &util_styles[i]; ··· 416 608 return js_heavy_mkfun(js, util_promisified_call, args[0]); 417 609 } 418 610 611 + static ant_value_t util_inherits(ant_t *js, ant_value_t *args, int nargs) { 612 + if (nargs < 2 || !is_callable(args[0]) || !is_callable(args[1])) { 613 + return js_mkerr(js, "inherits(ctor, superCtor) requires constructor functions"); 614 + } 615 + 616 + ant_value_t ctor = args[0]; 617 + ant_value_t super_ctor = args[1]; 618 + ant_value_t ctor_proto = js_get(js, ctor, "prototype"); 619 + ant_value_t super_proto = js_get(js, super_ctor, "prototype"); 620 + 621 + if (!is_object_type(ctor_proto) || !is_object_type(super_proto)) { 622 + return js_mkerr(js, "inherits(ctor, superCtor) requires prototype objects"); 623 + } 624 + 625 + js_set(js, ctor, "super_", super_ctor); 626 + js_set_proto_init(ctor_proto, super_proto); 627 + 628 + return js_mkundef(); 629 + } 630 + 419 631 ant_value_t util_library(ant_t *js) { 420 632 ant_value_t lib = js_mkobj(js); 421 633 ··· 423 635 js_set(js, lib, "formatWithOptions", js_mkfun(util_format_with_options)); 424 636 js_set(js, lib, "inspect", js_mkfun(util_inspect)); 425 637 js_set(js, lib, "deprecate", js_mkfun(util_deprecate)); 638 + js_set(js, lib, "inherits", js_mkfun(util_inherits)); 639 + js_set(js, lib, "parseEnv", js_mkfun(util_parse_env)); 426 640 js_set(js, lib, "promisify", js_mkfun(util_promisify)); 427 641 js_set(js, lib, "stripVTControlCharacters", js_mkfun(util_strip_vt_control_characters)); 428 642 js_set(js, lib, "styleText", js_mkfun(util_style_text));
+202
src/modules/wasi.c
··· 1 + #include <stdio.h> 2 + #include <stdlib.h> 3 + #include <string.h> 4 + 5 + #include "ant.h" 6 + #include "errors.h" 7 + #include "internal.h" 8 + 9 + #include "ptr.h" 10 + #include "modules/buffer.h" 11 + #include "modules/wasi.h" 12 + #include "wasm_c_api.h" 13 + #include "wasm_export.h" 14 + 15 + enum { 16 + WASI_INSTANCE_TAG = 0x57415349u, // WASI 17 + WASI_FUNC_TAG = 0x57415346u, // WASF 18 + }; 19 + 20 + typedef struct { 21 + uint8_t *binary; 22 + wasm_module_t module; 23 + wasm_module_inst_t inst; 24 + wasm_exec_env_t exec_env; 25 + } wasi_instance_handle_t; 26 + 27 + typedef struct { 28 + ant_t *js; 29 + wasm_module_inst_t inst; 30 + wasm_exec_env_t exec_env; 31 + wasm_function_inst_t func; 32 + } wasi_func_env_t; 33 + 34 + static ant_value_t wasi_exported_func_call(ant_t *js, ant_value_t *args, int nargs) { 35 + if (!js_check_native_tag(js->current_func, WASI_FUNC_TAG)) 36 + return js_mkerr(js, "Invalid WASI function"); 37 + wasi_func_env_t *env = (wasi_func_env_t *)js_get_native_ptr(js->current_func); 38 + 39 + uint32_t param_count = wasm_func_get_param_count(env->func, env->inst); 40 + uint32_t result_count = wasm_func_get_result_count(env->func, env->inst); 41 + 42 + uint32_t wasm_argv[32]; 43 + memset(wasm_argv, 0, sizeof(wasm_argv)); 44 + 45 + for (int i = 0; i < nargs && (uint32_t)i < param_count; i++) { 46 + wasm_argv[i] = (uint32_t)js_getnum(args[i]); 47 + } 48 + 49 + if (!wasm_runtime_call_wasm(env->exec_env, env->func, param_count, wasm_argv)) { 50 + const char *exception = wasm_runtime_get_exception(env->inst); 51 + return js_mkerr(js, "%s", exception ? exception : "WASI function call failed"); 52 + } 53 + 54 + if (result_count == 0) return js_mkundef(); 55 + return js_mknum((double)(int32_t)wasm_argv[0]); 56 + } 57 + 58 + static void wasi_instance_finalize(ant_t *js, ant_object_t *obj) { 59 + if (obj->native.tag != WASI_INSTANCE_TAG) return; 60 + wasi_instance_handle_t *handle = (wasi_instance_handle_t *)obj->native.ptr; 61 + 62 + if (!handle) return; 63 + if (handle->exec_env) wasm_runtime_destroy_exec_env(handle->exec_env); 64 + if (handle->inst) wasm_runtime_deinstantiate(handle->inst); 65 + if (handle->module) wasm_runtime_unload(handle->module); 66 + 67 + free(handle->binary); 68 + free(handle); 69 + } 70 + 71 + bool wasi_module_has_wasi_imports(void *c_api_module) { 72 + wasm_importtype_vec_t import_types = {0}; 73 + wasm_module_imports((wasm_module_t *)c_api_module, &import_types); 74 + 75 + bool has_wasi = false; 76 + for (size_t i = 0; i < import_types.size; i++) { 77 + const wasm_name_t *mod = wasm_importtype_module(import_types.data[i]); 78 + if (mod && mod->size >= 22 && memcmp(mod->data, "wasi_snapshot_preview1", 22) == 0) { 79 + has_wasi = true; 80 + break; 81 + }} 82 + 83 + wasm_importtype_vec_delete(&import_types); 84 + return has_wasi; 85 + } 86 + 87 + ant_value_t wasi_instantiate( 88 + ant_t *js, const uint8_t *wasm_bytes, size_t wasm_len, 89 + ant_value_t module_obj, ant_value_t wasi_opts 90 + ) { 91 + char error_buf[128] = {0}; 92 + uint8_t *bin_copy = malloc(wasm_len); 93 + 94 + if (!bin_copy) return js_mkerr(js, "out of memory"); 95 + memcpy(bin_copy, wasm_bytes, wasm_len); 96 + 97 + wasm_module_t rt_module = wasm_runtime_load(bin_copy, (uint32_t)wasm_len, error_buf, sizeof(error_buf)); 98 + if (!rt_module) { 99 + free(bin_copy); 100 + return js_mkerr(js, "%s", error_buf[0] ? error_buf : "Failed to load WASI module"); 101 + } 102 + 103 + const char *dirs[] = { "." }; 104 + ant_value_t args_val = is_object_type(wasi_opts) 105 + ? js_get(js, wasi_opts, "args") 106 + : js_mkundef(); 107 + 108 + int argc = vtype(args_val) == T_ARR ? (int)js_arr_len(js, args_val) : 0; 109 + if (argc < 1) argc = 1; 110 + 111 + char *argv[argc]; 112 + if (vtype(args_val) == T_ARR) { 113 + for (int i = 0; i < argc; i++) 114 + argv[i] = js_getstr(js, js_arr_get(js, args_val, (ant_offset_t)i), NULL); 115 + } else argv[0] = (char *)"wasi"; 116 + 117 + wasm_runtime_set_wasi_args(rt_module, dirs, 1, NULL, 0, NULL, 0, argv, argc); 118 + wasm_module_inst_t inst = wasm_runtime_instantiate(rt_module, 512 * 1024, 256 * 1024, error_buf, sizeof(error_buf)); 119 + 120 + if (!inst) { 121 + wasm_runtime_unload(rt_module); 122 + free(bin_copy); 123 + return js_mkerr(js, "%s", error_buf[0] ? error_buf : "Failed to instantiate WASI module"); 124 + } 125 + 126 + wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(inst, 512 * 1024); 127 + if (!exec_env) { 128 + wasm_runtime_deinstantiate(inst); 129 + wasm_runtime_unload(rt_module); 130 + free(bin_copy); 131 + return js_mkerr(js, "Failed to create WASI exec env"); 132 + } 133 + 134 + wasi_instance_handle_t *handle = calloc(1, sizeof(*handle)); 135 + if (!handle) { 136 + wasm_runtime_destroy_exec_env(exec_env); 137 + wasm_runtime_deinstantiate(inst); 138 + wasm_runtime_unload(rt_module); 139 + free(bin_copy); 140 + return js_mkerr(js, "out of memory"); 141 + } 142 + 143 + handle->binary = bin_copy; 144 + handle->module = rt_module; 145 + handle->inst = inst; 146 + handle->exec_env = exec_env; 147 + 148 + ant_value_t instance_obj = js_mkobj(js); 149 + ant_value_t exports_obj = js_mkobj(js); 150 + 151 + js_set_native_ptr(instance_obj, handle); 152 + js_set_native_tag(instance_obj, WASI_INSTANCE_TAG); 153 + js_set_slot_wb(js, instance_obj, SLOT_CTOR, module_obj); 154 + js_set_finalizer(instance_obj, wasi_instance_finalize); 155 + 156 + int32_t export_count = wasm_runtime_get_export_count(rt_module); 157 + for (int32_t i = 0; i < export_count; i++) { 158 + wasm_export_t export_info; 159 + wasm_runtime_get_export_type(rt_module, i, &export_info); 160 + 161 + if (export_info.kind == WASM_IMPORT_EXPORT_KIND_FUNC) { 162 + wasm_function_inst_t func = wasm_runtime_lookup_function(inst, export_info.name); 163 + if (!func) continue; 164 + 165 + wasi_func_env_t *fenv = calloc(1, sizeof(*fenv)); 166 + if (!fenv) continue; 167 + fenv->js = js; 168 + fenv->inst = inst; 169 + fenv->exec_env = exec_env; 170 + fenv->func = func; 171 + 172 + ant_value_t obj = js_mkobj(js); 173 + js_set_slot(obj, SLOT_CFUNC, js_mkfun(wasi_exported_func_call)); 174 + js_set_native_ptr(obj, fenv); 175 + js_set_native_tag(obj, WASI_FUNC_TAG); 176 + js_set(js, exports_obj, export_info.name, js_obj_to_func(obj)); 177 + } 178 + else if (export_info.kind == WASM_IMPORT_EXPORT_KIND_MEMORY) { 179 + void *mem_data = wasm_runtime_addr_app_to_native(inst, 0); 180 + wasm_memory_inst_t mem = wasm_runtime_get_default_memory(inst); 181 + uint64_t pages = mem ? wasm_memory_get_cur_page_count(mem) : 0; 182 + size_t mem_size = (size_t)(pages * 65536); 183 + 184 + ArrayBufferData *buffer = calloc(1, sizeof(ArrayBufferData)); 185 + if (buffer && mem_data) { 186 + buffer->data = (uint8_t *)mem_data; 187 + buffer->length = mem_size; 188 + buffer->capacity = mem_size; 189 + buffer->ref_count = 1; 190 + ant_value_t ab = create_arraybuffer_obj(js, buffer); 191 + ant_value_t mem_obj = js_mkobj(js); 192 + js_set_slot_wb(js, mem_obj, SLOT_DATA, ab); 193 + js_set(js, exports_obj, export_info.name, mem_obj); 194 + } 195 + } 196 + } 197 + 198 + js_set_slot_wb(js, instance_obj, SLOT_ENTRIES, exports_obj); 199 + js_set(js, instance_obj, "exports", exports_obj); 200 + 201 + return instance_obj; 202 + }
+1265
src/modules/wasm.c
··· 1 + #include <stdio.h> 2 + #include <stdlib.h> 3 + #include <string.h> 4 + #include <inttypes.h> 5 + 6 + #include "ant.h" 7 + #include "errors.h" 8 + #include "runtime.h" 9 + #include "internal.h" 10 + #include "descriptors.h" 11 + 12 + #include "gc/modules.h" 13 + #include "silver/engine.h" 14 + #include "modules/buffer.h" 15 + #include "modules/wasm.h" 16 + #include "modules/wasi.h" 17 + #include "wasm_c_api.h" 18 + 19 + typedef struct { 20 + wasm_store_t *store; 21 + wasm_module_t *module; 22 + } wasm_module_handle_t; 23 + 24 + typedef struct { 25 + wasm_instance_t *instance; 26 + wasm_extern_vec_t exports; 27 + wasm_func_t **host_funcs; 28 + size_t host_func_count; 29 + } wasm_instance_handle_t; 30 + 31 + typedef enum { 32 + WASM_EXTERN_WRAP_GLOBAL = 1, 33 + WASM_EXTERN_WRAP_MEMORY, 34 + WASM_EXTERN_WRAP_TABLE, 35 + } wasm_extern_wrap_kind_t; 36 + 37 + typedef struct { 38 + wasm_extern_wrap_kind_t kind; 39 + wasm_store_t *store; 40 + bool own_handle; 41 + bool use_cached_value; 42 + wasm_val_t cached_value; 43 + union { 44 + wasm_global_t *global; 45 + wasm_memory_t *memory; 46 + wasm_table_t *table; 47 + } as; 48 + } wasm_extern_handle_t; 49 + 50 + typedef struct { 51 + ant_t *js; 52 + wasm_store_t *store; 53 + ant_value_t owner; 54 + ant_value_t fn; 55 + } wasm_import_func_env_t; 56 + 57 + static wasm_engine_t *g_wasm_engine = NULL; 58 + 59 + static wasm_import_func_env_t **g_wasm_import_envs = NULL; 60 + static size_t g_wasm_import_env_count = 0; 61 + static size_t g_wasm_import_env_cap = 0; 62 + 63 + static void wasm_register_import_env(wasm_import_func_env_t *env) { 64 + if (g_wasm_import_env_count == g_wasm_import_env_cap) { 65 + size_t new_cap = g_wasm_import_env_cap ? g_wasm_import_env_cap * 2 : 16; 66 + wasm_import_func_env_t **new_arr = realloc(g_wasm_import_envs, new_cap * sizeof(*new_arr)); 67 + if (!new_arr) return; 68 + g_wasm_import_envs = new_arr; 69 + g_wasm_import_env_cap = new_cap; 70 + } 71 + g_wasm_import_envs[g_wasm_import_env_count++] = env; 72 + } 73 + 74 + static void wasm_unregister_import_env(wasm_import_func_env_t *env) { 75 + for (size_t i = 0; i < g_wasm_import_env_count; i++) { 76 + if (g_wasm_import_envs[i] != env) continue; 77 + g_wasm_import_envs[i] = g_wasm_import_envs[--g_wasm_import_env_count]; 78 + return; 79 + } 80 + } 81 + 82 + static void wasm_import_func_env_finalizer(void *env_ptr) { 83 + wasm_import_func_env_t *env = (wasm_import_func_env_t *)env_ptr; 84 + wasm_unregister_import_env(env); 85 + free(env); 86 + } 87 + 88 + static ant_value_t g_wasm_module_proto = 0; 89 + static ant_value_t g_wasm_instance_proto = 0; 90 + static ant_value_t g_wasm_global_proto = 0; 91 + static ant_value_t g_wasm_memory_proto = 0; 92 + static ant_value_t g_wasm_table_proto = 0; 93 + static ant_value_t g_wasm_tag_proto = 0; 94 + static ant_value_t g_wasm_exception_proto = 0; 95 + 96 + static ant_value_t g_wasm_compileerror_proto = 0; 97 + static ant_value_t g_wasm_linkerror_proto = 0; 98 + static ant_value_t g_wasm_runtimeerror_proto = 0; 99 + 100 + static bool ensure_wasm_engine(void) { 101 + if (g_wasm_engine) return true; 102 + g_wasm_engine = wasm_engine_new(); 103 + return g_wasm_engine != NULL; 104 + } 105 + 106 + static size_t wasm_name_len(const wasm_name_t *name) { 107 + if (!name || !name->data) return 0; 108 + if (name->size > 0 && name->data[name->size - 1] == '\0') return name->size - 1; 109 + return name->size; 110 + } 111 + 112 + static const char *wasm_extern_kind_name(wasm_externkind_t kind) { 113 + switch (kind) { 114 + case WASM_EXTERN_FUNC: return "function"; 115 + case WASM_EXTERN_GLOBAL: return "global"; 116 + case WASM_EXTERN_TABLE: return "table"; 117 + case WASM_EXTERN_MEMORY: return "memory"; 118 + default: return "unknown"; 119 + } 120 + } 121 + 122 + static wasm_valkind_t wasm_valkind_from_string(const char *name, size_t len, bool *ok) { 123 + *ok = true; 124 + if (len == 3 && !memcmp(name, "i32", 3)) return WASM_I32; 125 + if (len == 3 && !memcmp(name, "i64", 3)) return WASM_I64; 126 + if (len == 3 && !memcmp(name, "f32", 3)) return WASM_F32; 127 + if (len == 3 && !memcmp(name, "f64", 3)) return WASM_F64; 128 + if (len == 9 && !memcmp(name, "externref", 9)) return WASM_EXTERNREF; 129 + if (len == 7 && !memcmp(name, "funcref", 7)) return WASM_FUNCREF; 130 + *ok = false; 131 + return WASM_I32; 132 + } 133 + 134 + static wasm_module_handle_t *wasm_module_handle(ant_value_t value) { 135 + if (!js_check_brand(value, BRAND_WASM_MODULE)) return NULL; 136 + ant_value_t slot = js_get_slot(value, SLOT_DATA); 137 + if (vtype(slot) != T_NUM) return NULL; 138 + return (wasm_module_handle_t *)(uintptr_t)(size_t)js_getnum(slot); 139 + } 140 + 141 + static wasm_instance_handle_t *wasm_instance_handle(ant_value_t value) { 142 + if (!js_check_brand(value, BRAND_WASM_INSTANCE)) return NULL; 143 + ant_value_t slot = js_get_slot(value, SLOT_DATA); 144 + if (vtype(slot) != T_NUM) return NULL; 145 + return (wasm_instance_handle_t *)(uintptr_t)(size_t)js_getnum(slot); 146 + } 147 + 148 + static wasm_extern_handle_t *wasm_extern_handle(ant_value_t value, wasm_extern_wrap_kind_t kind) { 149 + if ((kind == WASM_EXTERN_WRAP_GLOBAL && !js_check_brand(value, BRAND_WASM_GLOBAL)) 150 + || (kind == WASM_EXTERN_WRAP_MEMORY && !js_check_brand(value, BRAND_WASM_MEMORY)) 151 + || (kind == WASM_EXTERN_WRAP_TABLE && !js_check_brand(value, BRAND_WASM_TABLE))) { 152 + return NULL; 153 + } 154 + 155 + ant_value_t slot = js_get_slot(value, SLOT_DATA); 156 + if (vtype(slot) != T_NUM) return NULL; 157 + wasm_extern_handle_t *handle = (wasm_extern_handle_t *)(uintptr_t)(size_t)js_getnum(slot); 158 + return handle && handle->kind == kind ? handle : NULL; 159 + } 160 + 161 + static ant_value_t wasm_make_error(ant_t *js, ant_value_t proto, const char *name, const char *message) { 162 + ant_value_t err = js_make_error_silent(js, JS_ERR_TYPE, message ? message : ""); 163 + if (vtype(err) != T_OBJ) return err; 164 + js_set(js, err, "name", js_mkstr(js, name, strlen(name))); 165 + if (is_object_type(proto)) js_set_proto_init(err, proto); 166 + return err; 167 + } 168 + 169 + static ant_value_t wasm_make_compile_error(ant_t *js, const char *message) { 170 + return wasm_make_error(js, g_wasm_compileerror_proto, "CompileError", message); 171 + } 172 + 173 + static ant_value_t wasm_make_link_error(ant_t *js, const char *message) { 174 + return wasm_make_error(js, g_wasm_linkerror_proto, "LinkError", message); 175 + } 176 + 177 + static ant_value_t wasm_make_runtime_error(ant_t *js, const char *message) { 178 + return wasm_make_error(js, g_wasm_runtimeerror_proto, "RuntimeError", message); 179 + } 180 + 181 + static ant_value_t wasm_error_value(ant_t *js, ant_value_t value) { 182 + if (is_err(value) && js->thrown_exists) 183 + return js->thrown_value; 184 + return value; 185 + } 186 + 187 + static void wasm_reject_with_error(ant_t *js, ant_value_t promise, ant_value_t error) { 188 + js_reject_promise(js, promise, error); 189 + } 190 + 191 + static bool wasm_buffer_source_to_vec(ant_t *js, ant_value_t value, wasm_byte_vec_t *out, char *error_buf, size_t error_buf_len) { 192 + const uint8_t *bytes = NULL; 193 + size_t len = 0; 194 + memset(out, 0, sizeof(*out)); 195 + 196 + if (!buffer_source_get_bytes(js, value, &bytes, &len)) { 197 + snprintf(error_buf, error_buf_len, "Expected a BufferSource"); 198 + return false; 199 + } 200 + 201 + wasm_byte_vec_new_uninitialized(out, len); 202 + if (len > 0 && !out->data) { 203 + snprintf(error_buf, error_buf_len, "Out of memory"); 204 + return false; 205 + } 206 + 207 + if (len > 0) memcpy(out->data, bytes, len); 208 + return true; 209 + } 210 + 211 + static ant_value_t wasm_value_from_i64(ant_t *js, int64_t value) { 212 + char buf[64]; 213 + int n = snprintf(buf, sizeof(buf), "%" PRId64, value); 214 + if (n < 0) return js_mkerr(js, "Failed to convert i64"); 215 + return js_mkbigint(js, buf, (size_t)n, value < 0); 216 + } 217 + 218 + static ant_value_t wasm_value_to_js(ant_t *js, const wasm_val_t *value) { 219 + switch (value->kind) { 220 + case WASM_I32: return js_mknum((double)value->of.i32); 221 + case WASM_I64: return wasm_value_from_i64(js, value->of.i64); 222 + case WASM_F32: return js_mknum((double)value->of.f32); 223 + case WASM_F64: return js_mknum(value->of.f64); 224 + case WASM_EXTERNREF: 225 + case WASM_FUNCREF: 226 + return value->of.ref ? js_mkundef() : js_mknull(); 227 + default: 228 + return js_mkundef(); 229 + } 230 + } 231 + 232 + static bool js_value_to_i64(ant_t *js, ant_value_t value, int64_t *out) { 233 + if (vtype(value) == T_BIGINT) { 234 + ant_value_t str_val = js_tostring_val(js, value); 235 + if (is_err(str_val) || vtype(str_val) != T_STR) return false; 236 + const char *str = js_str(js, str_val); 237 + if (!str) return false; 238 + char *end = NULL; 239 + long long parsed = strtoll(str, &end, 10); 240 + if (!end || *end != '\0') return false; 241 + *out = (int64_t)parsed; 242 + return true; 243 + } 244 + 245 + double d = js_to_number(js, value); 246 + if (!isfinite(d)) return false; 247 + *out = (int64_t)d; 248 + return true; 249 + } 250 + 251 + static bool js_value_to_wasm(ant_t *js, ant_value_t value, wasm_valkind_t kind, wasm_val_t *out) { 252 + memset(out, 0, sizeof(*out)); 253 + out->kind = kind; 254 + 255 + switch (kind) { 256 + case WASM_I32: 257 + out->of.i32 = (int32_t)js_to_number(js, value); 258 + return true; 259 + case WASM_I64: 260 + return js_value_to_i64(js, value, &out->of.i64); 261 + case WASM_F32: 262 + out->of.f32 = (float)js_to_number(js, value); 263 + return true; 264 + case WASM_F64: 265 + out->of.f64 = js_to_number(js, value); 266 + return true; 267 + case WASM_EXTERNREF: 268 + case WASM_FUNCREF: 269 + out->of.ref = NULL; 270 + return vtype(value) == T_NULL || vtype(value) == T_UNDEF; 271 + default: 272 + return false; 273 + } 274 + } 275 + 276 + static ant_value_t wasm_js_from_result_vec(ant_t *js, wasm_val_vec_t *results) { 277 + if (!results || results->size == 0) return js_mkundef(); 278 + if (results->size == 1) return wasm_value_to_js(js, &results->data[0]); 279 + 280 + ant_value_t arr = js_mkarr(js); 281 + for (size_t i = 0; i < results->size; i++) { 282 + js_arr_push(js, arr, wasm_value_to_js(js, &results->data[i])); 283 + } 284 + return arr; 285 + } 286 + 287 + static ant_value_t wasm_trap_to_error(ant_t *js, wasm_trap_t *trap) { 288 + wasm_message_t message = WASM_EMPTY_VEC; 289 + wasm_trap_message(trap, &message); 290 + 291 + ant_value_t err = wasm_make_runtime_error(js, message.data ? message.data : "WebAssembly trap"); 292 + 293 + wasm_byte_vec_delete(&message); 294 + wasm_trap_delete(trap); 295 + return err; 296 + } 297 + 298 + static ant_value_t wasm_wrap_module(ant_t *js, wasm_store_t *store, wasm_module_t *module) { 299 + wasm_module_handle_t *handle = calloc(1, sizeof(*handle)); 300 + if (!handle) { 301 + if (module) wasm_module_delete(module); 302 + if (store) wasm_store_delete(store); 303 + return js_mkerr(js, "out of memory"); 304 + } 305 + 306 + handle->store = store; 307 + handle->module = module; 308 + 309 + ant_value_t obj = js_mkobj(js); 310 + js_set_proto_init(obj, g_wasm_module_proto); 311 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_WASM_MODULE)); 312 + js_set_slot(obj, SLOT_DATA, ANT_PTR(handle)); 313 + return obj; 314 + } 315 + 316 + static void wasm_module_finalize(ant_t *js, ant_object_t *obj) { 317 + (void)js; 318 + if (!obj->extra_slots) return; 319 + 320 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 321 + for (uint8_t i = 0; i < obj->extra_count; i++) { 322 + if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue; 323 + wasm_module_handle_t *handle = (wasm_module_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 324 + if (!handle) return; 325 + if (handle->module) wasm_module_delete(handle->module); 326 + if (handle->store) wasm_store_delete(handle->store); 327 + free(handle); 328 + return; 329 + } 330 + } 331 + 332 + static ant_value_t wasm_wrap_instance(ant_t *js, wasm_instance_handle_t *handle, ant_value_t module_ref) { 333 + ant_value_t obj = js_mkobj(js); 334 + js_set_proto_init(obj, g_wasm_instance_proto); 335 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_WASM_INSTANCE)); 336 + js_set_slot(obj, SLOT_DATA, ANT_PTR(handle)); 337 + js_set_slot_wb(js, obj, SLOT_CTOR, module_ref); 338 + return obj; 339 + } 340 + 341 + static void wasm_instance_finalize(ant_t *js, ant_object_t *obj) { 342 + (void)js; 343 + if (!obj->extra_slots) return; 344 + 345 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 346 + for (uint8_t i = 0; i < obj->extra_count; i++) { 347 + if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue; 348 + wasm_instance_handle_t *handle = (wasm_instance_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 349 + if (!handle) return; 350 + for (size_t j = 0; j < handle->host_func_count; j++) { 351 + if (handle->host_funcs[j]) wasm_func_delete(handle->host_funcs[j]); 352 + } 353 + free(handle->host_funcs); 354 + wasm_extern_vec_delete(&handle->exports); 355 + free(handle); 356 + return; 357 + } 358 + } 359 + 360 + static ant_value_t wasm_wrap_extern_object(ant_t *js, wasm_extern_wrap_kind_t kind, ant_value_t proto, int brand, wasm_store_t *store, bool own_handle, void *ptr, ant_value_t owner) { 361 + wasm_extern_handle_t *handle = calloc(1, sizeof(*handle)); 362 + if (!handle) return js_mkerr(js, "out of memory"); 363 + 364 + handle->kind = kind; 365 + handle->store = store; 366 + handle->own_handle = own_handle; 367 + handle->cached_value = (wasm_val_t)WASM_INIT_VAL; 368 + 369 + switch (kind) { 370 + case WASM_EXTERN_WRAP_GLOBAL: handle->as.global = (wasm_global_t *)ptr; break; 371 + case WASM_EXTERN_WRAP_MEMORY: handle->as.memory = (wasm_memory_t *)ptr; break; 372 + case WASM_EXTERN_WRAP_TABLE: handle->as.table = (wasm_table_t *)ptr; break; 373 + } 374 + 375 + ant_value_t obj = js_mkobj(js); 376 + js_set_proto_init(obj, proto); 377 + js_set_slot(obj, SLOT_BRAND, js_mknum(brand)); 378 + js_set_slot(obj, SLOT_DATA, ANT_PTR(handle)); 379 + if (is_object_type(owner)) js_set_slot_wb(js, obj, SLOT_ENTRIES, owner); 380 + return obj; 381 + } 382 + 383 + static void wasm_extern_finalize(ant_t *js, ant_object_t *obj) { 384 + (void)js; 385 + if (!obj->extra_slots) return; 386 + 387 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 388 + for (uint8_t i = 0; i < obj->extra_count; i++) { 389 + if (entries[i].slot != SLOT_DATA || vtype(entries[i].value) != T_NUM) continue; 390 + wasm_extern_handle_t *handle = (wasm_extern_handle_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 391 + if (!handle) return; 392 + 393 + if (handle->own_handle) { 394 + switch (handle->kind) { 395 + case WASM_EXTERN_WRAP_GLOBAL: 396 + if (handle->as.global) wasm_global_delete(handle->as.global); 397 + break; 398 + case WASM_EXTERN_WRAP_MEMORY: 399 + if (handle->as.memory) wasm_memory_delete(handle->as.memory); 400 + break; 401 + case WASM_EXTERN_WRAP_TABLE: 402 + if (handle->as.table) wasm_table_delete(handle->as.table); 403 + break; 404 + } 405 + if (handle->store) wasm_store_delete(handle->store); 406 + } 407 + 408 + free(handle); 409 + return; 410 + } 411 + } 412 + 413 + static ant_value_t js_wasm_exported_func_call(ant_t *js, ant_value_t *args, int nargs) { 414 + ant_value_t state = js_get_slot(js->current_func, SLOT_DATA); 415 + if (vtype(state) != T_OBJ) return js_mkerr(js, "Invalid WebAssembly function"); 416 + 417 + ant_value_t func_ptr = js_get_slot(state, SLOT_DATA); 418 + if (vtype(func_ptr) != T_NUM) return js_mkerr(js, "Invalid WebAssembly function"); 419 + 420 + wasm_func_t *func = (wasm_func_t *)(uintptr_t)(size_t)js_getnum(func_ptr); 421 + if (!func) return js_mkerr(js, "Invalid WebAssembly function"); 422 + 423 + wasm_functype_t *type = wasm_func_type(func); 424 + if (!type) return js_mkerr(js, "Failed to inspect WebAssembly function"); 425 + 426 + const wasm_valtype_vec_t *params = wasm_functype_params(type); 427 + const wasm_valtype_vec_t *results_t = wasm_functype_results(type); 428 + 429 + wasm_val_vec_t wasm_args = WASM_EMPTY_VEC; 430 + wasm_val_vec_t wasm_results = WASM_EMPTY_VEC; 431 + wasm_trap_t *trap = NULL; 432 + ant_value_t result = js_mkundef(); 433 + 434 + wasm_val_vec_new_uninitialized(&wasm_args, params ? params->size : 0); 435 + wasm_val_vec_new_uninitialized(&wasm_results, results_t ? results_t->size : 0); 436 + 437 + for (size_t i = 0; params && i < params->size; i++) { 438 + if ((int)i >= nargs) { 439 + wasm_val_vec_delete(&wasm_args); 440 + wasm_val_vec_delete(&wasm_results); 441 + wasm_functype_delete(type); 442 + return js_mkerr_typed(js, JS_ERR_TYPE, "Missing WebAssembly argument"); 443 + } 444 + 445 + if (!js_value_to_wasm(js, args[i], wasm_valtype_kind(params->data[i]), &wasm_args.data[i])) { 446 + wasm_val_vec_delete(&wasm_args); 447 + wasm_val_vec_delete(&wasm_results); 448 + wasm_functype_delete(type); 449 + return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly argument type"); 450 + } 451 + } 452 + 453 + trap = wasm_func_call(func, &wasm_args, &wasm_results); 454 + if (trap) { 455 + result = wasm_trap_to_error(js, trap); 456 + wasm_val_vec_delete(&wasm_args); 457 + wasm_val_vec_delete(&wasm_results); 458 + wasm_functype_delete(type); 459 + return js_throw(js, result); 460 + } 461 + 462 + result = wasm_js_from_result_vec(js, &wasm_results); 463 + 464 + wasm_val_vec_delete(&wasm_args); 465 + wasm_val_vec_delete(&wasm_results); 466 + wasm_functype_delete(type); 467 + return result; 468 + } 469 + 470 + static ant_value_t wasm_wrap_export_value(ant_t *js, ant_value_t instance_obj, const wasm_exporttype_t *export_type, wasm_extern_t *external) { 471 + switch (wasm_extern_kind(external)) { 472 + case WASM_EXTERN_FUNC: { 473 + ant_value_t state = js_mkobj(js); 474 + js_set_slot(state, SLOT_DATA, ANT_PTR(wasm_extern_as_func(external))); 475 + js_set_slot_wb(js, state, SLOT_ENTRIES, instance_obj); 476 + return js_heavy_mkfun(js, js_wasm_exported_func_call, state); 477 + } 478 + case WASM_EXTERN_GLOBAL: { 479 + ant_value_t obj = wasm_wrap_extern_object( 480 + js, WASM_EXTERN_WRAP_GLOBAL, g_wasm_global_proto, BRAND_WASM_GLOBAL, 481 + NULL, false, wasm_extern_as_global(external), instance_obj 482 + ); 483 + if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize); 484 + return obj; 485 + } 486 + case WASM_EXTERN_MEMORY: { 487 + ant_value_t obj = wasm_wrap_extern_object( 488 + js, WASM_EXTERN_WRAP_MEMORY, g_wasm_memory_proto, BRAND_WASM_MEMORY, 489 + NULL, false, wasm_extern_as_memory(external), instance_obj 490 + ); 491 + if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize); 492 + return obj; 493 + } 494 + case WASM_EXTERN_TABLE: { 495 + ant_value_t obj = wasm_wrap_extern_object( 496 + js, WASM_EXTERN_WRAP_TABLE, g_wasm_table_proto, BRAND_WASM_TABLE, 497 + NULL, false, wasm_extern_as_table(external), instance_obj 498 + ); 499 + if (vtype(obj) == T_OBJ) js_set_finalizer(obj, wasm_extern_finalize); 500 + return obj; 501 + } 502 + default: 503 + (void)export_type; 504 + return js_mkundef(); 505 + } 506 + } 507 + 508 + static ant_value_t wasm_module_from_bytes(ant_t *js, ant_value_t value, ant_value_t *out_module) { 509 + wasm_byte_vec_t binary = WASM_EMPTY_VEC; 510 + wasm_store_t *store = NULL; 511 + wasm_module_t *module = NULL; 512 + char error_buf[128] = {0}; 513 + 514 + *out_module = js_mkundef(); 515 + 516 + if (!ensure_wasm_engine()) return js_mkerr(js, "Failed to initialize WebAssembly engine"); 517 + if (!(store = wasm_store_new(g_wasm_engine))) return js_mkerr(js, "Failed to create WebAssembly store"); 518 + 519 + if (!wasm_buffer_source_to_vec(js, value, &binary, error_buf, sizeof(error_buf))) { 520 + wasm_store_delete(store); 521 + return js_mkerr_typed(js, JS_ERR_TYPE, "%s", error_buf); 522 + } 523 + 524 + if (!wasm_module_validate(store, &binary)) { 525 + wasm_byte_vec_delete(&binary); 526 + wasm_store_delete(store); 527 + return wasm_make_compile_error(js, "Invalid WebAssembly binary"); 528 + } 529 + 530 + module = wasm_module_new(store, &binary); 531 + wasm_byte_vec_delete(&binary); 532 + if (!module) { 533 + wasm_store_delete(store); 534 + return wasm_make_compile_error(js, "Failed to compile WebAssembly module"); 535 + } 536 + 537 + *out_module = wasm_wrap_module(js, store, module); 538 + if (vtype(*out_module) == T_OBJ) { 539 + js_set_finalizer(*out_module, wasm_module_finalize); 540 + js_set_slot_wb(js, *out_module, SLOT_MAP, value); 541 + } 542 + return js_mkundef(); 543 + } 544 + 545 + static ant_value_t js_wasm_module_ctor(ant_t *js, ant_value_t *args, int nargs) { 546 + ant_value_t module = js_mkundef(); 547 + ant_value_t err; 548 + 549 + if (vtype(js->new_target) == T_UNDEF) 550 + return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module constructor requires 'new'"); 551 + if (nargs < 1) 552 + return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module requires a BufferSource"); 553 + 554 + err = wasm_module_from_bytes(js, args[0], &module); 555 + if (is_err(err)) return err; 556 + if (vtype(module) != T_OBJ) return js_throw(js, wasm_error_value(js, err)); 557 + return module; 558 + } 559 + 560 + static ant_value_t wasm_module_type_descriptors(ant_t *js, ant_value_t module_obj, bool imports) { 561 + wasm_module_handle_t *handle = wasm_module_handle(module_obj); 562 + if (!handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Module"); 563 + 564 + ant_value_t arr = js_mkarr(js); 565 + if (imports) { 566 + wasm_importtype_vec_t vec = WASM_EMPTY_VEC; 567 + wasm_module_imports(handle->module, &vec); 568 + for (size_t i = 0; i < vec.size; i++) { 569 + const wasm_importtype_t *entry = vec.data[i]; 570 + const wasm_name_t *module_name = wasm_importtype_module(entry); 571 + const wasm_name_t *name = wasm_importtype_name(entry); 572 + const wasm_externtype_t *type = wasm_importtype_type(entry); 573 + 574 + ant_value_t item = js_mkobj(js); 575 + js_set(js, item, "module", js_mkstr(js, module_name->data, wasm_name_len(module_name))); 576 + js_set(js, item, "name", js_mkstr(js, name->data, wasm_name_len(name))); 577 + js_set(js, item, "kind", js_mkstr(js, wasm_extern_kind_name(wasm_externtype_kind(type)), strlen(wasm_extern_kind_name(wasm_externtype_kind(type))))); 578 + js_arr_push(js, arr, item); 579 + } 580 + wasm_importtype_vec_delete(&vec); 581 + } else { 582 + wasm_exporttype_vec_t vec = WASM_EMPTY_VEC; 583 + wasm_module_exports(handle->module, &vec); 584 + for (size_t i = 0; i < vec.size; i++) { 585 + const wasm_exporttype_t *entry = vec.data[i]; 586 + const wasm_name_t *name = wasm_exporttype_name(entry); 587 + const wasm_externtype_t *type = wasm_exporttype_type(entry); 588 + 589 + ant_value_t item = js_mkobj(js); 590 + js_set(js, item, "name", js_mkstr(js, name->data, wasm_name_len(name))); 591 + js_set(js, item, "kind", js_mkstr(js, wasm_extern_kind_name(wasm_externtype_kind(type)), strlen(wasm_extern_kind_name(wasm_externtype_kind(type))))); 592 + js_arr_push(js, arr, item); 593 + } 594 + wasm_exporttype_vec_delete(&vec); 595 + } 596 + 597 + return arr; 598 + } 599 + 600 + static ant_value_t js_wasm_module_imports(ant_t *js, ant_value_t *args, int nargs) { 601 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module.imports requires 1 argument"); 602 + return wasm_module_type_descriptors(js, args[0], true); 603 + } 604 + 605 + static ant_value_t js_wasm_module_exports(ant_t *js, ant_value_t *args, int nargs) { 606 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Module.exports requires 1 argument"); 607 + return wasm_module_type_descriptors(js, args[0], false); 608 + } 609 + 610 + static ant_value_t js_wasm_instance_exports_getter(ant_t *js, ant_value_t *args, int nargs) { 611 + if (!js_check_brand(js->this_val, BRAND_WASM_INSTANCE)) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Instance"); 612 + return js_get_slot(js->this_val, SLOT_ENTRIES); 613 + } 614 + 615 + static ant_value_t wasm_property_get_nested(ant_t *js, ant_value_t base, const wasm_name_t *name) { 616 + if (!is_object_type(base)) return js_mkundef(); 617 + return js_getprop_fallback(js, base, name && name->data ? name->data : ""); 618 + } 619 + 620 + static wasm_trap_t *wasm_import_func_callback(void *env_ptr, const wasm_val_vec_t *args, wasm_val_vec_t *results) { 621 + wasm_import_func_env_t *env = (wasm_import_func_env_t *)env_ptr; 622 + ant_t *js = env->js; 623 + 624 + ant_value_t *js_args = NULL; 625 + ant_value_t result = js_mkundef(); 626 + wasm_message_t trap_msg = WASM_EMPTY_VEC; 627 + 628 + if (args && args->size > 0) { 629 + js_args = calloc(args->size, sizeof(*js_args)); 630 + if (!js_args) { 631 + wasm_name_new_from_string_nt(&trap_msg, "Out of memory"); 632 + wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg); 633 + wasm_byte_vec_delete(&trap_msg); 634 + return trap; 635 + } 636 + } 637 + 638 + for (size_t i = 0; args && i < args->size; i++) { 639 + js_args[i] = wasm_value_to_js(js, &args->data[i]); 640 + } 641 + 642 + result = sv_vm_call(js->vm, js, env->fn, js_mkundef(), js_args, args ? (int)args->size : 0, NULL, false); 643 + free(js_args); 644 + 645 + if (is_err(result)) { 646 + const char *msg = "WebAssembly import threw"; 647 + if (vtype(js->thrown_value) == T_OBJ) { 648 + const char *message = get_str_prop(js, js->thrown_value, "message", 7, NULL); 649 + if (message && *message) msg = message; 650 + } 651 + wasm_name_new_from_string_nt(&trap_msg, msg); 652 + wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg); 653 + wasm_byte_vec_delete(&trap_msg); 654 + return trap; 655 + } 656 + 657 + if (results && results->size > 0) { 658 + if (results->size == 1) { 659 + if (!js_value_to_wasm(js, result, results->data[0].kind, &results->data[0])) { 660 + wasm_name_new_from_string_nt(&trap_msg, "Unsupported import return value"); 661 + wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg); 662 + wasm_byte_vec_delete(&trap_msg); 663 + return trap; 664 + } 665 + } else { 666 + if (vtype(result) != T_ARR) { 667 + wasm_name_new_from_string_nt(&trap_msg, "Expected an array for multi-value return"); 668 + wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg); 669 + wasm_byte_vec_delete(&trap_msg); 670 + return trap; 671 + } 672 + 673 + for (size_t i = 0; i < results->size; i++) { 674 + ant_value_t item = js_arr_get(js, result, (ant_offset_t)i); 675 + if (!js_value_to_wasm(js, item, results->data[i].kind, &results->data[i])) { 676 + wasm_name_new_from_string_nt(&trap_msg, "Unsupported import return value"); 677 + wasm_trap_t *trap = wasm_trap_new(env->store, &trap_msg); 678 + wasm_byte_vec_delete(&trap_msg); 679 + return trap; 680 + } 681 + } 682 + }} 683 + 684 + return NULL; 685 + } 686 + 687 + static ant_value_t wasm_instantiate_module(ant_t *js, ant_value_t module_obj, ant_value_t import_obj, ant_value_t *out_instance) { 688 + wasm_module_handle_t *module_handle = wasm_module_handle(module_obj); 689 + wasm_importtype_vec_t import_types = WASM_EMPTY_VEC; 690 + wasm_exporttype_vec_t export_types = WASM_EMPTY_VEC; 691 + 692 + wasm_extern_t **imports = NULL; 693 + wasm_func_t **owned_host_funcs = NULL; 694 + 695 + size_t owned_host_func_count = 0; 696 + wasm_extern_vec_t exports = WASM_EMPTY_VEC; 697 + 698 + wasm_trap_t *trap = NULL; 699 + wasm_instance_t *instance = NULL; 700 + ant_value_t instance_obj = js_mkundef(); 701 + ant_value_t exports_obj = js_mkobj(js); 702 + 703 + *out_instance = js_mkundef(); 704 + 705 + if (!module_handle) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Module"); 706 + 707 + if (wasi_module_has_wasi_imports(module_handle->module)) { 708 + ant_value_t wasi_opts = is_object_type(import_obj) ? js_get(js, import_obj, "wasi") : js_mkundef(); 709 + if (!is_object_type(import_obj) || is_object_type(wasi_opts)) { 710 + ant_value_t bytes_src = js_get_slot(module_obj, SLOT_MAP); 711 + wasm_byte_vec_t binary = WASM_EMPTY_VEC; 712 + char wasi_err[128] = {0}; 713 + if (!wasm_buffer_source_to_vec(js, bytes_src, &binary, wasi_err, sizeof(wasi_err))) { 714 + return js_mkerr(js, "WASI: cannot extract module bytes"); 715 + } 716 + *out_instance = wasi_instantiate(js, (const uint8_t *)binary.data, binary.size, module_obj, wasi_opts); 717 + wasm_byte_vec_delete(&binary); 718 + return is_err(*out_instance) ? *out_instance : js_mkundef(); 719 + } 720 + } 721 + 722 + wasm_module_imports(module_handle->module, &import_types); 723 + if (import_types.size > 0) { 724 + imports = calloc(import_types.size, sizeof(*imports)); 725 + owned_host_funcs = calloc(import_types.size, sizeof(*owned_host_funcs)); 726 + if (!imports || !owned_host_funcs) { 727 + free(imports); 728 + free(owned_host_funcs); 729 + wasm_importtype_vec_delete(&import_types); 730 + return js_mkerr(js, "out of memory"); 731 + } 732 + } 733 + 734 + for (size_t i = 0; i < import_types.size; i++) { 735 + const wasm_importtype_t *import_type = import_types.data[i]; 736 + const wasm_name_t *module_name = wasm_importtype_module(import_type); 737 + const wasm_name_t *field_name = wasm_importtype_name(import_type); 738 + const wasm_externtype_t *extern_type = wasm_importtype_type(import_type); 739 + 740 + ant_value_t namespace_obj = wasm_property_get_nested(js, import_obj, module_name); 741 + ant_value_t value = wasm_property_get_nested(js, namespace_obj, field_name); 742 + wasm_externkind_t kind = wasm_externtype_kind(extern_type); 743 + 744 + if (kind == WASM_EXTERN_MEMORY || kind == WASM_EXTERN_TABLE) { 745 + free(imports); 746 + free(owned_host_funcs); 747 + wasm_importtype_vec_delete(&import_types); 748 + return wasm_make_link_error(js, "The current WAMR backend does not support memory/table imports"); 749 + } 750 + 751 + if (kind == WASM_EXTERN_FUNC) { 752 + const wasm_functype_t *func_type = wasm_externtype_as_functype_const(extern_type); 753 + wasm_import_func_env_t *env = NULL; 754 + 755 + if (!is_callable(value)) { 756 + free(imports); 757 + free(owned_host_funcs); 758 + wasm_importtype_vec_delete(&import_types); 759 + return wasm_make_link_error(js, "Missing function import"); 760 + } 761 + 762 + env = calloc(1, sizeof(*env)); 763 + if (!env) { 764 + free(imports); 765 + free(owned_host_funcs); 766 + wasm_importtype_vec_delete(&import_types); 767 + return js_mkerr(js, "out of memory"); 768 + } 769 + 770 + env->js = js; 771 + env->store = module_handle->store; 772 + env->owner = module_obj; 773 + env->fn = value; 774 + wasm_register_import_env(env); 775 + 776 + 777 + owned_host_funcs[owned_host_func_count] = wasm_func_new_with_env( 778 + module_handle->store, 779 + func_type, 780 + wasm_import_func_callback, 781 + env, 782 + wasm_import_func_env_finalizer 783 + ); 784 + 785 + if (!owned_host_funcs[owned_host_func_count]) { 786 + free(env); 787 + free(imports); 788 + free(owned_host_funcs); 789 + wasm_importtype_vec_delete(&import_types); 790 + return wasm_make_link_error(js, "Failed to create function import"); 791 + } 792 + 793 + imports[i] = wasm_func_as_extern(owned_host_funcs[owned_host_func_count]); 794 + owned_host_func_count++; 795 + continue; 796 + } 797 + 798 + if (kind == WASM_EXTERN_GLOBAL) { 799 + wasm_extern_handle_t *handle = wasm_extern_handle(value, WASM_EXTERN_WRAP_GLOBAL); 800 + if (!handle || !handle->as.global) { 801 + free(imports); 802 + free(owned_host_funcs); 803 + wasm_importtype_vec_delete(&import_types); 804 + return wasm_make_link_error(js, "Missing global import"); 805 + } 806 + imports[i] = wasm_global_as_extern(handle->as.global); 807 + continue; 808 + } 809 + } 810 + 811 + { 812 + wasm_extern_vec_t import_vec = WASM_EMPTY_VEC; 813 + if (imports && import_types.size > 0) 814 + import_vec = (wasm_extern_vec_t){ import_types.size, imports, import_types.size, sizeof(*imports), NULL }; 815 + 816 + instance = wasm_instance_new(module_handle->store, module_handle->module, &import_vec, &trap); 817 + } 818 + 819 + free(imports); 820 + wasm_importtype_vec_delete(&import_types); 821 + 822 + if (!instance) { 823 + for (size_t i = 0; i < owned_host_func_count; i++) { 824 + if (owned_host_funcs[i]) wasm_func_delete(owned_host_funcs[i]); 825 + } 826 + free(owned_host_funcs); 827 + if (trap) return wasm_trap_to_error(js, trap); 828 + return wasm_make_link_error(js, "Failed to instantiate WebAssembly module"); 829 + } 830 + 831 + wasm_instance_exports(instance, &exports); 832 + wasm_module_exports(module_handle->module, &export_types); 833 + 834 + { 835 + wasm_instance_handle_t *inst_handle = calloc(1, sizeof(*inst_handle)); 836 + if (!inst_handle) { 837 + wasm_extern_vec_delete(&exports); 838 + wasm_exporttype_vec_delete(&export_types); 839 + return js_mkerr(js, "out of memory"); 840 + } 841 + inst_handle->instance = instance; 842 + inst_handle->exports = exports; 843 + inst_handle->host_funcs = owned_host_funcs; 844 + inst_handle->host_func_count = owned_host_func_count; 845 + 846 + instance_obj = wasm_wrap_instance(js, inst_handle, module_obj); 847 + } 848 + if (vtype(instance_obj) != T_OBJ) { 849 + wasm_exporttype_vec_delete(&export_types); 850 + return instance_obj; 851 + } 852 + js_set_finalizer(instance_obj, wasm_instance_finalize); 853 + 854 + for (size_t i = 0; i < export_types.size && i < exports.size; i++) { 855 + const wasm_exporttype_t *export_type = export_types.data[i]; 856 + const wasm_name_t *name = wasm_exporttype_name(export_type); 857 + ant_value_t export_value = wasm_wrap_export_value(js, instance_obj, export_type, exports.data[i]); 858 + js_setprop(js, exports_obj, js_mkstr(js, name->data, wasm_name_len(name)), export_value); 859 + } 860 + 861 + wasm_exporttype_vec_delete(&export_types); 862 + js_set_slot_wb(js, instance_obj, SLOT_ENTRIES, exports_obj); 863 + if (is_object_type(import_obj)) js_set_slot_wb(js, instance_obj, SLOT_MAP, import_obj); 864 + 865 + *out_instance = instance_obj; 866 + return js_mkundef(); 867 + } 868 + 869 + static ant_value_t js_wasm_instance_ctor(ant_t *js, ant_value_t *args, int nargs) { 870 + ant_value_t instance = js_mkundef(); 871 + ant_value_t import_obj = (nargs >= 2 && is_object_type(args[1])) ? args[1] : js_mkundef(); 872 + ant_value_t err; 873 + 874 + if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Instance constructor requires 'new'"); 875 + if (nargs < 1) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Instance requires a module"); 876 + 877 + err = wasm_instantiate_module(js, args[0], import_obj, &instance); 878 + if (is_err(err)) return err; 879 + if (vtype(instance) != T_OBJ) return js_throw(js, wasm_error_value(js, err)); 880 + 881 + return instance; 882 + } 883 + 884 + static ant_value_t js_wasm_global_value_getter(ant_t *js, ant_value_t *args, int nargs) { 885 + wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_GLOBAL); 886 + wasm_val_t value = WASM_INIT_VAL; 887 + 888 + if (!handle || !handle->as.global) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Global"); 889 + if (handle->use_cached_value) return wasm_value_to_js(js, &handle->cached_value); 890 + 891 + wasm_global_get(handle->as.global, &value); 892 + return wasm_value_to_js(js, &value); 893 + } 894 + 895 + static ant_value_t js_wasm_global_value_setter(ant_t *js, ant_value_t *args, int nargs) { 896 + wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_GLOBAL); 897 + wasm_globaltype_t *type = NULL; 898 + 899 + const wasm_valtype_t *content = NULL; 900 + wasm_val_t value = WASM_INIT_VAL; 901 + 902 + if (!handle || !handle->as.global) 903 + return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Global"); 904 + if (nargs < 1) return js_mkundef(); 905 + 906 + type = wasm_global_type(handle->as.global); 907 + if (!type) return js_mkerr(js, "Failed to inspect WebAssembly.Global"); 908 + if (wasm_globaltype_mutability(type) != WASM_VAR) { 909 + wasm_globaltype_delete(type); 910 + return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global is immutable"); 911 + } 912 + 913 + content = wasm_globaltype_content(type); 914 + if (!js_value_to_wasm(js, args[0], wasm_valtype_kind(content), &value)) { 915 + wasm_globaltype_delete(type); 916 + return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported global value"); 917 + } 918 + 919 + if (handle->use_cached_value) handle->cached_value = value; 920 + else wasm_global_set(handle->as.global, &value); 921 + wasm_globaltype_delete(type); 922 + 923 + return js_mkundef(); 924 + } 925 + 926 + static ant_value_t js_wasm_global_value_of(ant_t *js, ant_value_t *args, int nargs) { 927 + return js_wasm_global_value_getter(js, NULL, 0); 928 + } 929 + 930 + static ant_value_t js_wasm_global_ctor(ant_t *js, ant_value_t *args, int nargs) { 931 + ant_value_t descriptor; 932 + ant_value_t mutable_val; 933 + 934 + const char *value_type; 935 + ant_offset_t value_type_len = 0; 936 + bool ok = false; 937 + 938 + wasm_valkind_t kind; 939 + wasm_store_t *store = NULL; 940 + wasm_valtype_t *valtype = NULL; 941 + wasm_globaltype_t *globaltype = NULL; 942 + wasm_global_t *global = NULL; 943 + wasm_val_t initial = WASM_INIT_VAL; 944 + ant_value_t result; 945 + 946 + if (vtype(js->new_target) == T_UNDEF) 947 + return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global constructor requires 'new'"); 948 + if (nargs < 1 || !is_object_type(args[0])) 949 + return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global requires a descriptor object"); 950 + if (!ensure_wasm_engine()) 951 + return js_mkerr(js, "Failed to initialize WebAssembly engine"); 952 + 953 + descriptor = args[0]; 954 + value_type = get_str_prop(js, descriptor, "value", 5, &value_type_len); 955 + if (!value_type) 956 + return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Global descriptor requires a value type"); 957 + 958 + kind = wasm_valkind_from_string(value_type, value_type_len, &ok); 959 + if (!ok) 960 + return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly.Global value type"); 961 + 962 + if (!js_value_to_wasm(js, nargs >= 2 ? args[1] : js_mknum(0), kind, &initial)) 963 + return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported WebAssembly.Global initial value"); 964 + 965 + mutable_val = js_get(js, descriptor, "mutable"); 966 + store = wasm_store_new(g_wasm_engine); 967 + if (!store) return js_mkerr(js, "Failed to create WebAssembly store"); 968 + 969 + valtype = wasm_valtype_new(kind); 970 + globaltype = wasm_globaltype_new(valtype, js_truthy(js, mutable_val) ? WASM_VAR : WASM_CONST); 971 + global = globaltype ? wasm_global_new(store, globaltype, &initial) : NULL; 972 + 973 + wasm_globaltype_delete(globaltype); 974 + 975 + if (!global) { 976 + wasm_store_delete(store); 977 + return js_throw(js, wasm_make_runtime_error(js, "Failed to create WebAssembly.Global")); 978 + } 979 + 980 + result = wasm_wrap_extern_object( 981 + js, WASM_EXTERN_WRAP_GLOBAL, g_wasm_global_proto, BRAND_WASM_GLOBAL, 982 + store, true, global, js_mkundef() 983 + ); 984 + if (vtype(result) == T_OBJ) { 985 + wasm_extern_handle_t *handle = wasm_extern_handle(result, WASM_EXTERN_WRAP_GLOBAL); 986 + if (handle) { 987 + handle->use_cached_value = true; 988 + handle->cached_value = initial; 989 + } 990 + js_set_finalizer(result, wasm_extern_finalize); 991 + } 992 + return result; 993 + } 994 + 995 + static ant_value_t js_wasm_memory_buffer_getter(ant_t *js, ant_value_t *args, int nargs) { 996 + wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_MEMORY); 997 + byte_t *data; size_t len; ArrayBufferData *buffer; 998 + 999 + if (!handle || !handle->as.memory) 1000 + return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Memory"); 1001 + 1002 + data = wasm_memory_data(handle->as.memory); 1003 + len = wasm_memory_data_size(handle->as.memory); 1004 + buffer = calloc(1, sizeof(ArrayBufferData)); 1005 + 1006 + if (!buffer) return js_mkerr(js, "out of memory"); 1007 + buffer->data = (uint8_t *)data; 1008 + buffer->length = len; 1009 + buffer->capacity = len; 1010 + buffer->ref_count = 1; 1011 + 1012 + return create_arraybuffer_obj(js, buffer); 1013 + } 1014 + 1015 + static ant_value_t js_wasm_memory_grow(ant_t *js, ant_value_t *args, int nargs) { 1016 + return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support host-side memory.grow"); 1017 + } 1018 + 1019 + static ant_value_t js_wasm_memory_ctor(ant_t *js, ant_value_t *args, int nargs) { 1020 + if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Memory constructor requires 'new'"); 1021 + return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support standalone WebAssembly.Memory"); 1022 + } 1023 + 1024 + static ant_value_t js_wasm_table_length_getter(ant_t *js, ant_value_t *args, int nargs) { 1025 + wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE); 1026 + if (!handle || !handle->as.table) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table"); 1027 + return js_mknum((double)wasm_table_size(handle->as.table)); 1028 + } 1029 + 1030 + static ant_value_t js_wasm_table_get(ant_t *js, ant_value_t *args, int nargs) { 1031 + wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE); 1032 + wasm_ref_t *ref; 1033 + uint32_t index; 1034 + 1035 + if (!handle || !handle->as.table) 1036 + return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table"); 1037 + 1038 + index = (uint32_t)(nargs > 0 ? js_to_number(js, args[0]) : 0); 1039 + ref = wasm_table_get(handle->as.table, index); 1040 + 1041 + if (!ref) return js_mknull(); 1042 + wasm_ref_delete(ref); 1043 + 1044 + return js_mknull(); 1045 + } 1046 + 1047 + static ant_value_t js_wasm_table_set(ant_t *js, ant_value_t *args, int nargs) { 1048 + wasm_extern_handle_t *handle = wasm_extern_handle(js->this_val, WASM_EXTERN_WRAP_TABLE); 1049 + uint32_t index; 1050 + 1051 + if (!handle || !handle->as.table) return js_mkerr_typed(js, JS_ERR_TYPE, "Expected a WebAssembly.Table"); 1052 + if (nargs < 2) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table.set requires 2 arguments"); 1053 + 1054 + index = (uint32_t)js_to_number(js, args[0]); 1055 + if (!wasm_table_set(handle->as.table, index, NULL)) return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to update WebAssembly.Table"); 1056 + 1057 + return js_mkundef(); 1058 + } 1059 + 1060 + static ant_value_t js_wasm_table_grow(ant_t *js, ant_value_t *args, int nargs) { 1061 + return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support host-side table.grow"); 1062 + } 1063 + 1064 + static ant_value_t js_wasm_table_ctor(ant_t *js, ant_value_t *args, int nargs) { 1065 + if (vtype(js->new_target) == T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.Table constructor requires 'new'"); 1066 + return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not support standalone WebAssembly.Table"); 1067 + } 1068 + 1069 + static ant_value_t js_wasm_tag_ctor(ant_t *js, ant_value_t *args, int nargs) { 1070 + return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not expose WebAssembly.Tag"); 1071 + } 1072 + 1073 + static ant_value_t js_wasm_exception_ctor(ant_t *js, ant_value_t *args, int nargs) { 1074 + return js_mkerr_typed(js, JS_ERR_TYPE, "The current WAMR backend does not expose WebAssembly.Exception"); 1075 + } 1076 + 1077 + static ant_value_t js_wasm_validate(ant_t *js, ant_value_t *args, int nargs) { 1078 + wasm_byte_vec_t binary = WASM_EMPTY_VEC; 1079 + wasm_store_t *store; 1080 + 1081 + char error_buf[128] = {0}; 1082 + bool ok; 1083 + 1084 + if (nargs < 1) return js_false; 1085 + if (!ensure_wasm_engine()) return js_false; 1086 + if (!(store = wasm_store_new(g_wasm_engine))) return js_false; 1087 + 1088 + if (!wasm_buffer_source_to_vec(js, args[0], &binary, error_buf, sizeof(error_buf))) { 1089 + wasm_store_delete(store); 1090 + return js_false; 1091 + } 1092 + 1093 + ok = wasm_module_validate(store, &binary); 1094 + wasm_byte_vec_delete(&binary); 1095 + wasm_store_delete(store); 1096 + 1097 + return js_bool(ok); 1098 + } 1099 + 1100 + static ant_value_t js_wasm_compile(ant_t *js, ant_value_t *args, int nargs) { 1101 + ant_value_t promise = js_mkpromise(js); 1102 + ant_value_t module = js_mkundef(); 1103 + ant_value_t err; 1104 + 1105 + if (nargs < 1) { 1106 + wasm_reject_with_error(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.compile requires 1 argument")); 1107 + return promise; 1108 + } 1109 + 1110 + err = wasm_module_from_bytes(js, args[0], &module); 1111 + if (is_err(err) || vtype(module) != T_OBJ) { 1112 + wasm_reject_with_error(js, promise, wasm_error_value(js, err)); 1113 + return promise; 1114 + } 1115 + 1116 + js_resolve_promise(js, promise, module); 1117 + return promise; 1118 + } 1119 + 1120 + static ant_value_t js_wasm_instantiate(ant_t *js, ant_value_t *args, int nargs) { 1121 + ant_value_t promise = js_mkpromise(js); 1122 + ant_value_t module = js_mkundef(); 1123 + ant_value_t instance = js_mkundef(); 1124 + ant_value_t import_obj = (nargs >= 2 && is_object_type(args[1])) ? args[1] : js_mkundef(); 1125 + ant_value_t err; 1126 + 1127 + if (nargs < 1) { 1128 + wasm_reject_with_error(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "WebAssembly.instantiate requires 1 argument")); 1129 + return promise; 1130 + } 1131 + 1132 + if (wasm_module_handle(args[0])) { 1133 + err = wasm_instantiate_module(js, args[0], import_obj, &instance); 1134 + if (is_err(err) || vtype(instance) != T_OBJ) { 1135 + wasm_reject_with_error(js, promise, wasm_error_value(js, err)); 1136 + return promise; 1137 + } 1138 + js_resolve_promise(js, promise, instance); 1139 + return promise; 1140 + } 1141 + 1142 + err = wasm_module_from_bytes(js, args[0], &module); 1143 + if (is_err(err) || vtype(module) != T_OBJ) { 1144 + wasm_reject_with_error(js, promise, wasm_error_value(js, err)); 1145 + return promise; 1146 + } 1147 + 1148 + err = wasm_instantiate_module(js, module, import_obj, &instance); 1149 + if (is_err(err) || vtype(instance) != T_OBJ) { 1150 + wasm_reject_with_error(js, promise, wasm_error_value(js, err)); 1151 + return promise; 1152 + } 1153 + 1154 + ant_value_t result = js_mkobj(js); 1155 + js_set(js, result, "module", module); 1156 + js_set(js, result, "instance", instance); 1157 + js_resolve_promise(js, promise, result); 1158 + 1159 + return promise; 1160 + } 1161 + 1162 + static ant_value_t js_wasm_compile_error_ctor(ant_t *js, ant_value_t *args, int nargs) { 1163 + ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0); 1164 + if (is_err(msg)) return msg; 1165 + return wasm_make_error(js, g_wasm_compileerror_proto, "CompileError", js_str(js, msg)); 1166 + } 1167 + 1168 + static ant_value_t js_wasm_link_error_ctor(ant_t *js, ant_value_t *args, int nargs) { 1169 + ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0); 1170 + if (is_err(msg)) return msg; 1171 + return wasm_make_error(js, g_wasm_linkerror_proto, "LinkError", js_str(js, msg)); 1172 + } 1173 + 1174 + static ant_value_t js_wasm_runtime_error_ctor(ant_t *js, ant_value_t *args, int nargs) { 1175 + ant_value_t msg = (nargs > 0) ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0); 1176 + if (is_err(msg)) return msg; 1177 + return wasm_make_error(js, g_wasm_runtimeerror_proto, "RuntimeError", js_str(js, msg)); 1178 + } 1179 + 1180 + void gc_mark_wasm(ant_t *js, gc_mark_fn mark) { 1181 + for (size_t i = 0; i < g_wasm_import_env_count; i++) { 1182 + wasm_import_func_env_t *env = g_wasm_import_envs[i]; 1183 + mark(js, env->fn); 1184 + mark(js, env->owner); 1185 + }} 1186 + 1187 + void init_wasm_module(void) { 1188 + ant_t *js = rt->js; 1189 + ant_value_t global = js_glob(js); 1190 + 1191 + ant_value_t error_proto = js_get_ctor_proto(js, "Error", 5); 1192 + ant_value_t ns = js_mkobj(js); 1193 + 1194 + if (!ensure_wasm_engine()) return; 1195 + 1196 + g_wasm_module_proto = js_mkobj(js); 1197 + g_wasm_instance_proto = js_mkobj(js); 1198 + g_wasm_global_proto = js_mkobj(js); 1199 + g_wasm_memory_proto = js_mkobj(js); 1200 + g_wasm_table_proto = js_mkobj(js); 1201 + g_wasm_tag_proto = js_mkobj(js); 1202 + g_wasm_exception_proto = js_mkobj(js); 1203 + 1204 + g_wasm_compileerror_proto = js_mkobj(js); 1205 + g_wasm_linkerror_proto = js_mkobj(js); 1206 + g_wasm_runtimeerror_proto = js_mkobj(js); 1207 + 1208 + js_set_proto_init(g_wasm_module_proto, js_get_ctor_proto(js, "Object", 6)); 1209 + js_set_proto_init(g_wasm_instance_proto, js_get_ctor_proto(js, "Object", 6)); 1210 + js_set_proto_init(g_wasm_global_proto, js_get_ctor_proto(js, "Object", 6)); 1211 + js_set_proto_init(g_wasm_memory_proto, js_get_ctor_proto(js, "Object", 6)); 1212 + js_set_proto_init(g_wasm_table_proto, js_get_ctor_proto(js, "Object", 6)); 1213 + js_set_proto_init(g_wasm_tag_proto, js_get_ctor_proto(js, "Object", 6)); 1214 + js_set_proto_init(g_wasm_exception_proto, js_get_ctor_proto(js, "Object", 6)); 1215 + 1216 + js_set_proto_init(g_wasm_compileerror_proto, error_proto); 1217 + js_set_proto_init(g_wasm_linkerror_proto, error_proto); 1218 + js_set_proto_init(g_wasm_runtimeerror_proto, error_proto); 1219 + 1220 + js_set(js, g_wasm_global_proto, "valueOf", js_mkfun(js_wasm_global_value_of)); 1221 + js_set_getter_desc(js, g_wasm_global_proto, "value", 5, js_mkfun(js_wasm_global_value_getter), JS_DESC_C); 1222 + js_set_setter_desc(js, g_wasm_global_proto, "value", 5, js_mkfun(js_wasm_global_value_setter), JS_DESC_C); 1223 + 1224 + js_set_getter_desc(js, g_wasm_instance_proto, "exports", 7, js_mkfun(js_wasm_instance_exports_getter), JS_DESC_C); 1225 + js_set_getter_desc(js, g_wasm_memory_proto, "buffer", 6, js_mkfun(js_wasm_memory_buffer_getter), JS_DESC_C); 1226 + js_set(js, g_wasm_memory_proto, "grow", js_mkfun(js_wasm_memory_grow)); 1227 + 1228 + js_set_getter_desc(js, g_wasm_table_proto, "length", 6, js_mkfun(js_wasm_table_length_getter), JS_DESC_C); 1229 + js_set(js, g_wasm_table_proto, "get", js_mkfun(js_wasm_table_get)); 1230 + js_set(js, g_wasm_table_proto, "set", js_mkfun(js_wasm_table_set)); 1231 + js_set(js, g_wasm_table_proto, "grow", js_mkfun(js_wasm_table_grow)); 1232 + 1233 + ant_value_t module_ctor = js_make_ctor(js, js_wasm_module_ctor, g_wasm_module_proto, "Module", 6); 1234 + ant_value_t instance_ctor = js_make_ctor(js, js_wasm_instance_ctor, g_wasm_instance_proto, "Instance", 8); 1235 + ant_value_t global_ctor = js_make_ctor(js, js_wasm_global_ctor, g_wasm_global_proto, "Global", 6); 1236 + ant_value_t memory_ctor = js_make_ctor(js, js_wasm_memory_ctor, g_wasm_memory_proto, "Memory", 6); 1237 + ant_value_t table_ctor = js_make_ctor(js, js_wasm_table_ctor, g_wasm_table_proto, "Table", 5); 1238 + ant_value_t tag_ctor = js_make_ctor(js, js_wasm_tag_ctor, g_wasm_tag_proto, "Tag", 3); 1239 + ant_value_t exception_ctor = js_make_ctor(js, js_wasm_exception_ctor, g_wasm_exception_proto, "Exception", 9); 1240 + 1241 + ant_value_t compile_error_ctor = js_make_ctor(js, js_wasm_compile_error_ctor, g_wasm_compileerror_proto, "CompileError", 12); 1242 + ant_value_t link_error_ctor = js_make_ctor(js, js_wasm_link_error_ctor, g_wasm_linkerror_proto, "LinkError", 9); 1243 + ant_value_t runtime_error_ctor = js_make_ctor(js, js_wasm_runtime_error_ctor, g_wasm_runtimeerror_proto, "RuntimeError", 12); 1244 + 1245 + js_set(js, module_ctor, "imports", js_mkfun(js_wasm_module_imports)); 1246 + js_set(js, module_ctor, "exports", js_mkfun(js_wasm_module_exports)); 1247 + 1248 + js_set(js, ns, "validate", js_mkfun(js_wasm_validate)); 1249 + js_set(js, ns, "compile", js_mkfun(js_wasm_compile)); 1250 + js_set(js, ns, "instantiate", js_mkfun(js_wasm_instantiate)); 1251 + 1252 + js_set(js, ns, "Module", module_ctor); 1253 + js_set(js, ns, "Instance", instance_ctor); 1254 + js_set(js, ns, "Global", global_ctor); 1255 + js_set(js, ns, "Memory", memory_ctor); 1256 + js_set(js, ns, "Table", table_ctor); 1257 + js_set(js, ns, "Tag", tag_ctor); 1258 + js_set(js, ns, "Exception", exception_ctor); 1259 + js_set(js, ns, "CompileError", compile_error_ctor); 1260 + js_set(js, ns, "LinkError", link_error_ctor); 1261 + js_set(js, ns, "RuntimeError", runtime_error_ctor); 1262 + 1263 + js_set(js, global, "WebAssembly", ns); 1264 + js_set_descriptor(js, global, "WebAssembly", 11, JS_DESC_W | JS_DESC_C); 1265 + }
+48 -7
src/net/listener.c
··· 2 2 3 3 #ifndef _WIN32 4 4 #include <arpa/inet.h> 5 + #include <netdb.h> 5 6 #include <netinet/in.h> 7 + #include <sys/socket.h> 6 8 #include <unistd.h> 7 9 #endif 8 10 #include <stdlib.h> ··· 11 13 #include "net/connection.h" 12 14 #include "net/listener.h" 13 15 16 + static int sockaddr_from_addrinfo(const struct addrinfo *ai, int port, struct sockaddr_storage *out) { 17 + if (ai->ai_family == AF_INET) { 18 + struct sockaddr_in sa; 19 + memcpy(&sa, ai->ai_addr, sizeof(sa)); 20 + sa.sin_port = htons((uint16_t)port); 21 + memcpy(out, &sa, sizeof(sa)); 22 + return 0; 23 + } 24 + 25 + if (ai->ai_family == AF_INET6) { 26 + struct sockaddr_in6 sa6; 27 + memcpy(&sa6, ai->ai_addr, sizeof(sa6)); 28 + sa6.sin6_port = htons((uint16_t)port); 29 + memcpy(out, &sa6, sizeof(sa6)); 30 + return 0; 31 + } 32 + 33 + return UV_ENOENT; 34 + } 35 + 36 + static int resolve_hostname(const char *hostname, int port, struct sockaddr_storage *out) { 37 + struct addrinfo hints = {0}, *res = NULL; 38 + hints.ai_family = AF_UNSPEC; 39 + hints.ai_socktype = SOCK_STREAM; 40 + 41 + if (getaddrinfo(hostname, NULL, &hints, &res) != 0 || !res) return UV_ENOENT; 42 + int rc = sockaddr_from_addrinfo(res, port, out); 43 + freeaddrinfo(res); 44 + 45 + return rc; 46 + } 47 + 14 48 static void ant_listener_close_cb(uv_handle_t *handle) { 15 49 ant_listener_t *listener = (ant_listener_t *)handle->data; 16 50 if (!listener) return; ··· 54 88 ant_listener_t *listener, 55 89 uv_loop_t *loop, 56 90 const char *hostname, 57 - int port, 58 - int backlog, 91 + int port, int backlog, 59 92 uint64_t idle_timeout_ms, 60 93 const ant_listener_callbacks_t *callbacks, 61 94 void *user_data 62 95 ) { 63 - struct sockaddr_in addr; 96 + struct sockaddr_storage addr; 97 + struct sockaddr *sockaddr = (struct sockaddr *)&addr; 98 + 64 99 int rc = 0; 65 100 int addr_len = sizeof(addr); 66 101 ··· 78 113 uv_tcp_init(loop, &listener->handle.tcp); 79 114 listener->handle.tcp.data = listener; 80 115 81 - rc = uv_ip4_addr(hostname, port, &addr); 116 + memset(&addr, 0, sizeof(addr)); 117 + rc = uv_ip4_addr(hostname, port, (struct sockaddr_in *)&addr); 118 + 119 + if (rc != 0) rc = uv_ip6_addr(hostname, port, (struct sockaddr_in6 *)&addr); 120 + if (rc != 0) rc = resolve_hostname(hostname, port, &addr); 82 121 if (rc != 0) return rc; 83 122 84 - rc = uv_tcp_bind(&listener->handle.tcp, (const struct sockaddr *)&addr, 0); 123 + rc = uv_tcp_bind(&listener->handle.tcp, sockaddr, 0); 85 124 if (rc != 0) return rc; 86 125 87 126 rc = uv_listen((uv_stream_t *)&listener->handle.tcp, listener->backlog, ant_listener_accept_cb); 88 127 if (rc != 0) return rc; 89 128 90 129 listener->started = true; 91 - if (port == 0 && uv_tcp_getsockname(&listener->handle.tcp, (struct sockaddr *)&addr, &addr_len) == 0) 92 - listener->port = ntohs(addr.sin_port); 130 + if (port == 0 && uv_tcp_getsockname(&listener->handle.tcp, sockaddr, &addr_len) == 0) { 131 + if (addr.ss_family == AF_INET) listener->port = ntohs(((struct sockaddr_in *)sockaddr)->sin_port); 132 + else if (addr.ss_family == AF_INET6) listener->port = ntohs(((struct sockaddr_in6 *)sockaddr)->sin6_port); 133 + } 93 134 94 135 return 0; 95 136 }
+3 -2
src/silver/ast.c
··· 795 795 ant_offset_t flags_start = POS; 796 796 while (POS < CLEN) { 797 797 char c = CODE[POS]; 798 - if (c == 'g' || c == 'i' || c == 'm' || c == 's' || c == 'u' || c == 'y') 799 - POS++; 798 + if ( 799 + c == 'd' || c == 'g' || c == 'i' || c == 'm' || 800 + c == 's' || c == 'u' || c == 'v' || c == 'y') POS++; 800 801 else break; 801 802 } 802 803
+2
src/silver/compiler.c
··· 1113 1113 ensure_local_at_depth(c, pat->str, pat->len, is_const, c->scope_depth); 1114 1114 break; 1115 1115 case N_ASSIGN_PAT: 1116 + case N_ASSIGN: 1116 1117 hoist_lexical_pattern(c, pat->left, is_const); 1117 1118 break; 1118 1119 case N_REST: ··· 3183 3184 break; 3184 3185 } 3185 3186 case N_ASSIGN_PAT: 3187 + case N_ASSIGN: 3186 3188 for_collect_pattern_slots(c, pat->left, slots, count, cap); 3187 3189 break; 3188 3190 case N_REST:
+15 -4
src/silver/ops/literals.h
··· 86 86 vm->stack[vm->sp++] = arr; 87 87 } 88 88 89 + // TODO: reduce duplication with regex.c 89 90 static inline void sv_op_regexp(sv_vm_t *vm, ant_t *js) { 90 91 ant_value_t pattern = vm->stack[vm->sp - 2]; 91 - ant_value_t flags = vm->stack[vm->sp - 1]; 92 + ant_value_t flags = vm->stack[vm->sp - 1]; 92 93 vm->sp -= 2; 93 94 94 95 ant_value_t regexp_obj = mkobj(js, 0); 95 - 96 96 ant_value_t regexp_proto = js_get_ctor_proto(js, "RegExp", 6); 97 97 if (vtype(regexp_proto) == T_OBJ) js_set_proto_init(regexp_obj, regexp_proto); 98 98 ··· 106 106 fstr = (const char *)(uintptr_t)(foff); 107 107 } 108 108 109 - bool g = false, i = false, m = false, s = false, y = false; 109 + bool d = false, g = false, i = false, m = false; 110 + bool s = false, u = false, v = false, y = false; 111 + 110 112 for (ant_offset_t k = 0; k < flen; k++) { 113 + if (fstr[k] == 'd') d = true; 111 114 if (fstr[k] == 'g') g = true; 112 115 if (fstr[k] == 'i') i = true; 113 116 if (fstr[k] == 'm') m = true; 114 117 if (fstr[k] == 's') s = true; 118 + if (fstr[k] == 'u') u = true; 119 + if (fstr[k] == 'v') v = true; 115 120 if (fstr[k] == 'y') y = true; 116 121 } 117 122 118 - char sorted[8]; int si = 0; 123 + char sorted[10]; int si = 0; 124 + if (d) sorted[si++] = 'd'; 119 125 if (g) sorted[si++] = 'g'; 120 126 if (i) sorted[si++] = 'i'; 121 127 if (m) sorted[si++] = 'm'; 122 128 if (s) sorted[si++] = 's'; 129 + if (u) sorted[si++] = 'u'; 130 + if (v) sorted[si++] = 'v'; 123 131 if (y) sorted[si++] = 'y'; 124 132 125 133 js_mkprop_fast(js, regexp_obj, "flags", 5, js_mkstr(js, sorted, si)); 134 + js_mkprop_fast(js, regexp_obj, "hasIndices", 10, mkval(T_BOOL, d ? 1 : 0)); 126 135 js_mkprop_fast(js, regexp_obj, "global", 6, mkval(T_BOOL, g ? 1 : 0)); 127 136 js_mkprop_fast(js, regexp_obj, "ignoreCase", 10, mkval(T_BOOL, i ? 1 : 0)); 128 137 js_mkprop_fast(js, regexp_obj, "multiline", 9, mkval(T_BOOL, m ? 1 : 0)); 129 138 js_mkprop_fast(js, regexp_obj, "dotAll", 6, mkval(T_BOOL, s ? 1 : 0)); 139 + js_mkprop_fast(js, regexp_obj, "unicode", 7, mkval(T_BOOL, u ? 1 : 0)); 140 + js_mkprop_fast(js, regexp_obj, "unicodeSets", 11, mkval(T_BOOL, v ? 1 : 0)); 130 141 js_mkprop_fast(js, regexp_obj, "sticky", 6, mkval(T_BOOL, y ? 1 : 0)); 131 142 js_mkprop_fast(js, regexp_obj, "lastIndex", 9, tov(0)); 132 143
+35
src/types/modules/fs.d.ts
··· 10 10 } 11 11 12 12 type Encoding = 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'latin1' | 'binary' | 'base64' | 'base64url' | 'hex' | 'ascii'; 13 + type WatchEventType = 'rename' | 'change'; 14 + 15 + interface FSWatcher { 16 + close(): this; 17 + ref(): this; 18 + unref(): this; 19 + on(event: 'change', listener: (eventType: WatchEventType, filename?: string) => void): this; 20 + on(event: 'error', listener: (error: Error) => void): this; 21 + } 22 + 23 + interface WatchOptions { 24 + persistent?: boolean; 25 + recursive?: boolean; 26 + encoding?: Encoding | 'buffer'; 27 + } 28 + 29 + interface WatchFileOptions { 30 + persistent?: boolean; 31 + interval?: number; 32 + } 13 33 14 34 const constants: { 15 35 F_OK: number; ··· 45 65 function appendFileSync(path: string, data: string): void; 46 66 function copyFileSync(src: string, dest: string): void; 47 67 function renameSync(oldPath: string, newPath: string): void; 68 + function rm(path: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>; 48 69 function unlink(path: string): Promise<void>; 70 + function rmSync(path: string, options?: { recursive?: boolean; force?: boolean }): void; 49 71 function unlinkSync(path: string): void; 50 72 function mkdir(path: string, options?: { recursive?: boolean; mode?: number }): Promise<void>; 51 73 function mkdirSync(path: string, options?: number | { recursive?: boolean; mode?: number }): void; ··· 64 86 namespace realpathSync { 65 87 function native(path: string): string; 66 88 } 89 + function watch(path: string, listener?: (eventType: WatchEventType, filename?: string) => void): FSWatcher; 90 + function watch( 91 + path: string, 92 + options: WatchOptions | Encoding | 'buffer', 93 + listener?: (eventType: WatchEventType, filename?: string) => void 94 + ): FSWatcher; 95 + function watchFile(path: string, listener: (curr: Stats, prev: Stats) => void): void; 96 + function watchFile(path: string, options: WatchFileOptions, listener: (curr: Stats, prev: Stats) => void): void; 97 + function unwatchFile(path: string, listener?: (curr: Stats, prev: Stats) => void): void; 98 + const FSWatcher: { 99 + prototype: FSWatcher; 100 + }; 67 101 } 68 102 69 103 declare module 'ant:fs' { ··· 108 142 function writeFile(path: string, data: string | ArrayBufferView): Promise<void>; 109 143 function write(fd: number, data: string | ArrayBufferView, offset?: number, length?: number, position?: number | null): Promise<number>; 110 144 function writev(fd: number, buffers: ArrayBufferView[], position?: number): Promise<number>; 145 + function rm(path: string, options?: { recursive?: boolean; force?: boolean }): Promise<void>; 111 146 function unlink(path: string): Promise<void>; 112 147 function mkdir(path: string, options?: { recursive?: boolean; mode?: number }): Promise<void>; 113 148 function rmdir(path: string): Promise<void>;
+5 -13
src/types/modules/util.d.ts
··· 7 7 } 8 8 9 9 function format(format?: unknown, ...args: unknown[]): string; 10 - function formatWithOptions( 11 - inspectOptions: unknown, 12 - format?: unknown, 13 - ...args: unknown[] 14 - ): string; 10 + function formatWithOptions(inspectOptions: unknown, format?: unknown, ...args: unknown[]): string; 15 11 function inspect(value: unknown, options?: unknown): string; 16 - function promisify( 17 - fn: (...args: unknown[]) => unknown 18 - ): (...args: unknown[]) => Promise<unknown>; 12 + function inherits(ctor: (...args: unknown[]) => unknown, superCtor: (...args: unknown[]) => unknown): void; 13 + function parseEnv(content: string): Record<string, string>; 14 + function promisify(fn: (...args: unknown[]) => unknown): (...args: unknown[]) => Promise<unknown>; 19 15 function stripVTControlCharacters(str: string): string; 20 - function styleText( 21 - format: StyleTextFormat, 22 - text: string, 23 - options?: StyleTextOptions 24 - ): string; 16 + function styleText(format: StyleTextFormat, text: string, options?: StyleTextOptions): string; 25 17 } 26 18 27 19 declare module 'ant:util' {
+5 -3
src/utf8.c
··· 53 53 54 54 char *utf8_json_quote(const char *str, size_t byte_len, size_t *out_len) { 55 55 size_t utf16_len = utf16_strlen(str, byte_len); 56 - char *raw = NULL; 57 56 size_t raw_len = 0; 58 - size_t raw_cap = 0; 57 + size_t raw_cap = byte_len + 4; 58 + 59 + char *raw = malloc(raw_cap); 60 + if (!raw) return NULL; 59 61 60 62 if (!utf8_json_quote_append_char(&raw, &raw_len, &raw_cap, '"')) goto oom; 61 63 62 64 for (size_t i = 0; i < utf16_len; i++) { 63 65 uint32_t cu = utf16_code_unit_at(str, byte_len, i); 64 - 66 + 65 67 if (cu >= 0xD800 && cu <= 0xDBFF && i + 1 < utf16_len) { 66 68 uint32_t cu2 = utf16_code_unit_at(str, byte_len, i + 1); 67 69 if (cu2 >= 0xDC00 && cu2 <= 0xDFFF) {
+48 -8
src/watch.c
··· 44 44 fflush(stdout); 45 45 } 46 46 47 - static char *watch_resolve_path(const char *path) { 47 + char *ant_watch_resolve_path(const char *path) { 48 48 #ifdef _WIN32 49 49 char *resolved = _fullpath(NULL, path, 0); 50 50 if (resolved) return resolved; ··· 54 54 const char *resolved = realpath(path, abs_path); 55 55 return strdup(resolved ? resolved : path); 56 56 #endif 57 + } 58 + 59 + int ant_watch_start( 60 + uv_loop_t *loop, 61 + uv_fs_event_t *event, 62 + const char *path, 63 + uv_fs_event_cb callback, 64 + void *data, 65 + unsigned int flags, 66 + char **resolved_path_out 67 + ) { 68 + char *watch_path = NULL; 69 + int rc = 0; 70 + 71 + if (!loop || !event || !path || !callback) return UV_EINVAL; 72 + 73 + watch_path = ant_watch_resolve_path(path); 74 + if (!watch_path) return UV_ENOMEM; 75 + 76 + rc = uv_fs_event_init(loop, event); 77 + if (rc != 0) goto cleanup; 78 + 79 + event->data = data; 80 + rc = uv_fs_event_start(event, callback, watch_path, flags); 81 + if (rc != 0) goto cleanup; 82 + 83 + if (resolved_path_out) *resolved_path_out = watch_path; 84 + else free(watch_path); 85 + return 0; 86 + 87 + cleanup: 88 + free(watch_path); 89 + return rc; 90 + } 91 + 92 + void ant_watch_stop(uv_fs_event_t *event) { 93 + if (!event) return; 94 + uv_fs_event_stop(event); 57 95 } 58 96 59 97 static char **watch_build_child_argv(int argc, char **argv) { ··· 238 276 239 277 watch_state_t state = {0}; 240 278 state.child_argv = watch_build_child_argv(argc, argv); 241 - state.watch_path = watch_resolve_path(entry_file); 279 + state.watch_path = ant_watch_resolve_path(entry_file); 242 280 state.no_clear_screen = no_clear_screen; 243 281 state.final_exit_code = EXIT_SUCCESS; 244 282 ··· 256 294 return EXIT_FAILURE; 257 295 } 258 296 259 - rc = uv_fs_event_init(&state.loop, &state.fs_event); 260 - if (rc == 0) state.fs_event_inited = true; 297 + rc = ant_watch_start( 298 + &state.loop, 299 + &state.fs_event, 300 + state.watch_path, 301 + watch_on_fs_event, 302 + &state, 0, NULL 303 + ); 261 304 262 - if (rc == 0) { 263 - state.fs_event.data = &state; 264 - rc = uv_fs_event_start(&state.fs_event, watch_on_fs_event, state.watch_path, 0); 265 - } 305 + if (rc == 0) state.fs_event_inited = true; 266 306 267 307 if (rc != 0) { 268 308 crfprintf(stderr, msg.watch_file_failed, state.watch_path, uv_strerror(rc));
+85
tests/readable_stream_abort_capture.mjs
··· 1 + function assert(condition, message) { 2 + if (!condition) throw new Error(message); 3 + } 4 + 5 + function makeAbortError() { 6 + try { 7 + return new DOMException('aborted', 'AbortError'); 8 + } catch { 9 + const error = new Error('aborted'); 10 + error.name = 'AbortError'; 11 + return error; 12 + } 13 + } 14 + 15 + function abortRequest(request, reason) { 16 + if (request.status === 10 || request.status === 11) request.status = 12; 17 + request.fatalError = reason; 18 + if (request.destination) { 19 + try { 20 + request.destination.error?.(reason); 21 + } catch {} 22 + request.destination = null; 23 + } 24 + } 25 + 26 + async function runIteration(index) { 27 + const controller = new AbortController(); 28 + const signal = controller.signal; 29 + const request = { 30 + status: 10, 31 + destination: null, 32 + fatalError: null, 33 + seen: 0, 34 + }; 35 + 36 + setTimeout(() => { 37 + if (request.status === 10) request.status = 11; 38 + }, 0); 39 + 40 + const listener = () => { 41 + abortRequest(request, signal.reason ?? makeAbortError()); 42 + signal.removeEventListener('abort', listener); 43 + }; 44 + 45 + signal.addEventListener('abort', listener); 46 + 47 + const stream = new ReadableStream({ 48 + pull(controllerObj) { 49 + request.seen++; 50 + 51 + if (request.status === 13) { 52 + request.status = 14; 53 + controllerObj.error(request.fatalError); 54 + return; 55 + } 56 + 57 + if (request.status !== 14 && request.destination === null) { 58 + request.destination = controllerObj; 59 + controllerObj.enqueue(new Uint8Array([index & 255])); 60 + controllerObj.close(); 61 + request.destination = null; 62 + } 63 + }, 64 + cancel(reason) { 65 + request.destination = null; 66 + abortRequest(request, reason); 67 + }, 68 + }, { highWaterMark: 0 }); 69 + 70 + queueMicrotask(() => controller.abort(makeAbortError())); 71 + 72 + const reader = stream.getReader(); 73 + try { 74 + await reader.read(); 75 + } catch {} 76 + 77 + assert(request.seen >= 1, `pull was not invoked for iteration ${index}`); 78 + } 79 + 80 + for (let i = 0; i < 2000; i++) { 81 + await runIteration(i); 82 + if ((i + 1) % 100 === 0) console.log(`iter:${i + 1}`); 83 + } 84 + 85 + console.log('ok');
+216
tests/repro_fsevents_direntry_bug.mjs
··· 1 + // Repro attempt for the watcher-side `dirObj.has(...)` failure without Vite. 2 + // 3 + // Run with: 4 + // ant tests/repro_fsevents_direntry_bug.mjs 5 + // 6 + // This uses only `fsevents` plus a tiny local watcher shim that mirrors the 7 + // chokidar/Vite code path around: 8 + // const dirObj = this.fsw._getWatchedDir(dirname(pp)); 9 + // if (dirObj.has(base)) return; 10 + 11 + import fs from 'node:fs'; 12 + import { EventEmitter } from 'node:events'; 13 + import path from 'node:path'; 14 + import fsevents from '/Users/themackabu/.ant/pkg/exec/vite/node_modules/fsevents/fsevents.js'; 15 + 16 + class DirEntry { 17 + constructor(dir) { 18 + this.path = dir; 19 + this.items = new Set(); 20 + } 21 + 22 + add(item) { 23 + const { items } = this; 24 + if (!items) return; 25 + if (item !== '.' && item !== '..') items.add(item); 26 + } 27 + 28 + has(item) { 29 + const { items } = this; 30 + if (!items) return; 31 + return items.has(item); 32 + } 33 + } 34 + 35 + class MiniWatcher { 36 + constructor(root) { 37 + this.root = path.resolve(root); 38 + this._watched = new Map(); 39 + } 40 + 41 + _getWatchedDir(directory) { 42 + const dir = path.resolve(directory); 43 + if (!this._watched.has(dir)) { 44 + this._watched.set(dir, new DirEntry(dir)); 45 + } 46 + return this._watched.get(dir); 47 + } 48 + 49 + emitAdd(newPath, stats) { 50 + const pp = path.resolve(newPath); 51 + const isDir = stats.isDirectory(); 52 + const dirObj = this._getWatchedDir(path.dirname(pp)); 53 + const base = path.basename(pp); 54 + 55 + if (isDir) this._getWatchedDir(pp); 56 + 57 + console.log('[emitAdd]', { 58 + pp, 59 + base, 60 + dir: dirObj.path, 61 + hasType: typeof dirObj.has, 62 + addType: typeof dirObj.add, 63 + itemsType: typeof dirObj.items, 64 + itemsHasType: typeof dirObj.items?.has, 65 + }); 66 + 67 + if (dirObj.has(base)) { 68 + console.log('[emitAdd] already present', base); 69 + return; 70 + } 71 + 72 + dirObj.add(base); 73 + console.log('[emitAdd] added', base); 74 + } 75 + } 76 + 77 + function statSafe(file) { 78 + try { 79 + return fs.statSync(file); 80 + } catch (error) { 81 + console.log('[stat error]', file, error?.name, error?.message); 82 + return null; 83 + } 84 + } 85 + 86 + async function walkEntries(root, emitEntry) { 87 + const pending = [root]; 88 + const seen = new Set(); 89 + 90 + while (pending.length > 0) { 91 + const dir = pending.pop(); 92 + const resolvedDir = path.resolve(dir); 93 + if (seen.has(resolvedDir)) continue; 94 + seen.add(resolvedDir); 95 + let entries; 96 + 97 + try { 98 + entries = await fs.promises.readdir(resolvedDir); 99 + } catch (error) { 100 + console.log('[readdir error]', resolvedDir, error?.name, error?.message); 101 + continue; 102 + } 103 + 104 + for (const name of entries) { 105 + const fullPath = path.join(resolvedDir, name); 106 + let stats; 107 + 108 + try { 109 + stats = await fs.promises.lstat(fullPath); 110 + } catch (error) { 111 + console.log('[lstat error]', fullPath, error?.name, error?.message); 112 + continue; 113 + } 114 + 115 + await emitEntry({ 116 + path: path.relative(root, fullPath), 117 + fullPath, 118 + stats, 119 + }); 120 + 121 + if (stats.isDirectory()) pending.push(fullPath); 122 + } 123 + } 124 + } 125 + 126 + function makeReaddirpLikeStream(root) { 127 + const stream = new EventEmitter(); 128 + 129 + queueMicrotask(async () => { 130 + try { 131 + await walkEntries(root, async (entry) => { 132 + stream.emit('data', entry); 133 + }); 134 + stream.emit('end'); 135 + } catch (error) { 136 + stream.emit('error', error); 137 + } 138 + }); 139 + 140 + return stream; 141 + } 142 + 143 + const root = process.cwd(); 144 + const watcher = new MiniWatcher(root); 145 + const probeFile = path.join(root, 'tmp', 'fsevents-direntry-probe.txt'); 146 + 147 + process.on('unhandledRejection', (reason) => { 148 + console.log('[unhandledRejection]', reason?.name, reason?.message); 149 + if (reason?.stack) console.log(reason.stack); 150 + }); 151 + 152 + process.on('uncaughtException', (error) => { 153 + console.log('[uncaughtException]', error?.name, error?.message); 154 + if (error?.stack) console.log(error.stack); 155 + process.exitCode = 1; 156 + }); 157 + 158 + const stop = fsevents.watch(root, (fullPath, flags, id) => { 159 + const info = fsevents.getInfo(fullPath, flags, id); 160 + const target = path.resolve(fullPath); 161 + 162 + console.log('[fsevent]', { 163 + fullPath: target, 164 + event: info.event, 165 + type: info.type, 166 + }); 167 + 168 + const stats = statSafe(target); 169 + if (!stats) return; 170 + 171 + try { 172 + watcher.emitAdd(target, stats); 173 + } catch (error) { 174 + console.log('[emitAdd throw]', error?.name, error?.message); 175 + if (error?.stack) console.log(error.stack); 176 + } 177 + }); 178 + 179 + console.log('watching', root); 180 + 181 + fs.mkdirSync(path.dirname(probeFile), { recursive: true }); 182 + fs.writeFileSync(probeFile, `probe ${Date.now()}\n`); 183 + console.log('wrote probe file', probeFile); 184 + 185 + const stream = makeReaddirpLikeStream(root); 186 + stream.on('data', (entry) => { 187 + console.log('[stream:data]', entry.path); 188 + 189 + try { 190 + watcher.emitAdd(entry.fullPath, entry.stats); 191 + } catch (error) { 192 + console.log('[stream emitAdd throw]', error?.name, error?.message); 193 + if (error?.stack) console.log(error.stack); 194 + } 195 + }); 196 + stream.on('error', (error) => { 197 + console.log('[stream:error]', error?.name, error?.message); 198 + if (error?.stack) console.log(error.stack); 199 + }); 200 + stream.on('end', () => { 201 + console.log('[stream:end]'); 202 + }); 203 + 204 + setTimeout(() => { 205 + try { 206 + fs.appendFileSync(probeFile, 'update\n'); 207 + console.log('updated probe file', probeFile); 208 + } catch (error) { 209 + console.log('[appendFileSync throw]', error?.name, error?.message); 210 + if (error?.stack) console.log(error.stack); 211 + } 212 + }, 250); 213 + 214 + await new Promise((resolve) => setTimeout(resolve, 1500)); 215 + await stop(); 216 + console.log('stopped');
+25
tests/repro_vite_startup_watcher_bug.mjs
··· 1 + // Repro: Vite dev server startup triggers watcher-side 2 + // `TypeError: undefined is not a function` inside chokidar/fsevents. 3 + // 4 + // Run with: 5 + // ant tests/repro_vite_startup_watcher_bug.mjs 6 + 7 + import * as vite from '/Users/themackabu/.ant/pkg/exec/vite/node_modules/vite/dist/node/index.js'; 8 + 9 + process.on('unhandledRejection', (reason) => { 10 + console.log('[unhandledRejection]', reason?.name, reason?.message); 11 + if (reason?.stack) console.log(reason.stack); 12 + }); 13 + 14 + const server = await vite.createServer({ 15 + configFile: false, 16 + root: process.cwd(), 17 + }); 18 + 19 + await server.listen(); 20 + console.log('listening'); 21 + 22 + setTimeout(async () => { 23 + console.log('done wait'); 24 + await server.close(); 25 + }, 1500);
+32
tests/repro_vite_watcher_add_bug.mjs
··· 1 + // Repro: Vite's chokidar watcher path triggers 2 + // `TypeError: undefined is not a function` without needing full server startup. 3 + // 4 + // Run with: 5 + // ant tests/repro_vite_watcher_add_bug.mjs 6 + 7 + import * as vite from '/Users/themackabu/.ant/pkg/exec/vite/node_modules/vite/dist/node/index.js'; 8 + 9 + process.on('unhandledRejection', (reason) => { 10 + console.log('[unhandledRejection]', reason?.name, reason?.message); 11 + if (reason?.stack) console.log(reason.stack); 12 + }); 13 + 14 + const server = await vite.createServer({ 15 + configFile: false, 16 + root: process.cwd(), 17 + optimizeDeps: { noDiscovery: true, entries: [] }, 18 + }); 19 + 20 + server.watcher.add([ 21 + '.env.local', 22 + '.env.development', 23 + '.env.development.local', 24 + '.definitely-missing', 25 + ]); 26 + 27 + console.log('watcher.add issued'); 28 + 29 + setTimeout(async () => { 30 + console.log('done wait'); 31 + await server.close(); 32 + }, 1500);
+75
tests/test_wasm_webassembly_api.mjs
··· 1 + import { readFileSync } from 'ant:fs'; 2 + 3 + function assert(condition, message) { 4 + if (!condition) throw new Error(message); 5 + } 6 + 7 + function fixture(path) { 8 + return readFileSync(import.meta.dirname + '/../' + path); 9 + } 10 + 11 + const api = globalThis.WebAssembly; 12 + 13 + assert(api && typeof api === 'object', 'globalThis.WebAssembly should exist'); 14 + 15 + for (const name of [ 16 + 'Global', 17 + 'Instance', 18 + 'Memory', 19 + 'Module', 20 + 'Table', 21 + 'Tag', 22 + 'Exception', 23 + 'CompileError', 24 + 'LinkError', 25 + 'RuntimeError', 26 + ]) { 27 + assert(typeof api[name] === 'function', `WebAssembly.${name} should exist`); 28 + } 29 + 30 + const incrementer = fixture('wpt/wasm/webapi/resources/incrementer.wasm'); 31 + 32 + assert(WebAssembly.validate(incrementer) === true, 'incrementer.wasm should validate'); 33 + 34 + const module = new WebAssembly.Module(incrementer); 35 + const descriptors = WebAssembly.Module.exports(module); 36 + assert(Array.isArray(descriptors), 'WebAssembly.Module.exports() should return an array'); 37 + assert( 38 + descriptors.some(entry => entry && entry.name === 'increment' && entry.kind === 'function'), 39 + 'incrementer.wasm should export increment()', 40 + ); 41 + 42 + const instance = new WebAssembly.Instance(module); 43 + assert(typeof instance.exports.increment === 'function', 'instance.exports.increment should exist'); 44 + assert(instance.exports.increment(41) === 42, 'increment(41) should return 42'); 45 + 46 + const compiled = await WebAssembly.compile(incrementer); 47 + assert(compiled instanceof WebAssembly.Module, 'WebAssembly.compile() should resolve a module'); 48 + 49 + const instantiated = await WebAssembly.instantiate(incrementer); 50 + assert(instantiated.module instanceof WebAssembly.Module, 'instantiate(bytes) should return a module'); 51 + assert(instantiated.instance instanceof WebAssembly.Instance, 'instantiate(bytes) should return an instance'); 52 + assert(instantiated.instance.exports.increment(9) === 10, 'instantiate(bytes) should wire exports'); 53 + 54 + const global = new WebAssembly.Global({ value: 'i32', mutable: true }, 7); 55 + assert(global.value === 7, 'WebAssembly.Global should expose its initial value'); 56 + global.value = 11; 57 + assert(global.value === 11, 'WebAssembly.Global should accept writes'); 58 + assert(global.valueOf() === 11, 'WebAssembly.Global.prototype.valueOf() should read the value'); 59 + 60 + const invalid = new Uint8Array([0x00, 0x61, 0x62, 0x63]); 61 + assert(WebAssembly.validate(invalid) === false, 'invalid wasm bytes should fail validation'); 62 + 63 + let sawCompileError = false; 64 + try { 65 + new WebAssembly.Module(invalid); 66 + } catch (error) { 67 + sawCompileError = true; 68 + assert( 69 + error instanceof WebAssembly.CompileError || error?.name === 'CompileError', 70 + 'invalid module should throw CompileError', 71 + ); 72 + } 73 + assert(sawCompileError, 'invalid module construction should fail'); 74 + 75 + console.log('wasm:webassembly-api:ok');
+34
tests/tmp_bind_async_test.mjs
··· 1 + import net from 'node:net'; 2 + 3 + // replicate exactly what vite does 4 + const server = net.createServer(); 5 + const origListen = server.listen.bind(server); 6 + 7 + console.log('server type:', typeof server); 8 + console.log('origListen type:', typeof origListen); 9 + 10 + server.listen = async (port, ...args) => { 11 + console.log('async wrapper called, port=', port, 'args=', args); 12 + try { 13 + await Promise.resolve(); // simulate initServer 14 + console.log('after await, calling origListen...'); 15 + return origListen(port, ...args); 16 + } catch (e) { 17 + server.emit('error', e); 18 + return; 19 + } 20 + }; 21 + 22 + await new Promise((resolve, reject) => { 23 + server.on('error', (e) => { 24 + console.log('error event:', e.message); 25 + reject(e); 26 + }); 27 + server.on('listening', () => { 28 + console.log('listening on', server.address()); 29 + server.close(resolve); 30 + }); 31 + server.listen(0, '127.0.0.1'); 32 + }); 33 + 34 + console.log('done');
+26
tests/tmp_ipv6_bind_test.mjs
··· 1 + import net from 'node:net'; 2 + 3 + function tryListen(port, host) { 4 + return new Promise((resolve) => { 5 + const server = net.createServer(); 6 + server.once('error', (e) => { 7 + console.log(` error on ${host}:${port} โ€” code=${e.code} message=${e.message}`); 8 + server.close(() => resolve({ ok: false, code: e.code })); 9 + }); 10 + server.once('listening', () => { 11 + const addr = server.address(); 12 + console.log(` listening on ${JSON.stringify(addr)}`); 13 + server.close(() => resolve({ ok: true })); 14 + }); 15 + server.listen(port, host); 16 + }); 17 + } 18 + 19 + const hosts = ['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0000:0000']; 20 + for (const host of hosts) { 21 + process.stdout.write(`tryListen(9874, '${host}') ... `); 22 + const result = await tryListen(9874, host); 23 + console.log(result.ok ? 'ok' : `fail(${result.code})`); 24 + } 25 + 26 + console.log('done');
+30
tests/tmp_vite_fs_trace.mjs
··· 1 + import fs from 'node:fs'; 2 + 3 + for (const name of ['access', 'readFile', 'readdir', 'realpath', 'rm', 'stat']) { 4 + const original = fs.promises?.[name]; 5 + if (typeof original !== 'function') continue; 6 + fs.promises[name] = async function (...args) { 7 + try { 8 + return await original.apply(this, args); 9 + } catch (error) { 10 + console.log(`fs.promises.${name}`, args[0], error?.name, error?.message); 11 + throw error; 12 + } 13 + }; 14 + } 15 + 16 + process.on('unhandledRejection', (reason) => { 17 + console.log('unhandledRejection', reason?.name, reason?.message); 18 + if (reason?.stack) console.log(reason.stack); 19 + }); 20 + 21 + const vite = await import('/Users/themackabu/.ant/pkg/exec/vite/node_modules/vite/dist/node/index.js'); 22 + const server = await vite.createServer({ configFile: false, root: process.cwd() }); 23 + 24 + try { 25 + await server.listen(); 26 + console.log('listen ok'); 27 + } catch (error) { 28 + console.log('listen', error?.name, error?.message); 29 + if (error?.stack) console.log(error.stack); 30 + }
+61
tests/tmp_vite_initserver_trace.mjs
··· 1 + const vite = await import('/Users/themackabu/.ant/pkg/exec/vite/node_modules/vite/dist/node/index.js'); 2 + 3 + async function run(label, fn) { 4 + try { 5 + const value = await fn(); 6 + console.log(label, 'ok', typeof value); 7 + return value; 8 + } catch (error) { 9 + console.log(label, error?.name, error?.message); 10 + if (error?.stack) console.log(error.stack); 11 + return null; 12 + } 13 + } 14 + 15 + const config = await run('resolveConfig', () => 16 + vite.resolveConfig({ configFile: false, root: process.cwd() }, 'serve') 17 + ); 18 + if (!config) process.exit(1); 19 + 20 + const server = await run('createServer', () => 21 + vite.createServer({ configFile: false, root: process.cwd() }) 22 + ); 23 + if (!server) process.exit(1); 24 + 25 + // call each piece of initServer manually in the same order Vite does, 26 + // so we can see which one hangs 27 + console.log('\n-- manual initServer sequence --'); 28 + 29 + console.log('calling buildStart...'); 30 + await run('buildStart', () => 31 + server.environments.client.pluginContainer.buildStart() 32 + ); 33 + 34 + console.log('calling client.listen...'); 35 + await run('client.listen', () => 36 + server.environments.client.listen(server) 37 + ); 38 + 39 + console.log('calling ssr.listen...'); 40 + await run('ssr.listen', () => 41 + server.environments.ssr.listen(server) 42 + ); 43 + 44 + console.log('\n-- second pass (simulates initServer re-run) --'); 45 + 46 + console.log('calling buildStart (2nd)...'); 47 + await run('buildStart2', () => 48 + server.environments.client.pluginContainer.buildStart() 49 + ); 50 + 51 + console.log('calling client.listen (2nd)...'); 52 + await run('client.listen2', () => 53 + server.environments.client.listen(server) 54 + ); 55 + 56 + console.log('calling ssr.listen (2nd)...'); 57 + await run('ssr.listen2', () => 58 + server.environments.ssr.listen(server) 59 + ); 60 + 61 + console.log('\ndone');
+77
tests/tmp_vite_listen_trace.mjs
··· 1 + import net from 'node:net'; 2 + import http from 'node:http'; 3 + import { promises as dns } from 'node:dns'; 4 + 5 + const wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0000:0000']); 6 + 7 + function tryListen(port, host) { 8 + return new Promise((resolve) => { 9 + const server = net.createServer(); 10 + server.once('error', (e) => { 11 + server.close(() => resolve({ ok: false, code: e.code })); 12 + }); 13 + server.once('listening', () => { 14 + server.close(() => resolve({ ok: true })); 15 + }); 16 + server.listen(port, host); 17 + }); 18 + } 19 + 20 + async function isPortAvailable(port) { 21 + for (const host of wildcardHosts) { 22 + console.log(` isPortAvailable: tryListen(${port}, '${host}')`); 23 + const r = await tryListen(port, host).catch(() => ({ ok: true })); 24 + console.log(` isPortAvailable: tryListen(${port}, '${host}') => ${JSON.stringify(r)}`); 25 + if (!r.ok) return false; 26 + } 27 + return true; 28 + } 29 + 30 + // step 1: dns.promises.lookup (resolveHostname) 31 + console.log('step 1: dns.promises.lookup'); 32 + const [a, b] = await Promise.all([ 33 + dns.lookup('localhost'), 34 + dns.lookup('localhost', { verbatim: true }), 35 + ]); 36 + console.log('lookup results:', a, b); 37 + 38 + // step 2: isPortAvailable 39 + console.log('\nstep 2: isPortAvailable(5173)'); 40 + const avail = await isPortAvailable(5173); 41 + console.log('isPortAvailable =>', avail); 42 + 43 + // step 3: tryBindServer via real http.createServer + listen 44 + console.log('\nstep 3: tryBindServer'); 45 + const httpServer = http.createServer((req, res) => res.end('ok')); 46 + 47 + // mimic Vite's listen wrapper 48 + const origListen = httpServer.listen.bind(httpServer); 49 + httpServer.listen = async (port, ...args) => { 50 + console.log(' wrapped listen called, port=', port); 51 + return origListen(port, ...args); 52 + }; 53 + 54 + const bindResult = await new Promise((resolve) => { 55 + const onError = (e) => { 56 + httpServer.off('error', onError); 57 + httpServer.off('listening', onListening); 58 + resolve({ success: false, error: e }); 59 + }; 60 + const onListening = () => { 61 + httpServer.off('error', onError); 62 + httpServer.off('listening', onListening); 63 + resolve({ success: true }); 64 + }; 65 + httpServer.on('error', onError); 66 + httpServer.on('listening', onListening); 67 + httpServer.listen(5173, 'localhost'); 68 + }); 69 + 70 + console.log('tryBindServer result:', bindResult.success ? 'success' : `fail: ${bindResult.error?.message}`); 71 + if (bindResult.success) { 72 + console.log('address:', httpServer.address()); 73 + await new Promise((resolve) => httpServer.close(resolve)); 74 + console.log('server closed'); 75 + } 76 + 77 + console.log('\ndone');
+47
tests/tmp_vite_server_trace.mjs
··· 1 + const vite = await import('/Users/themackabu/.ant/pkg/exec/vite/node_modules/vite/dist/node/index.js'); 2 + 3 + async function run(label, fn) { 4 + try { 5 + const value = await fn(); 6 + console.log(label, 'ok', typeof value); 7 + return value; 8 + } catch (error) { 9 + console.log(label, error?.name, error?.message); 10 + if (error?.stack) console.log(error.stack); 11 + return null; 12 + } 13 + } 14 + 15 + const config = await run('resolveConfig', () => 16 + vite.resolveConfig({ configFile: false, root: process.cwd() }, 'serve') 17 + ); 18 + 19 + if (config) { 20 + const server = await run('createServer', () => 21 + vite.createServer({ configFile: false, root: process.cwd() }) 22 + ); 23 + 24 + if (server) { 25 + const httpServer = server.httpServer; 26 + 27 + // Patch origListen (Vite's async wrapper) to catch the inner rejection 28 + const viteAsyncWrapper = httpServer.listen; 29 + httpServer.listen = function(...args) { 30 + console.log('[trace] httpServer.listen called, port=', args[0], 'host=', args[1]); 31 + const p = viteAsyncWrapper.apply(this, args); 32 + if (p && typeof p.then === 'function') { 33 + p.then( 34 + () => console.log('[trace] async wrapper resolved'), 35 + (e) => console.log('[trace] async wrapper rejected:', e?.message) 36 + ); 37 + } 38 + return p; 39 + }; 40 + 41 + await run('server.listen', async () => { 42 + await server.listen(); 43 + await server.close(); 44 + return server; 45 + }); 46 + } 47 + }
+55
tests/tmp_vite_trace.mjs
··· 1 + const vite = await import('/Users/themackabu/.ant/pkg/exec/vite/node_modules/vite/dist/node/index.js'); 2 + const chunk = await import('/Users/themackabu/.ant/pkg/exec/vite/node_modules/vite/dist/node/chunks/node.js'); 3 + const config = await vite.resolveConfig({ configFile: false, root: process.cwd() }, 'serve'); 4 + const env = new vite.DevEnvironment('client', config, { 5 + hot: true, 6 + transport: vite.createServerHotChannel(), 7 + disableDepsOptimizer: true 8 + }); 9 + 10 + function tryStringify(label, value) { 11 + try { 12 + const out = JSON.stringify(value, (_, item) => { 13 + if (typeof item === 'function' || item instanceof RegExp) return item.toString(); 14 + return item; 15 + }); 16 + console.log(label, 'ok', typeof out, out?.length ?? 0); 17 + } catch (error) { 18 + console.log(label, error?.name, error?.message); 19 + } 20 + } 21 + 22 + tryStringify('env.config.resolve', env.config.resolve); 23 + tryStringify('env.config.assetsInclude', env.config.assetsInclude); 24 + tryStringify('env.config.plugins', env.config.plugins.map((p) => p.name)); 25 + tryStringify('env.config.optimizeDeps.include', env.config.optimizeDeps.include); 26 + tryStringify('env config-hash-shape', { 27 + define: !env.config.keepProcessEnv ? process.env.NODE_ENV || env.config.mode : null, 28 + root: env.config.root, 29 + resolve: env.config.resolve, 30 + assetsInclude: env.config.assetsInclude, 31 + plugins: env.config.plugins.map((p) => p.name), 32 + optimizeDeps: { 33 + include: env.config.optimizeDeps.include, 34 + exclude: env.config.optimizeDeps.exclude, 35 + rolldownOptions: { 36 + ...env.config.optimizeDeps.rolldownOptions, 37 + plugins: undefined, 38 + onLog: undefined, 39 + onwarn: undefined, 40 + checks: undefined, 41 + output: { 42 + ...env.config.optimizeDeps.rolldownOptions?.output, 43 + plugins: undefined 44 + } 45 + } 46 + }, 47 + optimizeDepsPluginNames: env.config.optimizeDepsPluginNames 48 + }); 49 + 50 + try { 51 + const metadata = chunk.at(env, String(Date.now())); 52 + console.log('initDepsOptimizerMetadata ok', typeof metadata); 53 + } catch (error) { 54 + console.log('initDepsOptimizerMetadata', error?.name, error?.message); 55 + }
+23
tests/todo_rsc_action_rsc_post.mjs
··· 1 + function assert(cond, msg) { 2 + if (!cond) throw new Error(msg); 3 + } 4 + 5 + const { default: handler } = await import('../todo/rsc/dist/rsc/index.js'); 6 + 7 + const response = await handler.fetch( 8 + new Request('http://localhost/_.rsc', { 9 + method: 'POST', 10 + headers: { 11 + 'x-rsc-action': '710363d987f5#updateServerCounter', 12 + }, 13 + body: '[1]', 14 + }), 15 + ); 16 + 17 + console.log(`status:${response.status}`); 18 + console.log(`content-type:${response.headers.get('content-type') || 'missing'}`); 19 + 20 + assert( 21 + response.headers.get('content-type') === 'text/x-component;charset=utf-8', 22 + 'expected RSC action response content-type' 23 + );
+47
tests/todo_rsc_form_action_markup.mjs
··· 1 + function assert(cond, msg) { 2 + if (!cond) throw new Error(msg); 3 + } 4 + 5 + async function readUntil(response, marker, limit = 32768) { 6 + const stream = response.body; 7 + assert(stream, 'expected response body stream'); 8 + 9 + const reader = stream.getReader(); 10 + const decoder = new TextDecoder(); 11 + let text = ''; 12 + 13 + while (text.length < limit) { 14 + const chunk = await reader.read(); 15 + if (chunk.done) break; 16 + text += decoder.decode(chunk.value, { stream: true }); 17 + if (text.includes(marker)) break; 18 + } 19 + 20 + text += decoder.decode(); 21 + return text; 22 + } 23 + 24 + const mod = await import('../todo/rsc/dist/rsc/index.js'); 25 + const handler = mod.default; 26 + 27 + assert(handler && typeof handler.fetch === 'function', 'expected todo/rsc handler.fetch'); 28 + 29 + const response = await handler.fetch(new Request('http://localhost/')); 30 + assert(response.status === 200, `expected status 200, got ${response.status}`); 31 + 32 + const html = await readUntil(response, '</form>'); 33 + const formStart = html.indexOf('<form'); 34 + assert(formStart !== -1, 'expected rendered form'); 35 + 36 + const formEnd = html.indexOf('</form>', formStart); 37 + assert(formEnd !== -1, 'expected form closing tag'); 38 + 39 + const snippet = html.slice(formStart, formEnd + 7); 40 + console.log(snippet); 41 + 42 + assert( 43 + snippet.includes('$ACTION_') || 44 + snippet.includes('javascript:throw new Error') || 45 + snippet.includes('formAction'), 46 + 'expected server action metadata or replay marker in form markup', 47 + );
+169
vendor/packagefiles/wasm-micro-runtime/meson.build
··· 1 + project('wasm-micro-runtime', 'c', default_options: ['c_std=gnu11', 'warning_level=0']) 2 + 3 + cc = meson.get_compiler('c') 4 + 5 + if host_machine.system() == 'windows' 6 + wamr_platform = 'windows' 7 + elif host_machine.system() == 'darwin' 8 + wamr_platform = 'darwin' 9 + else 10 + wamr_platform = 'linux' 11 + endif 12 + 13 + common_c_args = [ 14 + '-DNDEBUG', 15 + '-DBH_MALLOC=wasm_runtime_malloc', 16 + '-DBH_FREE=wasm_runtime_free', 17 + '-DWASM_ENABLE_INTERP=1', 18 + '-DWASM_ENABLE_FAST_INTERP=0', 19 + '-DWASM_ENABLE_AOT=0', 20 + '-DWASM_ENABLE_JIT=0', 21 + '-DWASM_ENABLE_FAST_JIT=0', 22 + '-DWASM_ENABLE_LIBC_BUILTIN=0', 23 + '-DWASM_ENABLE_LIBC_WASI=1', 24 + '-DWASM_ENABLE_MULTI_MODULE=0', 25 + '-DWASM_ENABLE_BULK_MEMORY=1', 26 + '-DWASM_ENABLE_BULK_MEMORY_OPT=1', 27 + '-DWASM_ENABLE_SHARED_MEMORY=0', 28 + '-DWASM_ENABLE_REF_TYPES=1', 29 + '-DWASM_ENABLE_CALL_INDIRECT_OVERLONG=1', 30 + '-DWASM_ENABLE_MINI_LOADER=0', 31 + '-DWASM_DISABLE_HW_BOUND_CHECK=1', 32 + '-DWASM_DISABLE_STACK_HW_BOUND_CHECK=1', 33 + '-DWASM_DISABLE_WAKEUP_BLOCKING_OP=0', 34 + '-DWASM_ENABLE_SIMD=0', 35 + '-DWASM_ENABLE_MODULE_INST_CONTEXT=1', 36 + '-DWASM_GLOBAL_HEAP_SIZE=' + (10 * 1024 * 1024).to_string(), 37 + ] 38 + 39 + if wamr_platform == 'windows' 40 + common_c_args += ['-DBH_PLATFORM_WINDOWS', '-DWASM_HAVE_MREMAP=0'] 41 + elif wamr_platform == 'darwin' 42 + common_c_args += ['-DBH_PLATFORM_DARWIN', '-DWASM_HAVE_MREMAP=0'] 43 + else 44 + common_c_args += ['-DBH_PLATFORM_LINUX', '-DWASM_HAVE_MREMAP=1'] 45 + endif 46 + 47 + if host_machine.cpu_family() == 'aarch64' 48 + common_c_args += ['-DBUILD_TARGET_AARCH64', '-DBUILD_TARGET=AARCH64'] 49 + elif host_machine.cpu_family() == 'x86_64' 50 + common_c_args += ['-DBUILD_TARGET_X86_64', '-DBUILD_TARGET=X86_64'] 51 + else 52 + error('Unsupported WAMR target: ' + host_machine.cpu_family()) 53 + endif 54 + 55 + wamr_inc = include_directories( 56 + '.', 57 + 'core', 58 + 'core/iwasm/include', 59 + 'core/iwasm/common', 60 + 'core/iwasm/interpreter', 61 + 'core/iwasm/libraries/libc-wasi', 62 + 'core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include', 63 + 'core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src', 64 + 'core/shared/mem-alloc', 65 + 'core/shared/utils', 66 + 'core/shared/platform/' + wamr_platform, 67 + 'core/shared/platform/include', 68 + 'core/shared/platform/common/libc-util', 69 + 'core/shared/platform/common/memory', 70 + 'core/shared/platform/common/posix', 71 + ) 72 + 73 + wamr_sources = files( 74 + 'core/shared/platform/' + wamr_platform + '/platform_init.c', 75 + 'core/shared/platform/common/libc-util/libc_errno.c', 76 + 'core/shared/platform/common/memory/mremap.c', 77 + 'core/shared/platform/common/posix/posix_blocking_op.c', 78 + 'core/shared/platform/common/posix/posix_clock.c', 79 + 'core/shared/platform/common/posix/posix_malloc.c', 80 + 'core/shared/platform/common/posix/posix_memmap.c', 81 + 'core/shared/platform/common/posix/posix_sleep.c', 82 + 'core/shared/platform/common/posix/posix_thread.c', 83 + 'core/shared/platform/common/posix/posix_time.c', 84 + 'core/shared/utils/bh_assert.c', 85 + 'core/shared/utils/bh_bitmap.c', 86 + 'core/shared/utils/bh_common.c', 87 + 'core/shared/utils/bh_hashmap.c', 88 + 'core/shared/utils/bh_leb128.c', 89 + 'core/shared/utils/bh_list.c', 90 + 'core/shared/utils/bh_log.c', 91 + 'core/shared/utils/bh_queue.c', 92 + 'core/shared/utils/bh_vector.c', 93 + 'core/shared/utils/runtime_timer.c', 94 + 'core/shared/mem-alloc/mem_alloc.c', 95 + 'core/shared/mem-alloc/ems/ems_alloc.c', 96 + 'core/shared/mem-alloc/ems/ems_gc.c', 97 + 'core/shared/mem-alloc/ems/ems_hmu.c', 98 + 'core/shared/mem-alloc/ems/ems_kfc.c', 99 + 'core/iwasm/common/wasm_application.c', 100 + 'core/iwasm/common/wasm_blocking_op.c', 101 + 'core/iwasm/common/wasm_c_api.c', 102 + 'core/iwasm/common/wasm_exec_env.c', 103 + 'core/iwasm/common/wasm_loader_common.c', 104 + 'core/iwasm/common/wasm_memory.c', 105 + 'core/iwasm/common/wasm_native.c', 106 + 'core/iwasm/common/wasm_runtime_common.c', 107 + 'core/iwasm/common/wasm_shared_memory.c', 108 + 'core/iwasm/interpreter/wasm_loader.c', 109 + 'core/iwasm/interpreter/wasm_runtime.c', 110 + 'core/iwasm/interpreter/wasm_interp_classic.c', 111 + 'core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c', 112 + 'core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c', 113 + 'core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c', 114 + 'core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c', 115 + 'core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.c', 116 + ) 117 + 118 + if host_machine.cpu_family() == 'aarch64' 119 + if wamr_platform == 'darwin' 120 + wamr_sources += files('core/iwasm/common/arch/invokeNative_aarch64.s') 121 + else 122 + wamr_sources += files('core/iwasm/common/arch/invokeNative_aarch64.s') 123 + endif 124 + elif host_machine.cpu_family() == 'x86_64' 125 + wamr_sources += files('core/iwasm/common/arch/invokeNative_em64.s') 126 + else 127 + wamr_sources += files('core/iwasm/common/arch/invokeNative_general.c') 128 + endif 129 + 130 + if wamr_platform == 'windows' 131 + wamr_sources += files( 132 + 'core/shared/platform/windows/win_file.c', 133 + 'core/shared/platform/windows/win_socket.c', 134 + ) 135 + else 136 + wamr_sources += files( 137 + 'core/shared/platform/common/posix/posix_file.c', 138 + 'core/shared/platform/common/posix/posix_socket.c', 139 + ) 140 + endif 141 + 142 + wamr_lib = static_library( 143 + 'wamr', 144 + wamr_sources, 145 + include_directories: wamr_inc, 146 + c_args: common_c_args, 147 + ) 148 + 149 + wamr_dep = declare_dependency( 150 + link_with: wamr_lib, 151 + include_directories: wamr_inc, 152 + compile_args: [ 153 + '-DWASM_ENABLE_INTERP=1', 154 + '-DWASM_ENABLE_LIBC_WASI=1', 155 + '-DWASM_ENABLE_MODULE_INST_CONTEXT=1', 156 + '-DWASM_ENABLE_BULK_MEMORY=1', 157 + '-DWASM_ENABLE_REF_TYPES=1', 158 + '-DWASM_ENABLE_SHARED_MEMORY=0', 159 + '-DWASM_ENABLE_SIMD=0', 160 + '-DWASM_ENABLE_AOT=0', 161 + '-DWASM_ENABLE_MULTI_MODULE=0', 162 + '-DWASM_ENABLE_TAGS=0', 163 + '-DWASM_CONFIGURABLE_BOUNDS_CHECKS=0', 164 + ], 165 + dependencies: [ 166 + cc.find_library('m', required: true), 167 + dependency('threads'), 168 + ], 169 + )
+8
vendor/wasm-micro-runtime.wrap
··· 1 + [wrap-git] 2 + url = https://github.com/bytecodealliance/wasm-micro-runtime.git 3 + revision = 92f40918bbfad35546a1512b10bd25eaa31add4d 4 + depth = 1 5 + patch_directory = wasm-micro-runtime 6 + 7 + [provide] 8 + wasm-micro-runtime = wamr_dep