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.

runtime: improve Node compatibility across more core modules and compiler

- extract Silver compile context helpers and fix inherited ctor/class field setup
- add SubtleCrypto.digest and expand events/process compatibility
- add Console instance routing and update console specs
- clean up stream/web export wiring
- add focused regression tests for constructors, crypto, events, and property errors

+4286 -738
+3 -1
.gitignore
··· 22 22 /javascript-zoo 23 23 /game-of-life 24 24 25 + node_modules 26 + !/tests/node_modules 27 + 25 28 /wpt 26 29 /tools 27 30 /zoo.sh ··· 33 36 *.todo 34 37 *.trace 35 38 36 - node_modules 37 39 bun.lock 38 40 pnpm-lock.yaml 39 41 package-lock.json
+79 -1
examples/spec/console.js
··· 1 - import { test, summary } from './helpers.js'; 1 + import consoleModule, { Console } from 'node:console'; 2 + import { test, testDeep, summary } from './helpers.js'; 2 3 3 4 console.log('Console Tests\n'); 4 5 ··· 10 11 test('console.trace exists', typeof console.trace, 'function'); 11 12 test('console.time exists', typeof console.time, 'function'); 12 13 test('console.timeEnd exists', typeof console.timeEnd, 'function'); 14 + test('console.timeLog exists', typeof console.timeLog, 'function'); 13 15 test('console.assert exists', typeof console.assert, 'function'); 14 16 test('console.clear exists', typeof console.clear, 'function'); 17 + test('console.count exists', typeof console.count, 'function'); 18 + test('console.countReset exists', typeof console.countReset, 'function'); 19 + test('console.group exists', typeof console.group, 'function'); 20 + test('console.groupCollapsed exists', typeof console.groupCollapsed, 'function'); 21 + test('console.groupEnd exists', typeof console.groupEnd, 'function'); 22 + test('console.dir exists', typeof console.dir, 'function'); 23 + test('console.dirxml exists', typeof console.dirxml, 'function'); 24 + test('console.table exists', typeof console.table, 'function'); 25 + test('console.Console exists', typeof console.Console, 'function'); 26 + 27 + test('node:console default export is object', typeof consoleModule, 'object'); 28 + test('node:console Console export exists', typeof Console, 'function'); 29 + test('node:console default Console matches named export', consoleModule.Console, Console); 30 + 31 + function makeSink() { 32 + return { 33 + chunks: [], 34 + write(chunk) { 35 + this.chunks.push(String(chunk)); 36 + return true; 37 + } 38 + }; 39 + } 40 + 41 + const stdoutSink = makeSink(); 42 + const stderrSink = makeSink(); 43 + const instance = new Console({ stdout: stdoutSink, stderr: stderrSink, groupIndentation: 2 }); 44 + 45 + test('Console instance log exists', typeof instance.log, 'function'); 46 + test('Console instance error exists', typeof instance.error, 'function'); 47 + test('Console instance warn exists', typeof instance.warn, 'function'); 48 + test('Console instance info exists', typeof instance.info, 'function'); 49 + test('Console instance debug exists', typeof instance.debug, 'function'); 50 + test('Console instance assert exists', typeof instance.assert, 'function'); 51 + test('Console instance trace exists', typeof instance.trace, 'function'); 52 + test('Console instance count exists', typeof instance.count, 'function'); 53 + test('Console instance countReset exists', typeof instance.countReset, 'function'); 54 + test('Console instance time exists', typeof instance.time, 'function'); 55 + test('Console instance timeLog exists', typeof instance.timeLog, 'function'); 56 + test('Console instance timeEnd exists', typeof instance.timeEnd, 'function'); 57 + test('Console instance group exists', typeof instance.group, 'function'); 58 + test('Console instance groupCollapsed exists', typeof instance.groupCollapsed, 'function'); 59 + test('Console instance groupEnd exists', typeof instance.groupEnd, 'function'); 60 + test('Console instance clear exists', typeof instance.clear, 'function'); 61 + test('Console instance dir exists', typeof instance.dir, 'function'); 62 + test('Console instance dirxml exists', typeof instance.dirxml, 'function'); 63 + test('Console instance table exists', typeof instance.table, 'function'); 64 + 65 + instance.log('hello', 'world'); 66 + instance.info('info'); 67 + instance.group('parent'); 68 + instance.log('child'); 69 + instance.groupEnd(); 70 + instance.count('hits'); 71 + instance.count('hits'); 72 + instance.countReset('hits'); 73 + instance.count('hits'); 74 + instance.error('problem'); 75 + instance.warn('warning'); 76 + instance.assert(false, 'boom'); 77 + 78 + testDeep('Console instance stdout routing', stdoutSink.chunks, [ 79 + 'hello world\n', 80 + 'info\n', 81 + 'parent\n', 82 + ' child\n', 83 + 'hits: 1\n', 84 + 'hits: 2\n', 85 + 'hits: 1\n' 86 + ]); 87 + 88 + testDeep('Console instance stderr routing', stderrSink.chunks, [ 89 + 'problem\n', 90 + 'warning\n', 91 + 'Assertion failed: boom\n' 92 + ]); 15 93 16 94 summary();
+10
examples/spec/events.js
··· 281 281 addAbortController.abort(); 282 282 test('events.addAbortListener fires on abort', addAbortValue, 1); 283 283 test('events.addAbortListener returns disposable', typeof disposable.dispose, 'function'); 284 + 285 + let disposedAbortValue = 0; 286 + const disposedAbortController = new AbortController(); 287 + const disposedAbort = addAbortListener(disposedAbortController.signal, () => { 288 + disposedAbortValue++; 289 + }); 290 + disposedAbort.dispose(); 291 + disposedAbortController.abort(); 292 + test('events.addAbortListener dispose removes listener', disposedAbortValue, 0); 293 + 284 294 test('events.getMaxListeners default', getMaxListeners(eeNodeOnce), 10); 285 295 test('events.setMaxListeners no-op return', setMaxListeners(20, eeNodeOnce), undefined); 286 296
+4 -1
examples/spec/objects.js
··· 1 - import { test, testDeep, summary } from './helpers.js'; 1 + import { test, testDeep, testThrows, summary } from './helpers.js'; 2 2 3 3 console.log('Object Tests\n'); 4 4 ··· 116 116 const arrayDescriptors = Object.getOwnPropertyDescriptors(['item']); 117 117 test('array descriptor index value', arrayDescriptors[0].value, 'item'); 118 118 test('array descriptor length value', arrayDescriptors.length.value, 1); 119 + testThrows('Object.getOwnPropertyDescriptors throws without argument', () => Object.getOwnPropertyDescriptors()); 120 + testThrows('Object.getOwnPropertyDescriptors throws on null', () => Object.getOwnPropertyDescriptors(null)); 121 + test('Object.getOwnPropertyDescriptors boxes number primitive', typeof Object.getOwnPropertyDescriptors(1), 'object'); 119 122 120 123 summary();
+7
include/common.h
··· 27 27 X(SLOT_BUILTIN) \ 28 28 X(SLOT_BRAND) \ 29 29 X(SLOT_DATA) \ 30 + X(SLOT_EVENT_MAX_LISTENERS) \ 30 31 X(SLOT_CTOR) \ 31 32 X(SLOT_FS_FLAGS) \ 32 33 X(SLOT_DEFAULT) \ 34 + X(SLOT_CONSOLE_STDOUT) \ 35 + X(SLOT_CONSOLE_STDERR) \ 36 + X(SLOT_CONSOLE_COUNTS) \ 37 + X(SLOT_CONSOLE_TIMERS) \ 38 + X(SLOT_CONSOLE_GROUP_INDENT) \ 39 + X(SLOT_CONSOLE_GROUP_LEVEL) \ 33 40 X(SLOT_ERROR_BRAND) \ 34 41 X(SLOT_ERR_TYPE) \ 35 42 X(SLOT_OBSERVABLE_SUBSCRIBER) \
+4
include/debug.h
··· 7 7 SV_DEBUG_DUMP_BYTECODE = 1u << 0, 8 8 SV_DEBUG_DUMP_JIT = 1u << 1, 9 9 SV_DEBUG_JIT_WARN = 1u << 2, 10 + SV_DEBUG_PARSE = 1u << 3, 11 + SV_DEBUG_COMPILE = 1u << 4, 10 12 } sv_debug_flag_t; 11 13 12 14 bool sv_debug_enabled(sv_debug_flag_t flag); ··· 18 20 #define sv_dump_bytecode_unlikely sv_debug_unlikely(SV_DEBUG_DUMP_BYTECODE) 19 21 #define sv_dump_jit_unlikely sv_debug_unlikely(SV_DEBUG_DUMP_JIT) 20 22 #define sv_jit_warn_unlikely sv_debug_unlikely(SV_DEBUG_JIT_WARN) 23 + #define sv_parse_trace_unlikely sv_debug_unlikely(SV_DEBUG_PARSE) 24 + #define sv_compile_trace_unlikely sv_debug_unlikely(SV_DEBUG_COMPILE) 21 25 22 26 #endif
+8 -7
include/esm/loader.h
··· 22 22 23 23 void js_esm_cleanup_module_cache(void); 24 24 25 - ant_value_t js_esm_import_sync(ant_t *js, ant_value_t specifier); 26 25 ant_value_t js_esm_make_file_url(ant_t *js, const char *path); 26 + ant_value_t js_esm_import_sync(ant_t *js, ant_value_t specifier); 27 + 28 + ant_value_t js_esm_import_sync_cstr(ant_t *js, const char *specifier, size_t spec_len); 27 29 ant_value_t js_esm_import_sync_from(ant_t *js, ant_value_t specifier, const char *base_path); 30 + ant_value_t js_esm_resolve_specifier(ant_t *js, ant_value_t specifier, const char *base_path); 28 31 29 - ant_value_t js_esm_eval_module_source( 30 - ant_t *js, const char *resolved_path, 31 - const char *js_code, size_t js_len, ant_value_t ns 32 - ); 32 + ant_value_t js_esm_import_sync_from_require(ant_t *js, ant_value_t specifier, const char *base_path); 33 + ant_value_t js_esm_resolve_specifier_require(ant_t *js, ant_value_t specifier, const char *base_path); 33 34 34 - ant_value_t js_esm_import_sync_cstr(ant_t *js, const char *specifier, size_t spec_len); 35 - ant_value_t js_esm_resolve_specifier(ant_t *js, ant_value_t specifier, const char *base_path); 36 35 ant_value_t js_esm_import_sync_cstr_from(ant_t *js, const char *specifier, size_t spec_len, const char *base_path); 37 36 ant_value_t js_esm_import_dynamic(ant_t *js, ant_value_t specifier, const char *base_path, ant_value_t *out_tla_promise); 37 + ant_value_t js_esm_import_sync_cstr_from_require(ant_t *js, const char *specifier, size_t spec_len, const char *base_path); 38 + ant_value_t js_esm_eval_module_source(ant_t *js, const char *resolved_path, const char *js_code, size_t js_len, ant_value_t ns); 38 39 39 40 #endif
+2
include/internal.h
··· 329 329 330 330 bool is_internal_prop(const char *key, ant_offset_t klen); 331 331 size_t uint_to_str(char *buf, size_t bufsize, uint64_t val); 332 + int extract_array_args(ant_t *js, ant_value_t arr, ant_value_t **out_args); 332 333 333 334 ant_value_t tov(double d); 334 335 double tod(ant_value_t v); ··· 387 388 388 389 bool is_proxy(ant_value_t obj); 389 390 bool strict_eq_values(ant_t *js, ant_value_t l, ant_value_t r); 391 + bool js_deep_equal(ant_t *js, ant_value_t a, ant_value_t b, bool strict); 390 392 391 393 ant_value_t js_proxy_apply(ant_t *js, ant_value_t proxy, ant_value_t this_arg, ant_value_t *args, int argc); 392 394 ant_value_t js_proxy_construct(ant_t *js, ant_value_t proxy, ant_value_t *args, int argc, ant_value_t new_target);
+2
include/modules/events.h
··· 4 4 #include <stdbool.h> 5 5 #include "types.h" 6 6 7 + #define EVENTS_DEFAULT_MAX_LISTENERS 10 8 + 7 9 ant_value_t events_library(ant_t *js); 8 10 ant_value_t eventemitter_prototype(ant_t *js); 9 11
+10 -3
include/modules/io.h
··· 47 47 inspect_visited_t *visited 48 48 ); 49 49 50 - ant_value_t console_print( 51 - ant_t *js, ant_value_t *args, 52 - int nargs, const char *color, FILE *stream 50 + ant_value_t console_emit( 51 + ant_t *js, 52 + bool use_stderr, const char *prefix, 53 + ant_value_t *args, int nargs 54 + ); 55 + 56 + ant_value_t console_emit_current( 57 + ant_t *js, 58 + bool use_stderr, const char *prefix, 59 + ant_value_t *args, int nargs 53 60 ); 54 61 55 62 #endif
+1
include/modules/stream.h
··· 7 7 8 8 ant_value_t stream_library(ant_t *js); 9 9 ant_value_t stream_promises_library(ant_t *js); 10 + ant_value_t stream_web_library(ant_t *js); 10 11 11 12 ant_value_t stream_readable_constructor(ant_t *js); 12 13 ant_value_t stream_writable_constructor(ant_t *js);
+42
include/silver/compile_ctx.h
··· 1 + #ifndef SILVER_COMPILE_CTX_H 2 + #define SILVER_COMPILE_CTX_H 3 + 4 + #include <uthash.h> 5 + #include "silver/compiler.h" 6 + 7 + void sv_compile_ctx_init_root( 8 + sv_compiler_t *ctx, 9 + ant_t *js, 10 + const char *filename, 11 + const char *source, 12 + ant_offset_t source_len, 13 + sv_compile_mode_t mode, 14 + bool is_strict, 15 + sv_line_table_t *line_table 16 + ); 17 + 18 + void sv_compile_ctx_init_child( 19 + sv_compiler_t *ctx, 20 + sv_compiler_t *enclosing, 21 + sv_ast_t *node, 22 + sv_compile_mode_t mode 23 + ); 24 + 25 + void sv_compile_ctx_line_table_lookup( 26 + sv_line_table_t *lt, 27 + uint32_t off, 28 + uint32_t *out_line, 29 + uint32_t *out_col 30 + ); 31 + 32 + uint32_t sv_compile_ctx_hash_local_name(const char *name, uint32_t len); 33 + sv_line_table_t *sv_compile_ctx_build_line_table(const char *source, ant_offset_t source_len); 34 + 35 + void sv_compile_ctx_cleanup(sv_compiler_t *ctx); 36 + void sv_compile_ctx_free_line_table(sv_line_table_t *lt); 37 + 38 + void sv_compile_ctx_ensure_local_lookup_capacity(sv_compiler_t *ctx, int next_count); 39 + void sv_compile_ctx_local_lookup_insert(sv_compiler_t *ctx, int idx); 40 + void sv_compile_ctx_local_lookup_remove(sv_compiler_t *ctx, int idx); 41 + 42 + #endif
+172 -1
include/silver/compiler.h
··· 3 3 4 4 #include "silver/ast.h" 5 5 #include "silver/vm.h" 6 + #include "silver/engine.h" 6 7 7 8 typedef enum { 8 9 SV_COMPILE_SCRIPT = 0, ··· 10 11 SV_COMPILE_MODULE = 2, 11 12 SV_COMPILE_REPL = 3, 12 13 } sv_compile_mode_t; 14 + 15 + typedef struct { 16 + const char *name; 17 + size_t len; 18 + } sv_param_t; 13 19 14 20 typedef struct { 15 21 const char *name; 22 + uint32_t name_len; 23 + uint32_t name_hash; 24 + int lookup_next; 25 + int depth; 26 + bool is_const; 27 + bool captured; 28 + bool is_tdz; 29 + uint8_t inferred_type; 30 + } sv_local_t; 31 + 32 + typedef struct { 33 + int *offsets; 34 + int count; 35 + int cap; 36 + } sv_patch_list_t; 37 + 38 + typedef struct { 39 + const char *name; 40 + uint32_t len; 41 + } sv_deferred_export_t; 42 + 43 + typedef struct { 44 + int loop_start; 45 + sv_patch_list_t breaks; 46 + sv_patch_list_t continues; 47 + int scope_depth; 48 + const char *label; 49 + uint32_t label_len; 50 + bool is_switch; 51 + } sv_loop_t; 52 + 53 + typedef struct const_dedup_entry { 54 + const char *str; 16 55 size_t len; 17 - } sv_param_t; 56 + int index; 57 + UT_hash_handle hh; 58 + } const_dedup_entry_t; 59 + 60 + typedef struct sv_line_table { 61 + uint32_t *offsets; 62 + int count; 63 + } sv_line_table_t; 64 + 65 + typedef struct sv_compiler { 66 + ant_t *js; 67 + const char *filename; 68 + const char *source; 69 + ant_offset_t source_len; 70 + 71 + uint8_t *code; 72 + int code_len; 73 + int code_cap; 74 + 75 + ant_value_t *constants; 76 + int const_count; 77 + int const_cap; 78 + 79 + sv_atom_t *atoms; 80 + int atom_count; 81 + int atom_cap; 82 + int ic_count; 83 + 84 + sv_local_t *locals; 85 + int local_count; 86 + int local_cap; 87 + int *local_lookup_heads; 88 + int local_lookup_cap; 89 + int max_local_count; 90 + int scope_depth; 91 + int param_locals; 92 + 93 + sv_upval_desc_t *upval_descs; 94 + int upvalue_count; 95 + int upvalue_cap; 96 + 97 + sv_loop_t *loops; 98 + int loop_count; 99 + int loop_cap; 100 + 101 + struct sv_compiler *enclosing; 102 + sv_ast_t **field_inits; 103 + 104 + int field_init_count; 105 + int *computed_key_locals; 106 + int param_count; 107 + 108 + bool is_arrow; 109 + bool is_async; 110 + bool is_strict; 111 + sv_compile_mode_t mode; 112 + 113 + bool is_tla; 114 + int try_depth; 115 + int with_depth; 116 + int strict_args_local; 117 + int new_target_local; 118 + int super_local; 119 + 120 + const char *pending_label; 121 + uint32_t pending_label_len; 122 + 123 + const char *inferred_name; 124 + uint32_t inferred_name_len; 125 + 126 + sv_srcpos_t *srcpos; 127 + int srcpos_count; 128 + int srcpos_cap; 129 + uint32_t last_srcpos_off; 130 + uint32_t last_srcpos_end; 131 + 132 + sv_type_info_t *slot_types; 133 + int slot_type_cap; 134 + 135 + sv_deferred_export_t *deferred_exports; 136 + int deferred_export_count; 137 + int deferred_export_cap; 138 + 139 + const_dedup_entry_t *const_dedup; 140 + sv_line_table_t *line_table; 141 + } sv_compiler_t; 142 + 18 143 19 144 #define SV_PARAM(name_literal) \ 20 145 ((sv_param_t){ (name_literal), sizeof(name_literal) - 1 }) ··· 35 160 int param_count, const char *body, 36 161 size_t body_len, bool is_async 37 162 ); 163 + 164 + sv_func_t *compile_function_body( 165 + sv_compiler_t *enclosing, 166 + sv_ast_t *node, 167 + sv_compile_mode_t mode 168 + ); 169 + 170 + void compile_array(sv_compiler_t *c, sv_ast_t *node); 171 + void compile_array_destructure(sv_compiler_t *c, sv_ast_t *pat, bool keep); 172 + void compile_assign(sv_compiler_t *c, sv_ast_t *node); 173 + void compile_binary(sv_compiler_t *c, sv_ast_t *node); 174 + void compile_break(sv_compiler_t *c, sv_ast_t *node); 175 + void compile_call(sv_compiler_t *c, sv_ast_t *node); 176 + void compile_class(sv_compiler_t *c, sv_ast_t *node); 177 + void compile_continue(sv_compiler_t *c, sv_ast_t *node); 178 + void compile_delete(sv_compiler_t *c, sv_ast_t *node); 179 + void compile_destructure_binding(sv_compiler_t *c, sv_ast_t *pat, sv_var_kind_t kind); 180 + void compile_do_while(sv_compiler_t *c, sv_ast_t *node); 181 + void compile_export_decl(sv_compiler_t *c, sv_ast_t *node); 182 + void compile_expr(sv_compiler_t *c, sv_ast_t *node); 183 + void compile_for_in(sv_compiler_t *c, sv_ast_t *node); 184 + void compile_for_of(sv_compiler_t *c, sv_ast_t *node); 185 + void compile_for(sv_compiler_t *c, sv_ast_t *node); 186 + void compile_func_expr(sv_compiler_t *c, sv_ast_t *node); 187 + void compile_if(sv_compiler_t *c, sv_ast_t *node); 188 + void compile_import_decl(sv_compiler_t *c, sv_ast_t *node); 189 + void compile_label(sv_compiler_t *c, sv_ast_t *node); 190 + void compile_lhs_set(sv_compiler_t *c, sv_ast_t *target, bool keep); 191 + void compile_member(sv_compiler_t *c, sv_ast_t *node); 192 + void compile_new(sv_compiler_t *c, sv_ast_t *node); 193 + void compile_object_destructure(sv_compiler_t *c, sv_ast_t *pat, bool keep); 194 + void compile_object(sv_compiler_t *c, sv_ast_t *node); 195 + void compile_optional_get(sv_compiler_t *c, sv_ast_t *node); 196 + void compile_optional(sv_compiler_t *c, sv_ast_t *node); 197 + void compile_stmt(sv_compiler_t *c, sv_ast_t *node); 198 + void compile_stmts(sv_compiler_t *c, sv_ast_list_t *list); 199 + void compile_switch(sv_compiler_t *c, sv_ast_t *node); 200 + void compile_tail_return_expr(sv_compiler_t *c, sv_ast_t *expr); 201 + void compile_template(sv_compiler_t *c, sv_ast_t *node); 202 + void compile_ternary(sv_compiler_t *c, sv_ast_t *node); 203 + void compile_try(sv_compiler_t *c, sv_ast_t *node); 204 + void compile_typeof(sv_compiler_t *c, sv_ast_t *node); 205 + void compile_unary(sv_compiler_t *c, sv_ast_t *node); 206 + void compile_update(sv_compiler_t *c, sv_ast_t *node); 207 + void compile_var_decl(sv_compiler_t *c, sv_ast_t *node); 208 + void compile_while(sv_compiler_t *c, sv_ast_t *node); 38 209 39 210 #endif
+188 -15
src/ant.c
··· 4787 4787 return sv_vm_call_explicit_this(js->vm, js, func, this_arg, call_args, call_nargs); 4788 4788 } 4789 4789 4790 - static int extract_array_args(ant_t *js, ant_value_t arr, ant_value_t **out_args) { 4790 + int extract_array_args(ant_t *js, ant_value_t arr, ant_value_t **out_args) { 4791 4791 int len = (int) get_array_length(js, arr); 4792 4792 if (len <= 0) return 0; 4793 4793 ··· 5686 5686 return js_mkundef(); 5687 5687 } 5688 5688 5689 + static ant_value_t legacy_accessor_this_obj(ant_t *js, ant_value_t this_val) { 5690 + uint8_t t = vtype(this_val); 5691 + if (t == T_UNDEF || t == T_NULL) { 5692 + return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert undefined or null to object"); 5693 + } 5694 + if (t == T_CFUNC) return js_cfunc_promote(js, this_val); 5695 + if (t == T_OBJ || t == T_ARR || t == T_FUNC) return js_as_obj(this_val); 5696 + return js_mkerr_typed(js, JS_ERR_TYPE, "Legacy accessor methods require an object receiver"); 5697 + } 5698 + 5699 + static bool legacy_accessor_key( 5700 + ant_t *js, ant_value_t key, 5701 + ant_value_t *key_val_out, 5702 + const char **key_str_out, 5703 + ant_offset_t *key_len_out, 5704 + ant_offset_t *sym_off_out 5705 + ) { 5706 + if (key_val_out) *key_val_out = key; 5707 + if (key_str_out) *key_str_out = NULL; 5708 + if (key_len_out) *key_len_out = 0; 5709 + if (sym_off_out) *sym_off_out = 0; 5710 + 5711 + if (vtype(key) == T_SYMBOL) { 5712 + if (sym_off_out) *sym_off_out = (ant_offset_t)vdata(key); 5713 + return true; 5714 + } 5715 + 5716 + ant_value_t key_val = (vtype(key) == T_STR) ? key : coerce_to_str(js, key); 5717 + if (is_err(key_val)) return false; 5718 + if (key_val_out) *key_val_out = key_val; 5719 + 5720 + ant_offset_t key_len = 0; 5721 + ant_offset_t key_off = vstr(js, key_val, &key_len); 5722 + 5723 + if (key_str_out) *key_str_out = (const char *)(uintptr_t)key_off; 5724 + if (key_len_out) *key_len_out = key_len; 5725 + 5726 + return true; 5727 + } 5728 + 5729 + static ant_value_t builtin_object___defineGetter__(ant_t *js, ant_value_t *args, int nargs) { 5730 + ant_value_t obj = legacy_accessor_this_obj(js, js->this_val); 5731 + if (is_err(obj)) return obj; 5732 + if (nargs < 2) return js_mkundef(); 5733 + 5734 + ant_value_t getter = args[1]; 5735 + uint8_t gt = vtype(getter); 5736 + 5737 + if (gt != T_FUNC && gt != T_CFUNC) { 5738 + return js_mkerr_typed(js, JS_ERR_TYPE, "Object.prototype.__defineGetter__: Expecting function"); 5739 + } 5740 + 5741 + ant_value_t key_val = js_mkundef(); 5742 + const char *key_str = NULL; 5743 + ant_offset_t key_len = 0; 5744 + ant_offset_t sym_off = 0; 5745 + 5746 + if (!legacy_accessor_key(js, args[0], &key_val, &key_str, &key_len, &sym_off)) { 5747 + return key_val; 5748 + } 5749 + 5750 + if (vtype(key_val) == T_SYMBOL) { 5751 + js_set_sym_getter_desc(js, js_as_obj(obj), key_val, getter, JS_DESC_E | JS_DESC_C); 5752 + } else js_set_getter_desc(js, js_as_obj(obj), key_str, (size_t)key_len, getter, JS_DESC_E | JS_DESC_C); 5753 + 5754 + return js_mkundef(); 5755 + } 5756 + 5757 + static ant_value_t builtin_object___defineSetter__(ant_t *js, ant_value_t *args, int nargs) { 5758 + ant_value_t obj = legacy_accessor_this_obj(js, js->this_val); 5759 + if (is_err(obj)) return obj; 5760 + if (nargs < 2) return js_mkundef(); 5761 + 5762 + ant_value_t setter = args[1]; 5763 + uint8_t st = vtype(setter); 5764 + 5765 + if (st != T_FUNC && st != T_CFUNC) { 5766 + return js_mkerr_typed(js, JS_ERR_TYPE, "Object.prototype.__defineSetter__: Expecting function"); 5767 + } 5768 + 5769 + ant_value_t key_val = js_mkundef(); 5770 + const char *key_str = NULL; 5771 + ant_offset_t key_len = 0; 5772 + ant_offset_t sym_off = 0; 5773 + 5774 + if (!legacy_accessor_key(js, args[0], &key_val, &key_str, &key_len, &sym_off)) { 5775 + return key_val; 5776 + } 5777 + 5778 + if (vtype(key_val) == T_SYMBOL) { 5779 + js_set_sym_setter_desc(js, js_as_obj(obj), key_val, setter, JS_DESC_E | JS_DESC_C); 5780 + } else js_set_setter_desc(js, js_as_obj(obj), key_str, (size_t)key_len, setter, JS_DESC_E | JS_DESC_C); 5781 + 5782 + return js_mkundef(); 5783 + } 5784 + 5785 + static ant_value_t legacy_lookup_accessor(ant_t *js, ant_value_t this_val, ant_value_t key, bool want_getter) { 5786 + ant_value_t obj = legacy_accessor_this_obj(js, this_val); 5787 + if (is_err(obj)) return obj; 5788 + 5789 + ant_value_t key_val = js_mkundef(); 5790 + const char *key_str = NULL; 5791 + ant_offset_t key_len = 0; 5792 + ant_offset_t sym_off = 0; 5793 + 5794 + if (!legacy_accessor_key(js, key, &key_val, &key_str, &key_len, &sym_off)) { 5795 + return key_val; 5796 + } 5797 + 5798 + for (ant_value_t cur = obj; is_object_type(cur); cur = get_proto(js, cur)) { 5799 + prop_meta_t meta; 5800 + bool has_meta = (vtype(key_val) == T_SYMBOL) 5801 + ? lookup_symbol_prop_meta(cur, sym_off, &meta) 5802 + : lookup_string_prop_meta(js, cur, key_str, (size_t)key_len, &meta); 5803 + if (has_meta) { 5804 + if (want_getter) return meta.has_getter ? meta.getter : js_mkundef(); 5805 + return meta.has_setter ? meta.setter : js_mkundef(); 5806 + } 5807 + 5808 + if (vtype(key_val) == T_SYMBOL) { 5809 + if (lkp_sym(js, cur, sym_off) != 0) return js_mkundef(); 5810 + } else { 5811 + if (array_obj_ptr(cur) && is_length_key(key_str, key_len)) return js_mkundef(); 5812 + if (array_obj_ptr(cur) && is_array_index(key_str, key_len)) { 5813 + unsigned long idx = 0; 5814 + if ( 5815 + parse_array_index(key_str, key_len, get_array_length(js, cur), &idx) && 5816 + arr_has(js, cur, (ant_offset_t)idx) 5817 + ) return js_mkundef(); 5818 + } 5819 + if (lkp(js, cur, key_str, key_len) != 0) return js_mkundef(); 5820 + } 5821 + } 5822 + 5823 + return js_mkundef(); 5824 + } 5825 + 5826 + static ant_value_t builtin_object___lookupGetter__(ant_t *js, ant_value_t *args, int nargs) { 5827 + if (nargs < 1) return js_mkundef(); 5828 + return legacy_lookup_accessor(js, js->this_val, args[0], true); 5829 + } 5830 + 5831 + static ant_value_t builtin_object___lookupSetter__(ant_t *js, ant_value_t *args, int nargs) { 5832 + if (nargs < 1) return js_mkundef(); 5833 + return legacy_lookup_accessor(js, js->this_val, args[0], false); 5834 + } 5835 + 5689 5836 static ant_value_t builtin_object_create(ant_t *js, ant_value_t *args, int nargs) { 5690 5837 if (nargs == 0) return js_mkerr(js, "Object.create requires a prototype argument"); 5691 5838 ··· 6526 6673 6527 6674 static ant_value_t builtin_object_getOwnPropertyDescriptors(ant_t *js, ant_value_t *args, int nargs) { 6528 6675 ant_value_t result = js_mkobj(js); 6529 - if (nargs == 0) return result; 6676 + if (nargs == 0 || vtype(args[0]) == T_NULL || vtype(args[0]) == T_UNDEF) { 6677 + return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot convert undefined or null to object"); 6678 + } 6530 6679 6531 6680 ant_value_t obj = args[0]; 6532 6681 uint8_t t = vtype(obj); 6682 + 6683 + if (t != T_OBJ && t != T_ARR && t != T_FUNC) { 6684 + obj = builtin_Object(js, &obj, 1); 6685 + if (is_err(obj)) return obj; 6686 + t = vtype(obj); 6687 + } 6688 + 6533 6689 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return result; 6534 - 6535 6690 ant_value_t names = builtin_object_getOwnPropertyNames(js, &obj, 1); 6536 6691 if (is_err(names)) return names; 6692 + 6537 6693 ant_value_t err = object_add_descriptors_for_keys(js, result, obj, names); 6538 6694 if (is_err(err)) return err; 6539 - 6695 + 6540 6696 ant_value_t symbols = builtin_object_getOwnPropertySymbols(js, &obj, 1); 6541 6697 if (is_err(symbols)) return symbols; 6698 + 6542 6699 err = object_add_descriptors_for_keys(js, result, obj, symbols); 6543 6700 if (is_err(err)) return err; 6544 6701 ··· 6939 7096 6940 7097 static ant_value_t builtin_array_slice(ant_t *js, ant_value_t *args, int nargs) { 6941 7098 ant_value_t arr = js->this_val; 6942 - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 7099 + ant_value_t string_val = unwrap_primitive(js, arr); 7100 + bool string_like = (vtype(string_val) == T_STR); 7101 + 7102 + if (!string_like && vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { 6943 7103 return js_mkerr(js, "slice called on non-array"); 6944 7104 } 6945 7105 6946 - ant_offset_t len = get_array_length(js, arr); 7106 + ant_offset_t len = 0; 7107 + ant_offset_t string_byte_len = 0; 7108 + ant_offset_t string_off = 0; 7109 + 7110 + if (string_like) { 7111 + string_off = vstr(js, string_val, &string_byte_len); 7112 + len = (ant_offset_t)utf16_strlen((const char *)(uintptr_t)string_off, string_byte_len); 7113 + } else len = get_array_length(js, arr); 6947 7114 6948 7115 ant_offset_t start = 0, end = len; 6949 7116 double dlen = D(len); 6950 7117 if (nargs >= 1 && vtype(args[0]) == T_NUM) { 6951 7118 double d = tod(args[0]); 6952 - if (d < 0) { 6953 - start = (ant_offset_t) (d + dlen < 0 ? 0 : d + dlen); 6954 - } else start = (ant_offset_t) (d > dlen ? dlen : d); 7119 + if (d < 0) start = (ant_offset_t) (d + dlen < 0 ? 0 : d + dlen); 7120 + else start = (ant_offset_t) (d > dlen ? dlen : d); 6955 7121 } 6956 7122 6957 7123 if (nargs >= 2 && vtype(args[1]) == T_NUM) { 6958 7124 double d = tod(args[1]); 6959 - if (d < 0) { 6960 - end = (ant_offset_t) (d + dlen < 0 ? 0 : d + dlen); 6961 - } else { 6962 - end = (ant_offset_t) (d > dlen ? dlen : d); 6963 - } 7125 + if (d < 0) end = (ant_offset_t) (d + dlen < 0 ? 0 : d + dlen); 7126 + else end = (ant_offset_t) (d > dlen ? dlen : d); 6964 7127 } 6965 7128 6966 7129 if (start > end) start = end; 6967 7130 ant_value_t result = array_alloc_like(js, arr); 7131 + 6968 7132 if (is_err(result)) return result; 6969 7133 ant_offset_t result_idx = 0; 6970 7134 6971 7135 for (ant_offset_t i = start; i < end; i++) { 6972 - ant_value_t elem = arr_get(js, arr, i); 7136 + ant_value_t elem = js_mkundef(); 7137 + if (string_like) { 7138 + uint32_t code_unit = utf16_code_unit_at((const char *)(uintptr_t)string_off, string_byte_len, i); 7139 + if (code_unit == 0xFFFFFFFF) break; 7140 + elem = js_string_from_utf16_code_unit(js, code_unit); 7141 + } else elem = arr_get(js, arr, i); 6973 7142 arr_set(js, result, result_idx, elem); 6974 7143 result_idx++; 6975 7144 } ··· 12569 12738 defmethod(js, object_proto, "hasOwnProperty", 14, js_mkfun(builtin_object_hasOwnProperty)); 12570 12739 defmethod(js, object_proto, "isPrototypeOf", 13, js_mkfun(builtin_object_isPrototypeOf)); 12571 12740 defmethod(js, object_proto, "propertyIsEnumerable", 20, js_mkfun(builtin_object_propertyIsEnumerable)); 12741 + defmethod(js, object_proto, "__defineGetter__", 16, js_mkfun(builtin_object___defineGetter__)); 12742 + defmethod(js, object_proto, "__defineSetter__", 16, js_mkfun(builtin_object___defineSetter__)); 12743 + defmethod(js, object_proto, "__lookupGetter__", 16, js_mkfun(builtin_object___lookupGetter__)); 12744 + defmethod(js, object_proto, "__lookupSetter__", 16, js_mkfun(builtin_object___lookupSetter__)); 12572 12745 12573 12746 ant_value_t proto_getter = js_mkfun(builtin_proto_getter); 12574 12747 ant_value_t proto_setter = js_mkfun(builtin_proto_setter);
+70
src/builtins/node/http.mjs
··· 110 110 throw new Error('node:http client transport is not implemented yet'); 111 111 } 112 112 113 + // compatibility stub only 114 + function createAgentState() { 115 + return Object.create(null); 116 + } 117 + 118 + export class OutgoingMessage extends EventEmitter {} 119 + 120 + export class ClientRequest extends OutgoingMessage { 121 + constructor() { 122 + super(); 123 + clientNotImplemented(); 124 + } 125 + } 126 + 127 + export class Agent extends EventEmitter { 128 + constructor(options = {}) { 129 + super(); 130 + this.options = options && typeof options === 'object' ? options : {}; 131 + this.requests = createAgentState(); 132 + this.sockets = createAgentState(); 133 + this.freeSockets = createAgentState(); 134 + this.keepAlive = !!this.options.keepAlive; 135 + this.keepAliveMsecs = this.options.keepAliveMsecs ?? 1000; 136 + this.maxSockets = this.options.maxSockets ?? Infinity; 137 + this.maxFreeSockets = this.options.maxFreeSockets ?? 256; 138 + this.maxTotalSockets = this.options.maxTotalSockets ?? Infinity; 139 + this.totalSocketCount = 0; 140 + this.defaultPort = 80; 141 + this.protocol = 'http:'; 142 + this.scheduling = this.options.scheduling ?? 'lifo'; 143 + } 144 + 145 + createConnection() { 146 + clientNotImplemented(); 147 + } 148 + 149 + createSocket(options, callback) { 150 + const error = new Error('node:http Agent client transport is not implemented yet'); 151 + if (typeof callback === 'function') callback(error); 152 + else throw error; 153 + } 154 + 155 + addRequest() { 156 + clientNotImplemented(); 157 + } 158 + 159 + removeSocket() {} 160 + 161 + keepSocketAlive() { 162 + return false; 163 + } 164 + 165 + reuseSocket() {} 166 + 167 + destroy() { 168 + return this; 169 + } 170 + 171 + getName(options = {}) { 172 + if (options.socketPath) return String(options.socketPath); 173 + const host = options.host ?? 'localhost'; 174 + const port = options.port ?? this.defaultPort; 175 + const localAddress = options.localAddress ? `:${options.localAddress}` : ''; 176 + const family = options.family ? `:${options.family}` : ''; 177 + return `${host}:${port}${localAddress}${family}`; 178 + } 179 + } 180 + 181 + export const globalAgent = new Agent(); 182 + 113 183 export class IncomingMessage extends EventEmitter { 114 184 constructor(socket, parsed) { 115 185 super();
+18 -1
src/builtins/node/http2.mjs
··· 9 9 export class Http2Server extends http.Server {} 10 10 export class Http2SecureServer extends https.Server {} 11 11 12 + export class Http2ServerRequest { 13 + constructor() { 14 + this.stream = undefined; 15 + this.authority = undefined; 16 + } 17 + } 18 + 19 + export class Http2ServerResponse {} 20 + 12 21 export class ClientHttp2Session {} 13 22 export class ClientHttp2Stream {} 14 23 export class ServerHttp2Session {} ··· 38 47 return {}; 39 48 } 40 49 41 - export const constants = Object.freeze({}); 50 + export const constants = Object.freeze({ 51 + NGHTTP2_NO_ERROR: 0, 52 + HTTP_STATUS_OK: 200, 53 + HTTP_STATUS_NO_CONTENT: 204, 54 + HTTP_STATUS_NOT_MODIFIED: 304 55 + }); 56 + 42 57 export const sensitiveHeaders = Object.freeze([]); 43 58 44 59 export default { 45 60 ClientHttp2Session, 46 61 ClientHttp2Stream, 47 62 Http2Server, 63 + Http2ServerRequest, 64 + Http2ServerResponse, 48 65 Http2SecureServer, 49 66 ServerHttp2Session, 50 67 ServerHttp2Stream,
+12
src/builtins/node/https.mjs
··· 105 105 } 106 106 } 107 107 108 + export class Agent extends http.Agent { 109 + constructor(options) { 110 + super(options); 111 + this.defaultPort = 443; 112 + this.protocol = 'https:'; 113 + this.maxCachedSessions = this.options.maxCachedSessions ?? 100; 114 + this._sessionCache = { map: Object.create(null), list: [] }; 115 + } 116 + } 117 + 118 + export const globalAgent = new Agent(); 119 + 108 120 export function createSecureContext(options) { 109 121 return tls.createContext(options); 110 122 }
+19 -4
src/core/features.ts
··· 4 4 typescript: 'transform' 5 5 }; 6 6 7 + // TODO: make most of these dynamic 7 8 // polyfill process.versions to satisfy modules that rely on node 8 - // runtime detection. values mirror node 25.2.0 defaults 9 + // runtime detection. values mirror node 25.9.0 defaults 10 + process.version = 'v25.9.0'; 11 + 9 12 process.versions = { 10 - node: '25.2.0', 11 13 ant: import.meta.env.VERSION, 12 - v8: '14.1.146.11-node.14', 14 + node: '25.9.0', 15 + brotli: '1.1.0', 16 + llhttp: '9.3.1', 17 + nghttp2: '1.68.0', 18 + simdjson: '0.12.0', 19 + pcre2: '10.47', 20 + libffi: '3.5.2', 21 + lmdb: '0.9.33', 22 + utf8proc: '2.10.0', 23 + zlib: '2.3.3', 24 + v8: '14.1.146.11-node.25', 13 25 uv: '1.52.0', 14 - modules: '141' 26 + modules: '141', 27 + napi: '10', 28 + wamr: '92f40918bbfad35546a1512b10bd25eaa31add4d', 29 + boringssl: '297b11798a0ed6bc7736aa57328909a4afbbf67a' 15 30 };
+2 -2
src/esm/commonjs.c
··· 26 26 base_path = (const char *)(uintptr_t)(path_off); 27 27 } 28 28 29 - ant_value_t ns = js_esm_import_sync_from(js, args[0], base_path); 29 + ant_value_t ns = js_esm_import_sync_from_require(js, args[0], base_path); 30 30 if (is_err(ns)) return ns; 31 31 32 32 if (vtype(ns) == T_OBJ) { ··· 52 52 base_path = (const char *)(uintptr_t)(data_off); 53 53 } 54 54 55 - ant_value_t resolved = js_esm_resolve_specifier(js, args[0], base_path); 55 + ant_value_t resolved = js_esm_resolve_specifier_require(js, args[0], base_path); 56 56 if (is_err(resolved)) return resolved; 57 57 if (vtype(resolved) != T_STR) return resolved; 58 58
+165 -20
src/esm/loader.c
··· 388 388 const char *capture, 389 389 size_t capture_len, 390 390 const char *base_path, 391 - bool allow_bare_specifiers 391 + bool allow_bare_specifiers, 392 + bool prefer_require 392 393 ) { 393 394 if (!target) return NULL; 394 395 ··· 417 418 yyjson_arr_foreach(target, idx, max, item) { 418 419 char *resolved = esm_resolve_exports_target( 419 420 item, package_dir, capture, 420 - capture_len, base_path, allow_bare_specifiers 421 + capture_len, base_path, allow_bare_specifiers, prefer_require 421 422 ); 422 423 if (resolved) return resolved; 423 424 } ··· 425 426 } 426 427 427 428 if (yyjson_is_obj(target)) { 428 - static const char *const conditions[] = {"import", "node", "default"}; 429 - 430 - for (size_t i = 0; i < sizeof(conditions) / sizeof(conditions[0]); i++) { 429 + static const char *const import_conditions[] = {"import", "node", "default"}; 430 + static const char *const require_conditions[] = {"require", "node", "default"}; 431 + 432 + const char *const *conditions = prefer_require 433 + ? require_conditions 434 + : import_conditions; 435 + 436 + size_t condition_count = prefer_require 437 + ? sizeof(require_conditions) / sizeof(require_conditions[0]) 438 + : sizeof(import_conditions) / sizeof(import_conditions[0]); 439 + 440 + for (size_t i = 0; i < condition_count; i++) { 431 441 yyjson_val *cond_target = yyjson_obj_get(target, conditions[i]); 432 442 if (!cond_target) continue; 433 443 char *resolved = esm_resolve_exports_target( 434 444 cond_target, package_dir, capture, 435 - capture_len, base_path, allow_bare_specifiers 445 + capture_len, base_path, allow_bare_specifiers, prefer_require 436 446 ); 437 447 if (resolved) return resolved; 438 448 } ··· 446 456 const char *request_key, 447 457 const char *package_dir, 448 458 const char *base_path, 449 - bool allow_bare_specifiers 459 + bool allow_bare_specifiers, 460 + bool prefer_require 450 461 ) { 451 462 if (!map_obj || !yyjson_is_obj(map_obj)) return NULL; 452 463 453 464 yyjson_val *exact = yyjson_obj_get(map_obj, request_key); 454 465 if (exact) return esm_resolve_exports_target( 455 466 exact, package_dir, "", 0, 456 - base_path, allow_bare_specifiers 467 + base_path, allow_bare_specifiers, prefer_require 457 468 ); 458 469 459 470 const char *best_capture = NULL; ··· 487 498 if (!best_target) return NULL; 488 499 return esm_resolve_exports_target( 489 500 best_target, package_dir, best_capture, 490 - best_capture_len, base_path, allow_bare_specifiers 501 + best_capture_len, base_path, allow_bare_specifiers, prefer_require 491 502 ); 492 503 } 493 504 ··· 505 516 return resolved; 506 517 } 507 518 508 - static char *esm_resolve_package_entrypoint(const char *package_dir, const char *subpath, const char *base_path) { 519 + static char *esm_resolve_package_entrypoint(const char *package_dir, const char *subpath, const char *base_path, bool prefer_require) { 509 520 char pkg_json_path[PATH_MAX]; 510 521 snprintf(pkg_json_path, sizeof(pkg_json_path), "%s/package.json", package_dir); 511 522 ··· 528 539 const char *key = yyjson_get_str(k); 529 540 if (key && key[0] == '.') { has_subpath_keys = true; break; } 530 541 } 531 - if (has_subpath_keys) resolved = esm_resolve_package_map(exports, subpath_key, package_dir, base_path, false); 532 - else if (!subpath || !subpath[0]) resolved = esm_resolve_exports_target(exports, package_dir, "", 0, base_path, false); 533 - } else if (!subpath || !subpath[0]) resolved = esm_resolve_exports_target(exports, package_dir, "", 0, base_path, false); 542 + if (has_subpath_keys) resolved = esm_resolve_package_map(exports, subpath_key, package_dir, base_path, false, prefer_require); 543 + else if (!subpath || !subpath[0]) resolved = esm_resolve_exports_target(exports, package_dir, "", 0, base_path, false, prefer_require); 544 + } else if (!subpath || !subpath[0]) resolved = esm_resolve_exports_target(exports, package_dir, "", 0, base_path, false, prefer_require); 534 545 535 546 if (doc) yyjson_doc_free(doc); 536 547 return resolved; ··· 552 563 return resolved; 553 564 } 554 565 555 - static char *esm_resolve_package_imports(const char *specifier, const char *base_path) { 566 + static char *esm_resolve_package_imports(const char *specifier, const char *base_path, bool prefer_require) { 556 567 char *start_dir = esm_get_base_dir(base_path); 557 568 if (!start_dir) return NULL; 558 569 ··· 570 581 yyjson_val *imports = (root && yyjson_is_obj(root)) ? yyjson_obj_get(root, "imports") : NULL; 571 582 char *resolved = NULL; 572 583 if (imports && yyjson_is_obj(imports)) { 573 - resolved = esm_resolve_package_map(imports, specifier, current, base_path, true); 584 + resolved = esm_resolve_package_map( 585 + imports, specifier, current, 586 + base_path, true, prefer_require 587 + ); 574 588 } 575 589 yyjson_doc_free(doc); 576 590 return resolved; ··· 586 600 return NULL; 587 601 } 588 602 589 - static char *esm_resolve_node_module(const char *specifier, const char *base_path) { 603 + static char *esm_resolve_node_module_cond(const char *specifier, const char *base_path, bool prefer_require) { 590 604 char package_name[PATH_MAX]; 591 605 const char *subpath = NULL; 592 606 if (!esm_split_package_specifier(specifier, package_name, sizeof(package_name), &subpath)) { ··· 600 614 free(start_dir); 601 615 if (!package_dir) return NULL; 602 616 603 - char *resolved = esm_resolve_package_entrypoint(package_dir, subpath, base_path); 617 + char *resolved = esm_resolve_package_entrypoint(package_dir, subpath, base_path, prefer_require); 604 618 free(package_dir); 605 619 return resolved; 606 620 } 607 621 622 + static char *esm_resolve_node_module(const char *specifier, const char *base_path) { 623 + return esm_resolve_node_module_cond(specifier, base_path, false); 624 + } 625 + 608 626 static char *esm_resolve_relative_path(const char *specifier, const char *base_path) { 609 627 char *base_copy = strdup(base_path); 610 628 if (!base_copy) return NULL; ··· 640 658 } 641 659 } 642 660 643 - static char *esm_resolve_path(const char *specifier, const char *base_path) { 661 + static char *esm_resolve_path_cond(const char *specifier, const char *base_path, bool prefer_require) { 644 662 if (!specifier || !specifier[0]) return NULL; 645 663 646 664 if (specifier[0] == '/') { ··· 652 670 } 653 671 654 672 if (specifier[0] == '#') { 655 - return esm_resolve_package_imports(specifier, base_path); 673 + return esm_resolve_package_imports(specifier, base_path, prefer_require); 656 674 } 657 675 658 - return esm_resolve_node_module(specifier, base_path); 676 + return esm_resolve_node_module_cond(specifier, base_path, prefer_require); 677 + } 678 + 679 + static char *esm_resolve_path(const char *specifier, const char *base_path) { 680 + return esm_resolve_path_cond(specifier, base_path, false); 681 + } 682 + 683 + static char *esm_resolve_path_require(const char *specifier, const char *base_path) { 684 + return esm_resolve_path_cond(specifier, base_path, true); 659 685 } 660 686 661 687 static bool esm_has_suffix(const char *path, const char *ext) { ··· 1262 1288 return ns; 1263 1289 } 1264 1290 1291 + ant_value_t js_esm_import_sync_cstr_from_require( 1292 + ant_t *js, 1293 + const char *specifier, 1294 + size_t spec_len, 1295 + const char *base_path 1296 + ) { 1297 + const ant_builtin_bundle_alias_t *bundle = NULL; 1298 + const ant_builtin_bundle_module_t *module = NULL; 1299 + 1300 + char *spec_copy = strndup(specifier, spec_len); 1301 + if (!spec_copy) return js_mkerr(js, "oom"); 1302 + 1303 + char *file_url_path = esm_file_url_to_path(js, spec_copy); 1304 + if (file_url_path) { 1305 + free(spec_copy); 1306 + spec_copy = file_url_path; 1307 + spec_len = strlen(spec_copy); 1308 + } 1309 + 1310 + bundle = esm_lookup_builtin_alias(spec_copy, spec_len); 1311 + if (bundle) { 1312 + module = esm_lookup_builtin_module(bundle->module_id); 1313 + if (!module) { 1314 + free(spec_copy); 1315 + return js_mkerr(js, "Invalid builtin module id"); 1316 + } 1317 + 1318 + ant_value_t ns = esm_get_or_load( 1319 + js, spec_copy, 1320 + bundle->source_name, 1321 + bundle->source_name, 1322 + module->format, 1323 + module->code, 1324 + module->code_len 1325 + ); 1326 + 1327 + free(spec_copy); 1328 + return ns; 1329 + } 1330 + 1331 + bool loaded = false; 1332 + ant_value_t lib = js_esm_load_registered_library(js, spec_copy, spec_len, &loaded); 1333 + if (loaded) { 1334 + free(spec_copy); 1335 + return lib; 1336 + } 1337 + 1338 + if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1339 + char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path_require); 1340 + if (!resolved_path) { 1341 + ant_value_t err = js_mkerr(js, "Cannot resolve module: %s", spec_copy); 1342 + free(spec_copy); 1343 + return err; 1344 + } 1345 + 1346 + ant_value_t ns = esm_get_or_load( 1347 + js, spec_copy, 1348 + resolved_path, 1349 + resolved_path, 1350 + MODULE_EVAL_FORMAT_UNKNOWN, 1351 + NULL, 0 1352 + ); 1353 + 1354 + free(resolved_path); 1355 + free(spec_copy); 1356 + 1357 + return ns; 1358 + } 1359 + 1265 1360 ant_value_t js_esm_import_sync_cstr(ant_t *js, const char *specifier, size_t spec_len) { 1266 1361 return js_esm_import_sync_cstr_from(js, specifier, spec_len, NULL); 1267 1362 } ··· 1275 1370 const char *spec_str = (const char *)(uintptr_t)(spec_off); 1276 1371 1277 1372 return js_esm_import_sync_cstr_from(js, spec_str, (size_t)spec_len, base_path); 1373 + } 1374 + 1375 + ant_value_t js_esm_import_sync_from_require(ant_t *js, ant_value_t specifier, const char *base_path) { 1376 + if (vtype(specifier) != T_STR) 1377 + return js_mkerr(js, "require() expects a string specifier"); 1378 + 1379 + ant_offset_t spec_len = 0; 1380 + ant_offset_t spec_off = vstr(js, specifier, &spec_len); 1381 + const char *spec_str = (const char *)(uintptr_t)(spec_off); 1382 + 1383 + return js_esm_import_sync_cstr_from_require(js, spec_str, (size_t)spec_len, base_path); 1278 1384 } 1279 1385 1280 1386 ant_value_t js_esm_import_sync(ant_t *js, ant_value_t specifier) { ··· 1360 1466 free(resolved_path); 1361 1467 return result; 1362 1468 } 1469 + 1470 + ant_value_t js_esm_resolve_specifier_require(ant_t *js, ant_value_t specifier, const char *base_path) { 1471 + const ant_builtin_bundle_alias_t *bundle = NULL; 1472 + if (vtype(specifier) != T_STR) { 1473 + return js_mkerr(js, "require.resolve() expects a string specifier"); 1474 + } 1475 + 1476 + ant_offset_t spec_len = 0; 1477 + ant_offset_t spec_off = vstr(js, specifier, &spec_len); 1478 + const char *spec_str = (const char *)(uintptr_t)(spec_off); 1479 + char *spec_copy = strndup(spec_str, (size_t)spec_len); 1480 + if (!spec_copy) return js_mkerr(js, "oom"); 1481 + 1482 + bundle = esm_lookup_builtin_alias(spec_copy, (size_t)spec_len); 1483 + if (bundle) { 1484 + ant_value_t result = js_mkstr(js, bundle->source_name, strlen(bundle->source_name)); 1485 + free(spec_copy); 1486 + return result; 1487 + } 1488 + 1489 + if (!base_path || !base_path[0]) base_path = esm_default_base_path(js); 1490 + char *resolved_path = esm_resolve(spec_copy, base_path, esm_resolve_path_require); 1491 + free(spec_copy); 1492 + 1493 + if (!resolved_path) { 1494 + return js_mkerr(js, "Cannot resolve module"); 1495 + } 1496 + 1497 + if (esm_is_url(resolved_path)) { 1498 + ant_value_t result = js_mkstr(js, resolved_path, strlen(resolved_path)); 1499 + free(resolved_path); 1500 + return result; 1501 + } 1502 + 1503 + ant_value_t result = js_esm_make_file_url(js, resolved_path); 1504 + free(resolved_path); 1505 + 1506 + return result; 1507 + }
+13 -4
src/main.c
··· 128 128 if (strcmp(key, "gc") == 0) { 129 129 if (strcmp(val, "disable") == 0) gc_disabled = true; 130 130 } 131 - 132 - if (strcmp(key, "dump/crprintf") == 0) { 131 + 132 + else if (strcmp(key, "dump/parse") == 0) { 133 + if (strcmp(val, "trace") == 0) sv_debug_enable(SV_DEBUG_PARSE); 134 + } 135 + 136 + else if (strcmp(key, "dump/compile") == 0) { 137 + if (strcmp(val, "trace") == 0) sv_debug_enable(SV_DEBUG_COMPILE); 138 + } 139 + 140 + else if (strcmp(key, "dump/crprintf") == 0) { 133 141 if (strcmp(val, "bytecode") == 0 || strcmp(val, "all") == 0) crprintf_set_debug(true); 134 142 if (strcmp(val, "hex") == 0 || strcmp(val, "all") == 0) crprintf_set_debug_hex(true); 135 - } 136 - 143 + } 144 + 137 145 else if (strcmp(key, "dump/vm") == 0) { 138 146 if (strcmp(val, "bytecode") == 0 || strcmp(val, "all") == 0) sv_debug_enable(SV_DEBUG_DUMP_BYTECODE); 139 147 if (strcmp(val, "jit") == 0 || strcmp(val, "all") == 0) sv_debug_enable(SV_DEBUG_DUMP_JIT); ··· 668 676 ant_standard_library("timers/promises", timers_promises_library); 669 677 ant_standard_library("readline/promises", readline_promises_library); 670 678 ant_standard_library("stream/promises", stream_promises_library); 679 + ant_standard_library("stream/web", stream_web_library); 671 680 672 681 ant_value_t snapshot_result = ant_load_snapshot(js); 673 682 if (vtype(snapshot_result) == T_ERR) {
+11 -9
src/modules/assert.c
··· 42 42 return js_mkerr(js, "ifError got unwanted exception: %s", msg ? msg : "(unknown)"); 43 43 } 44 44 45 - // TODO: make into global helper 46 45 static bool values_strict_equal(ant_t *js, ant_value_t a, ant_value_t b) { 47 46 uint8_t ta = vtype(a), tb = vtype(b); 48 47 if (ta != tb) return false; ··· 76 75 return false; 77 76 } 78 77 79 - // TODO: make into global helper 80 - static bool deep_equal(ant_t *js, ant_value_t a, ant_value_t b, bool strict, int depth) { 78 + static bool deep_equal_impl(ant_t *js, ant_value_t a, ant_value_t b, bool strict, int depth) { 81 79 if (depth > 64) return false; 82 80 uint8_t ta = vtype(a), tb = vtype(b); 83 81 ··· 85 83 ant_offset_t la = js_arr_len(js, a), lb = js_arr_len(js, b); 86 84 if (la != lb) return false; 87 85 for (ant_offset_t i = 0; i < la; i++) { 88 - if (!deep_equal(js, js_arr_get(js, a, i), js_arr_get(js, b, i), strict, depth + 1)) 86 + if (!deep_equal_impl(js, js_arr_get(js, a, i), js_arr_get(js, b, i), strict, depth + 1)) 89 87 return false; 90 88 } 91 89 return true; ··· 97 95 const char *key; size_t key_len; ant_value_t va; 98 96 while (js_prop_iter_next(&iter, &key, &key_len, &va)) { 99 97 ant_value_t vb = js_get(js, b, key); 100 - if (!deep_equal(js, va, vb, strict, depth + 1)) { 98 + if (!deep_equal_impl(js, va, vb, strict, depth + 1)) { 101 99 js_prop_iter_end(&iter); 102 100 return false; 103 101 } ··· 118 116 return strict ? values_strict_equal(js, a, b) : values_loose_equal(js, a, b); 119 117 } 120 118 119 + bool js_deep_equal(ant_t *js, ant_value_t a, ant_value_t b, bool strict) { 120 + return deep_equal_impl(js, a, b, strict, 0); 121 + } 122 + 121 123 static ant_value_t assert_equal(ant_t *js, ant_value_t *args, int nargs) { 122 124 if (nargs < 2) return js_mkundef(); 123 125 if (!values_loose_equal(js, args[0], args[1])) ··· 148 150 149 151 static ant_value_t assert_deep_equal(ant_t *js, ant_value_t *args, int nargs) { 150 152 if (nargs < 2) return js_mkundef(); 151 - if (!deep_equal(js, args[0], args[1], false, 0)) 153 + if (!js_deep_equal(js, args[0], args[1], false)) 152 154 return assertion_error(js, "Expected values to be deeply equal", nargs >= 3 ? args[2] : js_mkundef()); 153 155 return js_mkundef(); 154 156 } 155 157 156 158 static ant_value_t assert_not_deep_equal(ant_t *js, ant_value_t *args, int nargs) { 157 159 if (nargs < 2) return js_mkundef(); 158 - if (deep_equal(js, args[0], args[1], false, 0)) 160 + if (js_deep_equal(js, args[0], args[1], false)) 159 161 return assertion_error(js, "Expected values to not be deeply equal", nargs >= 3 ? args[2] : js_mkundef()); 160 162 return js_mkundef(); 161 163 } 162 164 163 165 static ant_value_t assert_deep_strict_equal(ant_t *js, ant_value_t *args, int nargs) { 164 166 if (nargs < 2) return js_mkundef(); 165 - if (!deep_equal(js, args[0], args[1], true, 0)) 167 + if (!js_deep_equal(js, args[0], args[1], true)) 166 168 return assertion_error(js, "Expected values to be deeply strictly equal", nargs >= 3 ? args[2] : js_mkundef()); 167 169 return js_mkundef(); 168 170 } 169 171 170 172 static ant_value_t assert_not_deep_strict_equal(ant_t *js, ant_value_t *args, int nargs) { 171 173 if (nargs < 2) return js_mkundef(); 172 - if (deep_equal(js, args[0], args[1], true, 0)) 174 + if (js_deep_equal(js, args[0], args[1], true)) 173 175 return assertion_error(js, "Expected values to not be deeply strictly equal", nargs >= 3 ? args[2] : js_mkundef()); 174 176 return js_mkundef(); 175 177 }
+131
src/modules/buffer.c
··· 2207 2207 return js_mknum((double)to_write); 2208 2208 } 2209 2209 2210 + static ant_value_t js_buffer_copy(ant_t *js, ant_value_t *args, int nargs) { 2211 + if (nargs < 1) return js_mkerr(js, "copy requires a target buffer"); 2212 + 2213 + TypedArrayData *src = buffer_get_typedarray_data(js_getthis(js)); 2214 + TypedArrayData *dst = buffer_get_typedarray_data(args[0]); 2215 + if (!src || !dst) return js_mkerr(js, "copy requires Buffer arguments"); 2216 + 2217 + size_t target_start = (nargs > 1 && vtype(args[1]) == T_NUM) ? (size_t)js_getnum(args[1]) : 0; 2218 + size_t source_start = (nargs > 2 && vtype(args[2]) == T_NUM) ? (size_t)js_getnum(args[2]) : 0; 2219 + size_t source_end = (nargs > 3 && vtype(args[3]) == T_NUM) ? (size_t)js_getnum(args[3]) : src->byte_length; 2220 + 2221 + if (target_start > dst->byte_length) target_start = dst->byte_length; 2222 + if (source_start > src->byte_length) source_start = src->byte_length; 2223 + if (source_end > src->byte_length) source_end = src->byte_length; 2224 + if (source_end < source_start) source_end = source_start; 2225 + 2226 + size_t src_len = source_end - source_start; 2227 + size_t dst_len = dst->byte_length - target_start; 2228 + size_t copy_len = src_len < dst_len ? src_len : dst_len; 2229 + if (copy_len == 0) return js_mknum(0); 2230 + 2231 + uint8_t *src_ptr = src->buffer->data + src->byte_offset + source_start; 2232 + uint8_t *dst_ptr = dst->buffer->data + dst->byte_offset + target_start; 2233 + memmove(dst_ptr, src_ptr, copy_len); 2234 + 2235 + return js_mknum((double)copy_len); 2236 + } 2237 + 2238 + static ant_value_t js_buffer_writeInt16BE(ant_t *js, ant_value_t *args, int nargs) { 2239 + if (nargs < 1) return js_mkerr(js, "writeInt16BE requires a value"); 2240 + 2241 + TypedArrayData *ta = buffer_get_typedarray_data(js_getthis(js)); 2242 + if (!ta) return js_mkerr(js, "Invalid Buffer"); 2243 + 2244 + int16_t value = (int16_t)js_to_int32(js_getnum(args[0])); 2245 + size_t offset = (nargs > 1 && vtype(args[1]) == T_NUM) ? (size_t)js_getnum(args[1]) : 0; 2246 + if (offset + 2 > ta->byte_length) return js_mkerr(js, "Offset out of bounds"); 2247 + 2248 + uint8_t *ptr = ta->buffer->data + ta->byte_offset + offset; 2249 + ptr[0] = (uint8_t)((value >> 8) & 0xff); 2250 + ptr[1] = (uint8_t)(value & 0xff); 2251 + 2252 + return js_mknum((double)(offset + 2)); 2253 + } 2254 + 2255 + static ant_value_t js_buffer_writeInt32BE(ant_t *js, ant_value_t *args, int nargs) { 2256 + if (nargs < 1) return js_mkerr(js, "writeInt32BE requires a value"); 2257 + 2258 + TypedArrayData *ta = buffer_get_typedarray_data(js_getthis(js)); 2259 + if (!ta) return js_mkerr(js, "Invalid Buffer"); 2260 + 2261 + int32_t value = js_to_int32(js_getnum(args[0])); 2262 + size_t offset = (nargs > 1 && vtype(args[1]) == T_NUM) ? (size_t)js_getnum(args[1]) : 0; 2263 + if (offset + 4 > ta->byte_length) return js_mkerr(js, "Offset out of bounds"); 2264 + 2265 + uint8_t *ptr = ta->buffer->data + ta->byte_offset + offset; 2266 + ptr[0] = (uint8_t)((value >> 24) & 0xff); 2267 + ptr[1] = (uint8_t)((value >> 16) & 0xff); 2268 + ptr[2] = (uint8_t)((value >> 8) & 0xff); 2269 + ptr[3] = (uint8_t)(value & 0xff); 2270 + 2271 + return js_mknum((double)(offset + 4)); 2272 + } 2273 + 2274 + static ant_value_t js_buffer_writeUInt32BE(ant_t *js, ant_value_t *args, int nargs) { 2275 + if (nargs < 1) return js_mkerr(js, "writeUInt32BE requires a value"); 2276 + 2277 + TypedArrayData *ta = buffer_get_typedarray_data(js_getthis(js)); 2278 + if (!ta) return js_mkerr(js, "Invalid Buffer"); 2279 + 2280 + uint32_t value = js_to_uint32(js_getnum(args[0])); 2281 + size_t offset = (nargs > 1 && vtype(args[1]) == T_NUM) ? (size_t)js_getnum(args[1]) : 0; 2282 + if (offset + 4 > ta->byte_length) return js_mkerr(js, "Offset out of bounds"); 2283 + 2284 + uint8_t *ptr = ta->buffer->data + ta->byte_offset + offset; 2285 + ptr[0] = (uint8_t)((value >> 24) & 0xff); 2286 + ptr[1] = (uint8_t)((value >> 16) & 0xff); 2287 + ptr[2] = (uint8_t)((value >> 8) & 0xff); 2288 + ptr[3] = (uint8_t)(value & 0xff); 2289 + 2290 + return js_mknum((double)(offset + 4)); 2291 + } 2292 + 2293 + static ant_value_t js_buffer_readInt16BE(ant_t *js, ant_value_t *args, int nargs) { 2294 + TypedArrayData *ta = buffer_get_typedarray_data(js_getthis(js)); 2295 + if (!ta) return js_mkerr(js, "Invalid Buffer"); 2296 + 2297 + size_t offset = (nargs > 0 && vtype(args[0]) == T_NUM) ? (size_t)js_getnum(args[0]) : 0; 2298 + if (offset + 2 > ta->byte_length) return js_mkerr(js, "Offset out of bounds"); 2299 + 2300 + uint8_t *ptr = ta->buffer->data + ta->byte_offset + offset; 2301 + int16_t value = (int16_t)((ptr[0] << 8) | ptr[1]); 2302 + 2303 + return js_mknum((double)value); 2304 + } 2305 + 2306 + static ant_value_t js_buffer_readInt32BE(ant_t *js, ant_value_t *args, int nargs) { 2307 + TypedArrayData *ta = buffer_get_typedarray_data(js_getthis(js)); 2308 + if (!ta) return js_mkerr(js, "Invalid Buffer"); 2309 + 2310 + size_t offset = (nargs > 0 && vtype(args[0]) == T_NUM) ? (size_t)js_getnum(args[0]) : 0; 2311 + if (offset + 4 > ta->byte_length) return js_mkerr(js, "Offset out of bounds"); 2312 + 2313 + uint8_t *ptr = ta->buffer->data + ta->byte_offset + offset; 2314 + int32_t value = (int32_t)(((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) | 2315 + ((uint32_t)ptr[2] << 8) | (uint32_t)ptr[3]); 2316 + 2317 + return js_mknum((double)value); 2318 + } 2319 + 2320 + static ant_value_t js_buffer_readUInt32BE(ant_t *js, ant_value_t *args, int nargs) { 2321 + TypedArrayData *ta = buffer_get_typedarray_data(js_getthis(js)); 2322 + if (!ta) return js_mkerr(js, "Invalid Buffer"); 2323 + 2324 + size_t offset = (nargs > 0 && vtype(args[0]) == T_NUM) ? (size_t)js_getnum(args[0]) : 0; 2325 + if (offset + 4 > ta->byte_length) return js_mkerr(js, "Offset out of bounds"); 2326 + 2327 + uint8_t *ptr = ta->buffer->data + ta->byte_offset + offset; 2328 + uint32_t value = ((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) | 2329 + ((uint32_t)ptr[2] << 8) | (uint32_t)ptr[3]; 2330 + 2331 + return js_mknum((double)value); 2332 + } 2333 + 2210 2334 // Buffer.isBuffer(obj) 2211 2335 static ant_value_t js_buffer_isBuffer(ant_t *js, ant_value_t *args, int nargs) { 2212 2336 if (nargs < 1) return js_false; ··· 2539 2663 js_set(js, buffer_proto, "toString", js_mkfun(js_buffer_toString)); 2540 2664 js_set(js, buffer_proto, "toBase64", js_mkfun(js_buffer_toBase64)); 2541 2665 js_set(js, buffer_proto, "write", js_mkfun(js_buffer_write)); 2666 + js_set(js, buffer_proto, "copy", js_mkfun(js_buffer_copy)); 2667 + js_set(js, buffer_proto, "writeInt16BE", js_mkfun(js_buffer_writeInt16BE)); 2668 + js_set(js, buffer_proto, "writeInt32BE", js_mkfun(js_buffer_writeInt32BE)); 2669 + js_set(js, buffer_proto, "writeUInt32BE", js_mkfun(js_buffer_writeUInt32BE)); 2670 + js_set(js, buffer_proto, "readInt16BE", js_mkfun(js_buffer_readInt16BE)); 2671 + js_set(js, buffer_proto, "readInt32BE", js_mkfun(js_buffer_readInt32BE)); 2672 + js_set(js, buffer_proto, "readUInt32BE", js_mkfun(js_buffer_readUInt32BE)); 2542 2673 2543 2674 js_set_sym(js, buffer_proto, get_toStringTag_sym(), js_mkstr(js, "Buffer", 6)); 2544 2675 js_set_sym(js, buffer_proto, get_iterator_sym(), ta_values_fn);
+82
src/modules/crypto.c
··· 409 409 return args[0]; 410 410 } 411 411 412 + // TODO: extend subtle 413 + static ant_value_t crypto_subtle_get_algorithm_name(ant_t *js, ant_value_t algorithm) { 414 + if (vtype(algorithm) == T_STR) return js_tostring_val(js, algorithm); 415 + if (is_object_type(algorithm)) { 416 + ant_value_t name = js_get(js, algorithm, "name"); 417 + if (is_err(name)) return name; 418 + return js_tostring_val(js, name); 419 + } 420 + return js_mkerr_typed(js, JS_ERR_TYPE, "Algorithm must be a string or object with a name"); 421 + } 422 + 423 + static ant_value_t crypto_subtle_digest_impl( 424 + ant_t *js, ant_value_t algorithm, ant_value_t data 425 + ) { 426 + ant_value_t algo_val = crypto_subtle_get_algorithm_name(js, algorithm); 427 + if (is_err(algo_val)) return algo_val; 428 + 429 + size_t algo_len = 0; 430 + const char *algo = js_getstr(js, algo_val, &algo_len); 431 + if (!algo || algo_len == 0) { 432 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid digest algorithm"); 433 + } 434 + 435 + char algo_name[16]; 436 + if (algo_len >= sizeof(algo_name)) { 437 + return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm"); 438 + } 439 + 440 + for (size_t i = 0; i < algo_len; i++) { 441 + char ch = algo[i]; 442 + if (ch >= 'a' && ch <= 'z') ch = (char)(ch - ('a' - 'A')); 443 + algo_name[i] = ch; 444 + } 445 + algo_name[algo_len] = '\0'; 446 + 447 + const char *evp_name = NULL; 448 + if (strcmp(algo_name, "SHA-1") == 0 || strcmp(algo_name, "SHA1") == 0) evp_name = "sha1"; 449 + else if (strcmp(algo_name, "SHA-256") == 0 || strcmp(algo_name, "SHA256") == 0) evp_name = "sha256"; 450 + else if (strcmp(algo_name, "SHA-384") == 0 || strcmp(algo_name, "SHA384") == 0) evp_name = "sha384"; 451 + else if (strcmp(algo_name, "SHA-512") == 0 || strcmp(algo_name, "SHA512") == 0) evp_name = "sha512"; 452 + else return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm"); 453 + 454 + const EVP_MD *md = EVP_get_digestbyname(evp_name); 455 + if (!md) return js_mkerr_typed(js, JS_ERR_TYPE, "Unsupported digest algorithm"); 456 + 457 + const uint8_t *bytes = NULL; 458 + size_t len = 0; 459 + if (!buffer_source_get_bytes(js, data, &bytes, &len)) { 460 + return js_mkerr_typed(js, JS_ERR_TYPE, "digest data must be an ArrayBuffer, TypedArray, DataView, or Buffer"); 461 + } 462 + 463 + unsigned char digest[EVP_MAX_MD_SIZE]; 464 + unsigned int digest_len = 0; 465 + if (EVP_Digest(bytes, len, digest, &digest_len, md, NULL) != 1) { 466 + return js_mkerr(js, "Digest failed"); 467 + } 468 + 469 + ArrayBufferData *buffer = create_array_buffer_data(digest_len); 470 + if (!buffer) return js_mkerr(js, "Out of memory"); 471 + if (digest_len > 0) memcpy(buffer->data, digest, digest_len); 472 + 473 + return create_arraybuffer_obj(js, buffer); 474 + } 475 + 476 + static ant_value_t js_crypto_subtle_digest(ant_t *js, ant_value_t *args, int nargs) { 477 + ant_value_t promise = js_mkpromise(js); 478 + if (nargs < 2) { 479 + js_reject_promise(js, promise, js_mkerr_typed(js, JS_ERR_TYPE, "subtle.digest requires algorithm and data")); 480 + return promise; 481 + } 482 + 483 + ant_value_t result = crypto_subtle_digest_impl(js, args[0], args[1]); 484 + if (is_err(result)) js_reject_promise(js, promise, result); 485 + else js_resolve_promise(js, promise, result); 486 + 487 + return promise; 488 + } 489 + 412 490 static ant_value_t js_hash_update(ant_t *js, ant_value_t *args, int nargs) { 413 491 ant_value_t this_val = js_getthis(js); 414 492 ant_hash_state_t *state = NULL; ··· 547 625 548 626 static ant_value_t create_crypto_obj(ant_t *js) { 549 627 ant_value_t crypto_obj = js_mkobj(js); 628 + ant_value_t subtle_obj = js_mkobj(js); 550 629 551 630 js_set(js, crypto_obj, "random", js_mkfun(js_crypto_random)); 552 631 js_set(js, crypto_obj, "randomBytes", js_mkfun(js_crypto_random_bytes)); ··· 554 633 js_set(js, crypto_obj, "randomUUID", js_mkfun(js_crypto_random_uuid)); 555 634 js_set(js, crypto_obj, "randomUUIDv7", js_mkfun(js_crypto_random_uuidv7)); 556 635 js_set(js, crypto_obj, "getRandomValues", js_mkfun(js_crypto_get_random_values)); 636 + js_set(js, subtle_obj, "digest", js_mkfun(js_crypto_subtle_digest)); 637 + js_set_sym(js, subtle_obj, get_toStringTag_sym(), js_mkstr(js, "SubtleCrypto", 12)); 638 + js_set(js, crypto_obj, "subtle", subtle_obj); 557 639 558 640 js_set_sym(js, crypto_obj, get_toStringTag_sym(), js_mkstr(js, "Crypto", 6)); 559 641 return crypto_obj;
+115 -6
src/modules/events.c
··· 69 69 ant_value_t js_key; 70 70 unsigned char *hash_key; 71 71 size_t hash_key_len; 72 + bool warned_max_listeners; 72 73 UT_hash_handle hh; 73 74 } EventType; 74 75 ··· 213 214 214 215 static bool is_eventtarget_instance(ant_value_t target) { 215 216 return js_check_brand(target, BRAND_EVENTTARGET); 217 + } 218 + 219 + static int eventemitter_get_max_listeners_impl(ant_value_t target) { 220 + ant_value_t slot = js_get_slot(target, SLOT_EVENT_MAX_LISTENERS); 221 + if (vtype(slot) == T_NUM) { 222 + int n = (int)js_getnum(slot); 223 + return n >= 0 ? n : EVENTS_DEFAULT_MAX_LISTENERS; 224 + } 225 + return EVENTS_DEFAULT_MAX_LISTENERS; 226 + } 227 + 228 + static ant_value_t eventemitter_get_listeners_array(ant_t *js, ant_value_t target, ant_value_t key) { 229 + ant_value_t result = js_mkarr(js); 230 + EventType *evt = NULL; 231 + 232 + if (!is_object_type(target) || !key) return result; 233 + evt = find_emitter_event_type(js, target, key); 234 + if (!evt) return result; 235 + 236 + for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { 237 + EventListenerEntry *entry = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); 238 + if (!entry) continue; 239 + js_arr_push(js, result, entry->callback); 240 + } 241 + 242 + return result; 216 243 } 217 244 218 245 static void js_init_event_obj(ant_t *js, ant_value_t obj, ant_value_t type_val, bool bubbles, bool cancelable) { ··· 663 690 items[0] = entry; 664 691 } 665 692 693 + int max_listeners = eventemitter_get_max_listeners_impl(target); 694 + if ( 695 + max_listeners > 0 && 696 + !evt->warned_max_listeners && 697 + (int)utarray_len(evt->listeners) > max_listeners 698 + ) { 699 + evt->warned_max_listeners = true; 700 + if (vtype(key) == T_STR) { 701 + fprintf( 702 + stderr, 703 + "Warning: Possible EventEmitter memory leak detected. " 704 + "%u '%s' listeners added. Use emitter.setMaxListeners() to increase limit.\n", 705 + (unsigned)utarray_len(evt->listeners), 706 + js_str(js, key) 707 + ); 708 + } else { 709 + fprintf( 710 + stderr, 711 + "Warning: Possible EventEmitter memory leak detected. " 712 + "%u listeners added for a Symbol event. Use emitter.setMaxListeners() to increase limit.\n", 713 + (unsigned)utarray_len(evt->listeners) 714 + ); 715 + } 716 + } 717 + 666 718 return true; 667 719 } 668 720 ··· 884 936 return js_mknum((double)utarray_len(evt->listeners)); 885 937 } 886 938 939 + static ant_value_t js_eventemitter_setMaxListeners(ant_t *js, ant_value_t *args, int nargs) { 940 + if (nargs < 1) return js_mkerr(js, "setMaxListeners requires 1 argument"); 941 + if (vtype(args[0]) != T_NUM) return js_mkerr(js, "n must be a number"); 942 + 943 + int n = (int)js_getnum(args[0]); 944 + if (n < 0) return js_mkerr(js, "n must be non-negative"); 945 + 946 + ant_value_t this_obj = js_getthis(js); 947 + if (!is_object_type(this_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "setMaxListeners requires an object receiver"); 948 + js_set_slot(this_obj, SLOT_EVENT_MAX_LISTENERS, js_mknum(n)); 949 + return this_obj; 950 + } 951 + 952 + static ant_value_t js_eventemitter_getMaxListeners(ant_t *js, ant_value_t *args, int nargs) { 953 + ant_value_t this_obj = js_getthis(js); 954 + if (!is_object_type(this_obj)) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS); 955 + return js_mknum(eventemitter_get_max_listeners_impl(this_obj)); 956 + } 957 + 958 + static ant_value_t js_eventemitter_rawListeners(ant_t *js, ant_value_t *args, int nargs) { 959 + if (nargs < 1) return js_mkarr(js); 960 + ant_value_t key = evt_key_from_arg(args[0]); 961 + if (!key) return js_mkarr(js); 962 + return eventemitter_get_listeners_array(js, js_getthis(js), key); 963 + } 964 + 887 965 static ant_value_t js_eventemitter_eventNames(ant_t *js, ant_value_t *args, int nargs) { 888 966 ant_value_t this_obj = js_getthis(js); 889 967 ant_value_t result = js_mkarr(js); ··· 1017 1095 return js_events_once_attach(js, promise, target, key, listener, signal); 1018 1096 } 1019 1097 1020 - // TODO: fix stub 1021 1098 static ant_value_t js_events_disposable_dispose(ant_t *js, ant_value_t *args, int nargs) { 1099 + ant_value_t state = js_get_slot(js_getcurrentfunc(js), SLOT_DATA); 1100 + if (!is_object_type(state)) return js_mkundef(); 1101 + 1102 + ant_value_t disposed = js_get_slot(state, SLOT_SETTLED); 1103 + if (vtype(disposed) == T_BOOL && disposed == js_true) return js_mkundef(); 1104 + js_set_slot(state, SLOT_SETTLED, js_true); 1105 + 1106 + ant_value_t signal = js_get(js, state, "signal"); 1107 + ant_value_t listener = js_get(js, state, "listener"); 1108 + if (abort_signal_is_signal(signal) && is_callable(listener)) 1109 + abort_signal_remove_listener(js, signal, listener); 1110 + 1022 1111 return js_mkundef(); 1023 1112 } 1024 1113 ··· 1026 1115 if (nargs < 2 || !abort_signal_is_signal(args[0]) || !is_callable(args[1])) 1027 1116 return js_mkerr_typed(js, JS_ERR_TYPE, "events.addAbortListener requires an AbortSignal and listener"); 1028 1117 1029 - if (abort_signal_is_aborted(args[0])) { 1118 + bool already_aborted = abort_signal_is_aborted(args[0]); 1119 + if (already_aborted) { 1030 1120 ant_value_t event = js_mkobj(js); 1031 1121 js_set(js, event, "type", js_mkstr(js, "abort", 5)); 1032 1122 sv_vm_call(js->vm, js, args[1], args[0], &event, 1, NULL, false); 1033 1123 } else abort_signal_add_listener(js, args[0], args[1]); 1034 1124 1125 + ant_value_t state = js_mkobj(js); 1126 + js_set_slot(state, SLOT_SETTLED, js_bool(already_aborted)); 1127 + js_set(js, state, "signal", args[0]); 1128 + js_set(js, state, "listener", args[1]); 1129 + 1035 1130 ant_value_t disposable = js_mkobj(js); 1036 - js_set(js, disposable, "dispose", js_mkfun(js_events_disposable_dispose)); 1131 + js_set(js, disposable, "dispose", js_heavy_mkfun(js, js_events_disposable_dispose, state)); 1037 1132 1038 1133 return disposable; 1039 1134 } 1040 1135 1041 - // TODO: fix stub 1042 1136 static ant_value_t js_events_set_max_listeners(ant_t *js, ant_value_t *args, int nargs) { 1137 + if (nargs < 1) return js_mkerr(js, "setMaxListeners requires at least 1 argument"); 1138 + if (vtype(args[0]) != T_NUM) return js_mkerr(js, "n must be a number"); 1139 + 1140 + int n = (int)js_getnum(args[0]); 1141 + if (n < 0) return js_mkerr(js, "n must be non-negative"); 1142 + 1143 + for (int i = 1; i < nargs; i++) { 1144 + if (!is_object_type(args[i])) continue; 1145 + js_set_slot(args[i], SLOT_EVENT_MAX_LISTENERS, js_mknum(n)); 1146 + } 1147 + 1043 1148 return js_mkundef(); 1044 1149 } 1045 1150 1046 - // TODO: fix stub 1047 1151 static ant_value_t js_events_get_max_listeners(ant_t *js, ant_value_t *args, int nargs) { 1048 - return js_mknum(10); 1152 + if (nargs < 1) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS); 1153 + if (!is_object_type(args[0])) return js_mknum(EVENTS_DEFAULT_MAX_LISTENERS); 1154 + return js_mknum(eventemitter_get_max_listeners_impl(args[0])); 1049 1155 } 1050 1156 1051 1157 // TODO: fix stub ··· 1084 1190 js_set(js, eventemitter_proto, "emit", js_mkfun(js_eventemitter_emit)); 1085 1191 js_set(js, eventemitter_proto, "removeAllListeners", js_mkfun(js_eventemitter_removeAllListeners)); 1086 1192 js_set(js, eventemitter_proto, "listenerCount", js_mkfun(js_eventemitter_listenerCount)); 1193 + js_set(js, eventemitter_proto, "setMaxListeners", js_mkfun(js_eventemitter_setMaxListeners)); 1194 + js_set(js, eventemitter_proto, "getMaxListeners", js_mkfun(js_eventemitter_getMaxListeners)); 1195 + js_set(js, eventemitter_proto, "rawListeners", js_mkfun(js_eventemitter_rawListeners)); 1087 1196 js_set(js, eventemitter_proto, "eventNames", js_mkfun(js_eventemitter_eventNames)); 1088 1197 js_set_sym(js, eventemitter_proto, get_toStringTag_sym(), js_mkstr(js, "EventEmitter", 12)); 1089 1198
+368 -44
src/modules/fs.c
··· 61 61 FS_OP_OPEN, 62 62 FS_OP_CLOSE, 63 63 FS_OP_MKDTEMP, 64 - FS_OP_CHMOD 64 + FS_OP_CHMOD, 65 + FS_OP_RENAME 65 66 } fs_op_type_t; 66 67 67 68 typedef struct fs_request_s { ··· 72 73 ant_value_t callback_fn; 73 74 74 75 char *path; 76 + char *path2; 75 77 char *data; 76 78 char *error_msg; 77 79 size_t data_len; ··· 198 200 199 201 static ant_value_t fs_mk_errno_error( 200 202 ant_t *js, int err_num, 203 + const char *syscall, const char *path, const char *dest 204 + ); 205 + 206 + static ant_value_t fs_mk_uv_error( 207 + ant_t *js, int uv_code, 201 208 const char *syscall, const char *path, const char *dest 202 209 ); 203 210 ··· 1314 1321 if (!req) return; 1315 1322 1316 1323 if (req->path) free(req->path); 1324 + if (req->path2) free(req->path2); 1317 1325 if (req->data) free(req->data); 1318 1326 if (req->error_msg) free(req->error_msg); 1319 1327 ··· 1354 1362 js_set(req->js, props, "code", js_mkstr(req->js, code, strlen(code))); 1355 1363 js_set(req->js, props, "errno", js_mknum((double)req->error_code)); 1356 1364 if (req->path) js_set(req->js, props, "path", js_mkstr(req->js, req->path, strlen(req->path))); 1365 + if (req->path2) js_set(req->js, props, "dest", js_mkstr(req->js, req->path2, strlen(req->path2))); 1357 1366 reject_value = js_mkerr_props(req->js, JS_ERR_TYPE, props, "%s", err_msg); 1358 1367 } else reject_value = js_mkerr(req->js, "%s", err_msg); 1359 1368 ··· 1509 1518 fs_request_fail(req, (int)uv_req->result); 1510 1519 } 1511 1520 1521 + uv_fs_req_cleanup(uv_req); 1522 + req->completed = 1; 1523 + complete_request(req); 1524 + } 1525 + 1526 + static void on_rename_complete(uv_fs_t *uv_req) { 1527 + fs_request_t *req = (fs_request_t *)uv_req->data; 1528 + 1529 + if (uv_req->result < 0) { 1530 + fs_request_fail(req, (int)uv_req->result); 1531 + } 1532 + 1512 1533 uv_fs_req_cleanup(uv_req); 1513 1534 req->completed = 1; 1514 1535 complete_request(req); ··· 1885 1906 return req->promise; 1886 1907 } 1887 1908 1888 - static ant_value_t builtin_fs_writeFileSync(ant_t *js, ant_value_t *args, int nargs) { 1889 - if (nargs < 2) return js_mkerr(js, "writeFileSync() requires path and data arguments"); 1890 - 1891 - if (vtype(args[0]) != T_STR) return js_mkerr(js, "writeFileSync() path must be a string"); 1892 - if (vtype(args[1]) != T_STR) return js_mkerr(js, "writeFileSync() data must be a string"); 1893 - 1909 + static ant_value_t fs_write_file_sync_impl( 1910 + ant_t *js, 1911 + ant_value_t *args, 1912 + int nargs, 1913 + const char *fn_name, 1914 + const char *mode 1915 + ) { 1916 + if (nargs < 2) return js_mkerr(js, "%s() requires path and data arguments", fn_name); 1917 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "%s() path must be a string", fn_name); 1918 + if (vtype(args[1]) != T_STR) return js_mkerr(js, "%s() data must be a string", fn_name); 1919 + 1894 1920 size_t path_len, data_len; 1895 1921 char *path = js_getstr(js, args[0], &path_len); 1896 1922 char *data = js_getstr(js, args[1], &data_len); 1897 - 1898 1923 if (!path || !data) return js_mkerr(js, "Failed to get arguments"); 1899 - 1924 + 1900 1925 char *path_cstr = strndup(path, path_len); 1901 1926 if (!path_cstr) return js_mkerr(js, "Out of memory"); 1902 - 1903 - FILE *file = fopen(path_cstr, "wb"); 1927 + 1928 + FILE *file = fopen(path_cstr, mode); 1904 1929 if (!file) { 1905 1930 ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 1906 1931 free(path_cstr); 1907 1932 return err; 1908 1933 } 1909 - 1934 + 1910 1935 size_t bytes_written = fwrite(data, 1, data_len, file); 1911 1936 fclose(file); 1912 1937 free(path_cstr); 1913 - 1938 + 1914 1939 if (bytes_written != data_len) { 1915 1940 return js_mkerr(js, "Failed to write entire file"); 1916 1941 } 1917 - 1942 + 1918 1943 return js_mkundef(); 1944 + } 1945 + 1946 + static ant_value_t builtin_fs_writeFileSync(ant_t *js, ant_value_t *args, int nargs) { 1947 + return fs_write_file_sync_impl(js, args, nargs, "writeFileSync", "wb"); 1919 1948 } 1920 1949 1921 1950 static ant_value_t builtin_fs_copyFileSync(ant_t *js, ant_value_t *args, int nargs) { ··· 1966 1995 1967 1996 fclose(in); fclose(out); 1968 1997 free(src_cstr); free(dest_cstr); 1998 + 1999 + return js_mkundef(); 2000 + } 2001 + 2002 + typedef struct { 2003 + bool recursive; 2004 + bool force; 2005 + bool error_on_exist; 2006 + } fs_cp_options_t; 2007 + 2008 + static void fs_parse_cp_options(ant_t *js, ant_value_t value, fs_cp_options_t *opts) { 2009 + opts->recursive = false; 2010 + opts->force = true; 2011 + opts->error_on_exist = false; 2012 + 2013 + if (vtype(value) != T_OBJ) return; 2014 + 2015 + ant_value_t recursive = js_get(js, value, "recursive"); 2016 + ant_value_t force = js_get(js, value, "force"); 2017 + ant_value_t error_on_exist = js_get(js, value, "errorOnExist"); 2018 + 2019 + if (!is_undefined(recursive)) opts->recursive = js_truthy(js, recursive); 2020 + if (!is_undefined(force)) opts->force = js_truthy(js, force); 2021 + if (!is_undefined(error_on_exist)) opts->error_on_exist = js_truthy(js, error_on_exist); 2022 + } 2023 + 2024 + static char *fs_join_path(const char *base, const char *name) { 2025 + size_t base_len = strlen(base); 2026 + size_t name_len = strlen(name); 2027 + bool need_sep = base_len > 0 && base[base_len - 1] != '/'; 2028 + size_t total_len = base_len + (need_sep ? 1 : 0) + name_len; 2029 + 2030 + char *joined = calloc(total_len + 1, 1); 2031 + if (!joined) return NULL; 2032 + 2033 + memcpy(joined, base, base_len); 2034 + if (need_sep) joined[base_len++] = '/'; 2035 + memcpy(joined + base_len, name, name_len); 2036 + joined[total_len] = '\0'; 2037 + return joined; 2038 + } 2039 + 2040 + static ant_value_t fs_copy_file_sync_impl( 2041 + ant_t *js, 2042 + const char *src_cstr, 2043 + const char *dest_cstr, 2044 + const fs_cp_options_t *opts, 2045 + const char *op_name 2046 + ) { 2047 + struct stat dest_st; 2048 + if (!opts->force && stat(dest_cstr, &dest_st) == 0) { 2049 + if (opts->error_on_exist) return fs_mk_errno_error(js, EEXIST, op_name, src_cstr, dest_cstr); 2050 + return js_mkundef(); 2051 + } 2052 + 2053 + FILE *in = fopen(src_cstr, "rb"); 2054 + if (!in) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2055 + 2056 + FILE *out = fopen(dest_cstr, "wb"); 2057 + if (!out) { 2058 + ant_value_t err = fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2059 + fclose(in); 2060 + return err; 2061 + } 2062 + 2063 + char buf[8192]; 2064 + size_t n = 0; 2065 + while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { 2066 + if (fwrite(buf, 1, n, out) != n) { 2067 + ant_value_t err = fs_mk_errno_error(js, errno ? errno : EIO, op_name, src_cstr, dest_cstr); 2068 + fclose(in); 2069 + fclose(out); 2070 + return err; 2071 + }} 2072 + 2073 + fclose(in); 2074 + fclose(out); 1969 2075 1970 2076 return js_mkundef(); 1971 2077 } 1972 2078 2079 + static ant_value_t builtin_fs_copyFile(ant_t *js, ant_value_t *args, int nargs) { 2080 + if (nargs < 2) return js_mkerr(js, "copyFile() requires src and dest arguments"); 2081 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "copyFile() src must be a string"); 2082 + if (vtype(args[1]) != T_STR) return js_mkerr(js, "copyFile() dest must be a string"); 2083 + 2084 + size_t src_len = 0, dest_len = 0; 2085 + const char *src = js_getstr(js, args[0], &src_len); 2086 + const char *dest = js_getstr(js, args[1], &dest_len); 2087 + if (!src || !dest) return js_mkerr(js, "Failed to get arguments"); 2088 + 2089 + char *src_cstr = strndup(src, src_len); 2090 + char *dest_cstr = strndup(dest, dest_len); 2091 + if (!src_cstr || !dest_cstr) { 2092 + free(src_cstr); 2093 + free(dest_cstr); 2094 + return js_mkerr(js, "Out of memory"); 2095 + } 2096 + 2097 + fs_cp_options_t opts = { 2098 + .recursive = false, 2099 + .force = true, 2100 + .error_on_exist = false, 2101 + }; 2102 + 2103 + ant_value_t promise = js_mkpromise(js); 2104 + ant_value_t result = fs_copy_file_sync_impl(js, src_cstr, dest_cstr, &opts, "copyFile"); 2105 + free(src_cstr); 2106 + free(dest_cstr); 2107 + 2108 + if (is_err(result)) js_reject_promise(js, promise, result); 2109 + else js_resolve_promise(js, promise, js_mkundef()); 2110 + 2111 + return promise; 2112 + } 2113 + 2114 + static ant_value_t fs_copy_path_sync_impl( 2115 + ant_t *js, 2116 + const char *src_cstr, 2117 + const char *dest_cstr, 2118 + const fs_cp_options_t *opts, 2119 + const char *op_name 2120 + ) { 2121 + struct stat src_st; 2122 + if (stat(src_cstr, &src_st) != 0) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2123 + 2124 + if ((src_st.st_mode & S_IFMT) == S_IFDIR) { 2125 + if (!opts->recursive) { 2126 + return js_mkerr(js, "%s() recursive option is required to copy directories", op_name); 2127 + } 2128 + 2129 + struct stat dest_st; 2130 + if (stat(dest_cstr, &dest_st) != 0) { 2131 + if (errno != ENOENT) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2132 + #ifdef _WIN32 2133 + if (_mkdir(dest_cstr) != 0) return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2134 + #else 2135 + if (mkdir(dest_cstr, (mode_t)(src_st.st_mode & 0777)) != 0) { 2136 + return fs_mk_errno_error(js, errno, op_name, src_cstr, dest_cstr); 2137 + } 2138 + #endif 2139 + } else if ((dest_st.st_mode & S_IFMT) != S_IFDIR) { 2140 + return fs_mk_errno_error(js, EEXIST, op_name, src_cstr, dest_cstr); 2141 + } 2142 + 2143 + uv_fs_t req; 2144 + int rc = uv_fs_scandir(NULL, &req, src_cstr, 0, NULL); 2145 + if (rc < 0) { 2146 + ant_value_t err = fs_mk_uv_error(js, rc, "scandir", src_cstr, dest_cstr); 2147 + uv_fs_req_cleanup(&req); 2148 + return err; 2149 + } 2150 + 2151 + uv_dirent_t dirent; 2152 + ant_value_t result = js_mkundef(); 2153 + while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { 2154 + if ( 2155 + strcmp(dirent.name, ".") == 0 || 2156 + strcmp(dirent.name, "..") == 0 2157 + ) continue; 2158 + 2159 + char *child_src = fs_join_path(src_cstr, dirent.name); 2160 + char *child_dest = fs_join_path(dest_cstr, dirent.name); 2161 + if (!child_src || !child_dest) { 2162 + free(child_src); 2163 + free(child_dest); 2164 + result = js_mkerr(js, "Out of memory"); 2165 + break; 2166 + } 2167 + 2168 + result = fs_copy_path_sync_impl(js, child_src, child_dest, opts, op_name); 2169 + free(child_src); 2170 + free(child_dest); 2171 + if (is_err(result)) break; 2172 + } 2173 + 2174 + uv_fs_req_cleanup(&req); 2175 + return result; 2176 + } 2177 + 2178 + return fs_copy_file_sync_impl(js, src_cstr, dest_cstr, opts, op_name); 2179 + } 2180 + 2181 + static ant_value_t fs_cp_sync_common( 2182 + ant_t *js, 2183 + ant_value_t *args, 2184 + int nargs, 2185 + const char *fn_name 2186 + ) { 2187 + if (nargs < 2) return js_mkerr(js, "%s() requires src and dest arguments", fn_name); 2188 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "%s() src must be a string", fn_name); 2189 + if (vtype(args[1]) != T_STR) return js_mkerr(js, "%s() dest must be a string", fn_name); 2190 + 2191 + size_t src_len = 0, dest_len = 0; 2192 + const char *src = js_getstr(js, args[0], &src_len); 2193 + const char *dest = js_getstr(js, args[1], &dest_len); 2194 + if (!src || !dest) return js_mkerr(js, "Failed to get arguments"); 2195 + 2196 + fs_cp_options_t opts; 2197 + fs_parse_cp_options(js, nargs >= 3 ? args[2] : js_mkundef(), &opts); 2198 + 2199 + char *src_cstr = strndup(src, src_len); 2200 + char *dest_cstr = strndup(dest, dest_len); 2201 + if (!src_cstr || !dest_cstr) { 2202 + free(src_cstr); 2203 + free(dest_cstr); 2204 + return js_mkerr(js, "Out of memory"); 2205 + } 2206 + 2207 + ant_value_t result = fs_copy_path_sync_impl(js, src_cstr, dest_cstr, &opts, fn_name); 2208 + free(src_cstr); 2209 + free(dest_cstr); 2210 + return result; 2211 + } 2212 + 2213 + static ant_value_t builtin_fs_cpSync(ant_t *js, ant_value_t *args, int nargs) { 2214 + return fs_cp_sync_common(js, args, nargs, "cpSync"); 2215 + } 2216 + 2217 + static ant_value_t builtin_fs_cp(ant_t *js, ant_value_t *args, int nargs) { 2218 + ant_value_t promise = js_mkpromise(js); 2219 + ant_value_t result = fs_cp_sync_common(js, args, nargs, "cp"); 2220 + if (is_err(result)) js_reject_promise(js, promise, result); 2221 + else js_resolve_promise(js, promise, js_mkundef()); 2222 + return promise; 2223 + } 2224 + 1973 2225 static ant_value_t builtin_fs_renameSync(ant_t *js, ant_value_t *args, int nargs) { 1974 2226 if (nargs < 2) return js_mkerr(js, "renameSync() requires oldPath and newPath arguments"); 1975 2227 ··· 2005 2257 return js_mkundef(); 2006 2258 } 2007 2259 2260 + static ant_value_t builtin_fs_rename(ant_t *js, ant_value_t *args, int nargs) { 2261 + if (nargs < 2) return js_mkerr(js, "rename() requires oldPath and newPath arguments"); 2262 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "rename() oldPath must be a string"); 2263 + if (vtype(args[1]) != T_STR) return js_mkerr(js, "rename() newPath must be a string"); 2264 + 2265 + size_t old_len = 0, new_len = 0; 2266 + const char *old_path = js_getstr(js, args[0], &old_len); 2267 + const char *new_path = js_getstr(js, args[1], &new_len); 2268 + if (!old_path || !new_path) return js_mkerr(js, "Failed to get arguments"); 2269 + 2270 + fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2271 + if (!req) return js_mkerr(js, "Out of memory"); 2272 + 2273 + req->js = js; 2274 + req->op_type = FS_OP_RENAME; 2275 + req->promise = js_mkpromise(js); 2276 + req->path = strndup(old_path, old_len); 2277 + req->path2 = strndup(new_path, new_len); 2278 + req->uv_req.data = req; 2279 + 2280 + if (!req->path || !req->path2) { 2281 + free_fs_request(req); 2282 + return js_mkerr(js, "Out of memory"); 2283 + } 2284 + 2285 + utarray_push_back(pending_requests, &req); 2286 + int result = uv_fs_rename(uv_default_loop(), &req->uv_req, req->path, req->path2, on_rename_complete); 2287 + 2288 + if (result < 0) { 2289 + fs_request_fail(req, result); 2290 + req->completed = 1; 2291 + complete_request(req); 2292 + } 2293 + 2294 + return req->promise; 2295 + } 2296 + 2008 2297 static double fs_time_arg_to_seconds(ant_t *js, ant_value_t v) { 2009 2298 if (is_date_instance(v)) { 2010 2299 ant_value_t t = js_get_slot(js_as_obj(v), SLOT_DATA); ··· 2052 2341 } 2053 2342 2054 2343 static ant_value_t builtin_fs_appendFileSync(ant_t *js, ant_value_t *args, int nargs) { 2055 - if (nargs < 2) return js_mkerr(js, "appendFileSync() requires path and data arguments"); 2056 - 2057 - if (vtype(args[0]) != T_STR) return js_mkerr(js, "appendFileSync() path must be a string"); 2058 - if (vtype(args[1]) != T_STR) return js_mkerr(js, "appendFileSync() data must be a string"); 2059 - 2060 - size_t path_len, data_len; 2061 - char *path = js_getstr(js, args[0], &path_len); 2062 - char *data = js_getstr(js, args[1], &data_len); 2063 - 2064 - if (!path || !data) return js_mkerr(js, "Failed to get arguments"); 2065 - 2066 - char *path_cstr = strndup(path, path_len); 2067 - if (!path_cstr) return js_mkerr(js, "Out of memory"); 2068 - 2069 - FILE *file = fopen(path_cstr, "ab"); 2070 - if (!file) { 2071 - ant_value_t err = fs_mk_errno_error(js, errno, "open", path_cstr, NULL); 2072 - free(path_cstr); 2073 - return err; 2074 - } 2075 - 2076 - size_t bytes_written = fwrite(data, 1, data_len, file); 2077 - fclose(file); 2078 - free(path_cstr); 2079 - 2080 - if (bytes_written != data_len) { 2081 - return js_mkerr(js, "Failed to write entire file"); 2082 - } 2083 - 2084 - return js_mkundef(); 2344 + return fs_write_file_sync_impl(js, args, nargs, "appendFileSync", "ab"); 2345 + } 2346 + 2347 + static ant_value_t builtin_fs_appendFile(ant_t *js, ant_value_t *args, int nargs) { 2348 + ant_value_t promise = js_mkpromise(js); 2349 + ant_value_t result = fs_write_file_sync_impl(js, args, nargs, "appendFile", "ab"); 2350 + if (is_err(result)) js_reject_promise(js, promise, result); 2351 + else js_resolve_promise(js, promise, js_mkundef()); 2352 + return promise; 2085 2353 } 2086 2354 2087 2355 static ant_value_t builtin_fs_writeFile(ant_t *js, ant_value_t *args, int nargs) { ··· 2701 2969 complete_request(req); 2702 2970 } 2703 2971 2972 + return req->promise; 2973 + } 2974 + 2975 + static ant_value_t builtin_fs_fstatSync(ant_t *js, ant_value_t *args, int nargs) { 2976 + if (nargs < 1) return js_mkerr(js, "fstatSync() requires an fd argument"); 2977 + if (vtype(args[0]) != T_NUM) return js_mkerr(js, "fstatSync() fd must be a number"); 2978 + 2979 + uv_file fd = (uv_file)(int)js_getnum(args[0]); 2980 + uv_fs_t req; 2981 + int result = uv_fs_fstat(NULL, &req, fd, NULL); 2982 + 2983 + if (result < 0) { 2984 + ant_value_t err = fs_mk_uv_error(js, result, "fstat", NULL, NULL); 2985 + uv_fs_req_cleanup(&req); 2986 + return err; 2987 + } 2988 + 2989 + ant_value_t stat_obj = fs_stats_object_from_uv(js, &req.statbuf); 2990 + uv_fs_req_cleanup(&req); 2991 + return stat_obj; 2992 + } 2993 + 2994 + static ant_value_t builtin_fs_fstat(ant_t *js, ant_value_t *args, int nargs) { 2995 + if (nargs < 1) return js_mkerr(js, "fstat() requires an fd argument"); 2996 + if (vtype(args[0]) != T_NUM) return js_mkerr(js, "fstat() fd must be a number"); 2997 + 2998 + fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2999 + if (!req) return js_mkerr(js, "Out of memory"); 3000 + 3001 + req->js = js; 3002 + req->op_type = FS_OP_STAT; 3003 + req->promise = js_mkpromise(js); 3004 + req->fd = (uv_file)(int)js_getnum(args[0]); 3005 + req->uv_req.data = req; 3006 + 3007 + utarray_push_back(pending_requests, &req); 3008 + int result = uv_fs_fstat(uv_default_loop(), &req->uv_req, req->fd, on_stat_complete); 3009 + 3010 + if (result < 0) { 3011 + fs_request_fail(req, result); 3012 + req->completed = 1; 3013 + complete_request(req); 3014 + } 3015 + 2704 3016 return req->promise; 2705 3017 } 2706 3018 ··· 3971 4283 } 3972 4284 3973 4285 static void fs_set_promise_methods(ant_t *js, ant_value_t lib) { 4286 + js_set(js, lib, "appendFile", js_mkfun(builtin_fs_appendFile)); 4287 + js_set(js, lib, "cp", js_mkfun(builtin_fs_cp)); 4288 + js_set(js, lib, "copyFile", js_mkfun(builtin_fs_copyFile)); 3974 4289 js_set(js, lib, "readFile", js_mkfun(builtin_fs_readFile)); 3975 4290 js_set(js, lib, "open", js_mkfun(builtin_fs_open_fd)); 3976 4291 js_set(js, lib, "close", js_mkfun(builtin_fs_close_fd)); 3977 4292 js_set(js, lib, "writeFile", js_mkfun(builtin_fs_writeFile)); 3978 4293 js_set(js, lib, "write", js_mkfun(builtin_fs_write_fd)); 3979 4294 js_set(js, lib, "writev", js_mkfun(builtin_fs_writev_fd)); 4295 + js_set(js, lib, "rename", js_mkfun(builtin_fs_rename)); 3980 4296 js_set(js, lib, "rm", js_mkfun(builtin_fs_rm)); 3981 4297 js_set(js, lib, "unlink", js_mkfun(builtin_fs_unlink)); 3982 4298 js_set(js, lib, "mkdir", js_mkfun(builtin_fs_mkdir)); ··· 3984 4300 js_set(js, lib, "rmdir", js_mkfun(builtin_fs_rmdir)); 3985 4301 js_set(js, lib, "stat", js_mkfun(builtin_fs_stat)); 3986 4302 js_set(js, lib, "lstat", js_mkfun(builtin_fs_lstat)); 4303 + js_set(js, lib, "fstat", js_mkfun(builtin_fs_fstat)); 3987 4304 js_set(js, lib, "utimes", js_mkfun(builtin_fs_utimes)); 3988 4305 js_set(js, lib, "futimes", js_mkfun(builtin_fs_futimes)); 3989 4306 js_set(js, lib, "exists", js_mkfun(builtin_fs_exists)); ··· 3997 4314 static void fs_set_callback_compatible_methods(ant_t *js, ant_value_t lib) { 3998 4315 ant_value_t realpath = fs_make_callback_wrapper(js, js_mkfun(builtin_fs_realpath), false); 3999 4316 4317 + js_set(js, lib, "appendFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_appendFile), false)); 4318 + js_set(js, lib, "cp", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_cp), false)); 4319 + js_set(js, lib, "copyFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_copyFile), false)); 4000 4320 js_set(js, lib, "readFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readFile), false)); 4001 4321 js_set(js, lib, "open", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_open_fd), false)); 4002 4322 js_set(js, lib, "close", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_close_fd), false)); 4003 4323 js_set(js, lib, "writeFile", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_writeFile), false)); 4004 4324 js_set(js, lib, "write", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_write_fd), false)); 4005 4325 js_set(js, lib, "writev", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_writev_fd), false)); 4326 + js_set(js, lib, "rename", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rename), false)); 4006 4327 js_set(js, lib, "rm", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rm), false)); 4007 4328 js_set(js, lib, "unlink", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_unlink), false)); 4008 4329 js_set(js, lib, "mkdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_mkdir), false)); ··· 4010 4331 js_set(js, lib, "rmdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rmdir), false)); 4011 4332 js_set(js, lib, "stat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_stat), false)); 4012 4333 js_set(js, lib, "lstat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_lstat), false)); 4334 + js_set(js, lib, "fstat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_fstat), false)); 4013 4335 js_set(js, lib, "utimes", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_utimes), false)); 4014 4336 js_set(js, lib, "futimes", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_futimes), false)); 4015 4337 js_set(js, lib, "exists", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_exists), true)); ··· 4086 4408 js_set(js, lib, "writeSync", js_mkfun(builtin_fs_writeSync)); 4087 4409 js_set(js, lib, "writevSync", js_mkfun(builtin_fs_writevSync)); 4088 4410 js_set(js, lib, "appendFileSync", js_mkfun(builtin_fs_appendFileSync)); 4411 + js_set(js, lib, "cpSync", js_mkfun(builtin_fs_cpSync)); 4089 4412 js_set(js, lib, "copyFileSync", js_mkfun(builtin_fs_copyFileSync)); 4090 4413 js_set(js, lib, "renameSync", js_mkfun(builtin_fs_renameSync)); 4091 4414 js_set(js, lib, "rmSync", js_mkfun(builtin_fs_rmSync)); ··· 4095 4418 js_set(js, lib, "rmdirSync", js_mkfun(builtin_fs_rmdirSync)); 4096 4419 js_set(js, lib, "statSync", js_mkfun(builtin_fs_statSync)); 4097 4420 js_set(js, lib, "lstatSync", js_mkfun(builtin_fs_lstatSync)); 4421 + js_set(js, lib, "fstatSync", js_mkfun(builtin_fs_fstatSync)); 4098 4422 js_set(js, lib, "utimesSync", js_mkfun(builtin_fs_utimesSync)); 4099 4423 js_set(js, lib, "futimesSync", js_mkfun(builtin_fs_futimesSync)); 4100 4424 js_set(js, lib, "existsSync", js_mkfun(builtin_fs_existsSync));
+107
src/modules/globals.c
··· 3 3 #include <string.h> 4 4 #include <stdio.h> 5 5 #include <stdlib.h> 6 + #include <stdbool.h> 6 7 #include <time.h> 7 8 8 9 #include "ant.h" ··· 150 151 return this; 151 152 } 152 153 154 + static size_t intl_utf8_segment_len(const char *input, size_t remaining) { 155 + if (remaining == 0) return 0; 156 + 157 + const unsigned char *s = (const unsigned char *)input; 158 + unsigned char c = s[0]; 159 + size_t len = 1; 160 + 161 + if ((c & 0x80) == 0) return 1; 162 + if ((c & 0xe0) == 0xc0) len = 2; 163 + else if ((c & 0xf0) == 0xe0) len = 3; 164 + else if ((c & 0xf8) == 0xf0) len = 4; 165 + 166 + if (len > remaining) return 1; 167 + for (size_t i = 1; i < len; i++) if ((s[i] & 0xc0) != 0x80) return 1; 168 + 169 + return len; 170 + } 171 + 172 + static bool intl_ascii_is_word_byte(const char *segment, size_t len) { 173 + if (len != 1) return true; 174 + 175 + unsigned char c = (unsigned char)segment[0]; 176 + return 177 + (c >= '0' && c <= '9') || 178 + (c >= 'A' && c <= 'Z') || 179 + (c >= 'a' && c <= 'z') || 180 + c == '_'; 181 + } 182 + 183 + static const char *intl_segmenter_granularity(ant_t *js, ant_value_t segmenter, size_t *len) { 184 + ant_value_t granularity = js_get(js, segmenter, "granularity"); 185 + if (vtype(granularity) != T_STR) { 186 + if (len) *len = 8; 187 + return "grapheme"; 188 + } 189 + 190 + return js_getstr(js, granularity, len); 191 + } 192 + 193 + static ant_value_t intl_segmenter_segment(ant_t *js, ant_value_t *args, int nargs) { 194 + ant_value_t input = nargs > 0 ? js_tostring_val(js, args[0]) : js_mkstr(js, "", 0); 195 + if (is_err(input)) return input; 196 + 197 + size_t input_len = 0; 198 + char *input_str = js_getstr(js, input, &input_len); 199 + ant_value_t segments = js_mkarr(js); 200 + 201 + ant_value_t this = js_getthis(js); 202 + size_t granularity_len = 0; 203 + const char *granularity = intl_segmenter_granularity(js, this, &granularity_len); 204 + bool word_granularity = granularity_len == 4 && memcmp(granularity, "word", 4) == 0; 205 + 206 + for (size_t offset = 0; offset < input_len;) { 207 + size_t segment_len = intl_utf8_segment_len(input_str + offset, input_len - offset); 208 + ant_value_t record = js_mkobj(js); 209 + 210 + js_set(js, record, "segment", js_mkstr(js, input_str + offset, segment_len)); 211 + js_set(js, record, "index", js_mknum((double)offset)); 212 + js_set(js, record, "input", input); 213 + 214 + if (word_granularity) js_set( 215 + js, record, "isWordLike", 216 + js_bool(intl_ascii_is_word_byte(input_str + offset, segment_len)) 217 + ); 218 + 219 + js_arr_push(js, segments, record); 220 + offset += segment_len; 221 + } 222 + 223 + return segments; 224 + } 225 + 226 + static ant_value_t intl_segmenter_resolvedOptions(ant_t *js, ant_value_t *args, int nargs) { 227 + ant_value_t obj = js_mkobj(js); 228 + ant_value_t this = js_getthis(js); 229 + 230 + size_t granularity_len = 0; 231 + const char *granularity = intl_segmenter_granularity(js, this, &granularity_len); 232 + 233 + js_set(js, obj, "locale", js_mkstr(js, "en-US", 5)); 234 + js_set(js, obj, "granularity", js_mkstr(js, granularity, granularity_len)); 235 + 236 + return obj; 237 + } 238 + 239 + static ant_value_t intl_segmenter_constructor(ant_t *js, ant_value_t *args, int nargs) { 240 + ant_value_t this = js_getthis(js); 241 + const char *granularity = "grapheme"; 242 + size_t granularity_len = 8; 243 + 244 + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 245 + ant_value_t option = js_get(js, args[1], "granularity"); 246 + if (vtype(option) == T_STR) granularity = js_getstr(js, option, &granularity_len); 247 + } 248 + 249 + js_set(js, this, "granularity", js_mkstr(js, granularity, granularity_len)); 250 + js_set(js, this, "segment", js_mkfun(intl_segmenter_segment)); 251 + js_set(js, this, "resolvedOptions", js_mkfun(intl_segmenter_resolvedOptions)); 252 + 253 + return this; 254 + } 255 + 153 256 void init_globals_module(void) { 154 257 ant_t *js = rt->js; 155 258 ant_value_t global = js_glob(js); ··· 159 262 160 263 ant_value_t intl = js_mkobj(js); 161 264 ant_value_t dtf_ctor = js_heavy_mkfun(js, intl_dtf_constructor, js_mkundef()); 265 + ant_value_t segmenter_ctor = js_heavy_mkfun(js, intl_segmenter_constructor, js_mkundef()); 162 266 163 267 js_mark_constructor(dtf_ctor, true); 268 + js_mark_constructor(segmenter_ctor, true); 269 + 164 270 js_set(js, intl, "DateTimeFormat", dtf_ctor); 271 + js_set(js, intl, "Segmenter", segmenter_ctor); 165 272 js_set(js, global, "Intl", intl); 166 273 }
+384 -125
src/modules/io.c
··· 13 13 #include "output.h" 14 14 #include "internal.h" 15 15 #include "runtime.h" 16 + #include "gc/roots.h" 16 17 #include "silver/engine.h" 17 18 #include "modules/io.h" 18 19 #include "modules/symbol.h" 19 20 20 21 bool io_no_color = false; 22 + 23 + static ant_value_t g_console_proto = 0; 24 + static ant_value_t g_console_ctor = 0; 21 25 22 26 #define JSON_KEY "\x1b[0m" 23 27 #define JSON_STRING "\x1b[32m" ··· 30 34 #define JSON_REF "\x1b[90m" 31 35 #define JSON_WHITE "\x1b[97m" 32 36 33 - static inline bool io_is_digit_ascii(char c) { 34 - return c >= '0' && c <= '9'; 37 + static inline bool io_is_digit_ascii(char c) { 38 + return c >= '0' && c <= '9'; 35 39 } 36 40 37 41 static bool io_print_to_output(const char *str, ant_output_stream_t *out) { ··· 68 72 if (c != 'm' && !ant_output_stream_putc(out, c)) return false; 69 73 goto *states[0]; 70 74 } 71 - 75 + 72 76 done: return true; 73 77 } 74 78 ··· 152 156 while (*p && *p != end_char) ant_output_stream_putc(out, *p++); \ 153 157 if (*p == end_char) ant_output_stream_putc(out, *p++); \ 154 158 ant_output_stream_append_cstr(out, C_RESET); goto next; 155 - 159 + 156 160 #define EMIT_TYPE(tag, len, color) \ 157 161 if (!(is_key && brace_depth > 0) && memcmp(p, tag, len) == 0) { \ 158 162 ant_output_stream_append_cstr(out, color); ant_output_stream_append_cstr(out, tag); ant_output_stream_append_cstr(out, C_RESET); \ ··· 274 278 if (memcmp(p, "<ref", 4) == 0) { EMIT_UNTIL('>', JSON_REF) } 275 279 if (memcmp(p, "<pen", 4) == 0) { is_key = false; EMIT_UNTIL('>', C_CYAN) } 276 280 if (memcmp(p, "<rej", 4) == 0) { is_key = false; EMIT_UNTIL('>', C_CYAN) } 277 - 281 + 278 282 if (p[1] == '>' || (isxdigit((unsigned char)p[1]) && isxdigit((unsigned char)p[2]))) { 279 283 ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); 280 284 ant_output_stream_append_cstr(out, JSON_WHITE); ··· 283 287 if (*p == '>') { ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); } 284 288 goto next; 285 289 } 286 - 290 + 287 291 ant_output_stream_append_cstr(out, JSON_BRACE); ant_output_stream_putc(out, *p++); ant_output_stream_append_cstr(out, C_RESET); 288 292 goto next; 289 293 ··· 294 298 alpha: 295 299 if (memcmp(p, "Object [", 8) == 0) { EMIT_UNTIL(']', JSON_TAG) } 296 300 if (memcmp(p, "Symbol(", 7) == 0) { EMIT_UNTIL(')', JSON_STRING) } 297 - 301 + 298 302 EMIT_TYPE("Map", 3, JSON_STRING) 299 303 EMIT_TYPE("Set", 3, JSON_STRING) 300 304 ··· 356 360 357 361 if (vtype(val) == T_OBJ && vtype(js_get_slot(val, SLOT_ERR_TYPE)) != T_UNDEF) { 358 362 const char *stack = get_str_prop(js, val, "stack", 5, NULL); 359 - 363 + 360 364 if (stack) { 361 365 ant_output_stream_begin(out); 362 366 io_print_to_output(stack, out); ··· 384 388 if (cstr.needs_free) free((void *)cstr.ptr); 385 389 } 386 390 387 - ant_value_t console_print(ant_t *js, ant_value_t *args, int nargs, const char *color, FILE *stream) { 388 - ant_output_stream_t *out = ant_output_stream(stream); 391 + static ant_value_t console_call_value( 392 + ant_t *js, ant_value_t fn, 393 + ant_value_t this_val, 394 + ant_value_t *args, int nargs 395 + ) { 396 + ant_value_t saved_this = js->this_val; 397 + ant_value_t result = js_mkundef(); 398 + 399 + js->this_val = this_val; 400 + if (vtype(fn) == T_CFUNC) result = ((ant_cfunc_t)vdata(fn))(js, args, nargs); 401 + else result = sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, false); 402 + js->this_val = saved_this; 403 + 404 + return result; 405 + } 406 + 407 + static ant_value_t console_get_process_stream(ant_t *js, const char *name) { 408 + ant_value_t process_obj = js_get(js, js_glob(js), "process"); 409 + return js_get(js, process_obj, name); 410 + } 411 + 412 + static ant_value_t console_get_effective_this(ant_t *js, ant_value_t this_obj) { 413 + if (is_special_object(this_obj)) return this_obj; 414 + ant_value_t console_obj = js_get(js, js_glob(js), "console"); 415 + if (is_special_object(console_obj)) return console_obj; 416 + return this_obj; 417 + } 418 + 419 + static ant_value_t console_get_target_stream(ant_t *js, ant_value_t this_obj, bool use_stderr) { 420 + if (is_special_object(this_obj)) { 421 + ant_value_t direct = js_get_slot(this_obj, use_stderr ? SLOT_CONSOLE_STDERR : SLOT_CONSOLE_STDOUT); 422 + if (is_special_object(direct)) return direct; 423 + } 424 + return console_get_process_stream(js, use_stderr ? "stderr" : "stdout"); 425 + } 426 + 427 + static bool console_write_to_stream_obj(ant_t *js, ant_value_t stream_obj, const char *data, size_t len) { 428 + if (!is_special_object(stream_obj)) return false; 429 + 430 + ant_value_t write_fn = js_get(js, stream_obj, "write"); 431 + if (!is_callable(write_fn)) return false; 432 + 433 + ant_value_t argv[1] = { js_mkstr(js, data, len) }; 434 + ant_value_t result = console_call_value(js, write_fn, stream_obj, argv, 1); 435 + if (is_err(result) || js->thrown_exists) return false; 436 + return true; 437 + } 438 + 439 + static bool console_write_string( 440 + ant_t *js, ant_value_t this_obj, 441 + bool use_stderr, const char *data, size_t len 442 + ) { 443 + ant_value_t stream_obj = console_get_target_stream(js, this_obj, use_stderr); 444 + if (console_write_to_stream_obj(js, stream_obj, data, len)) return true; 445 + 446 + ant_output_stream_t *out = ant_output_stream(use_stderr ? stderr : stdout); 389 447 ant_output_stream_begin(out); 390 - if (color && !io_no_color) ant_output_stream_append_cstr(out, color); 391 - 448 + if (!ant_output_stream_append(out, data, len)) return false; 449 + return ant_output_stream_flush(out); 450 + } 451 + 452 + static bool console_output_put_indent(ant_output_stream_t *out, int total) { 453 + for (int i = 0; i < total; i++) if (!ant_output_stream_putc(out, ' ')) return false; 454 + return true; 455 + } 456 + 457 + static int console_get_group_indentation(ant_t *js, ant_value_t this_obj) { 458 + ant_value_t value = is_special_object(this_obj) ? js_get_slot(this_obj, SLOT_CONSOLE_GROUP_INDENT) : js_mkundef(); 459 + return vtype(value) == T_NUM ? (int)js_getnum(value) : 2; 460 + } 461 + 462 + static int console_get_group_level(ant_t *js, ant_value_t this_obj) { 463 + ant_value_t value = is_special_object(this_obj) ? js_get_slot(this_obj, SLOT_CONSOLE_GROUP_LEVEL) : js_mkundef(); 464 + return vtype(value) == T_NUM ? (int)js_getnum(value) : 0; 465 + } 466 + 467 + static void console_set_group_level(ant_t *js, ant_value_t this_obj, int level) { 468 + if (!is_special_object(this_obj)) return; 469 + if (level < 0) level = 0; 470 + js_set_slot(this_obj, SLOT_CONSOLE_GROUP_LEVEL, js_mknum((double)level)); 471 + } 472 + 473 + static ant_value_t console_get_state_map(ant_t *js, ant_value_t this_obj, const char *name) { 474 + internal_slot_t slot = SLOT_NONE; 475 + 476 + if (!is_special_object(this_obj)) return js_mkundef(); 477 + if (strcmp(name, "counts") == 0) slot = SLOT_CONSOLE_COUNTS; 478 + else if (strcmp(name, "timers") == 0) slot = SLOT_CONSOLE_TIMERS; 479 + else return js_mkundef(); 480 + 481 + ant_value_t map = js_get_slot(this_obj, slot); 482 + if (is_special_object(map)) return map; 483 + 484 + map = js_mkobj(js); 485 + js_set_slot_wb(js, this_obj, slot, map); 486 + 487 + return map; 488 + } 489 + 490 + static bool console_write_args_to_stream( 491 + ant_t *js, ant_output_stream_t *out, 492 + ant_value_t *args, int nargs, bool color_values 493 + ) { 392 494 for (int i = 0; i < nargs; i++) { 393 - if (i) ant_output_stream_putc(out, ' '); 495 + if (i && !ant_output_stream_putc(out, ' ')) return false; 496 + 394 497 if (vtype(args[i]) == T_OBJ) { 395 - const char *stack = get_str_prop(js, args[i], "stack", 5, NULL); 396 - if (stack) { io_print_to_output(stack, out); continue; } 397 - } 398 - 498 + const char *stack = get_str_prop(js, args[i], "stack", 5, NULL); 499 + if (stack) { 500 + if (!io_print_to_output(stack, out)) return false; 501 + continue; 502 + }} 503 + 399 504 char cbuf[512]; 400 505 js_cstr_t cstr = js_to_cstr(js, args[i], cbuf, sizeof(cbuf)); 401 - 402 - if (vtype(args[i]) == T_STR) io_print_to_output(cstr.ptr, out); else { 403 - if (color && !io_no_color) ant_output_stream_append_cstr(out, C_RESET); 404 - print_value_colored_to_output(cstr.ptr, out); 405 - if (color && !io_no_color) ant_output_stream_append_cstr(out, color); 506 + bool ok = true; 507 + 508 + if (vtype(args[i]) == T_STR) ok = io_print_to_output(cstr.ptr, out); 509 + else { 510 + bool saved_no_color = io_no_color; 511 + io_no_color = saved_no_color || !color_values; 512 + if (ok) print_value_colored_to_output(cstr.ptr, out); 513 + io_no_color = saved_no_color; 406 514 } 407 - 515 + 408 516 if (cstr.needs_free) free((void *)cstr.ptr); 517 + if (!ok) return false; 409 518 } 410 - 411 - if (color && !io_no_color) ant_output_stream_append_cstr(out, C_RESET); 412 - ant_output_stream_putc(out, '\n'); 413 - ant_output_stream_flush(out); 414 - 519 + 520 + return true; 521 + } 522 + 523 + static ant_value_t console_emit_to_output( 524 + ant_t *js, ant_value_t this_obj, 525 + const char *prefix, ant_value_t *args, 526 + int nargs, 527 + ant_output_stream_t *out 528 + ) { 529 + bool color_values = !io_no_color; 530 + 531 + int group_level = console_get_group_level(js, this_obj); 532 + int indent = console_get_group_indentation(js, this_obj); 533 + int total_indent = group_level * indent; 534 + 535 + if (!console_output_put_indent(out, total_indent)) goto oom; 536 + if (prefix && !ant_output_stream_append_cstr(out, prefix)) goto oom; 537 + if (prefix && nargs > 0 && !ant_output_stream_putc(out, ' ')) goto oom; 538 + if (!console_write_args_to_stream(js, out, args, nargs, color_values)) goto oom; 539 + if (!ant_output_stream_putc(out, '\n')) goto oom; 540 + 415 541 return js_mkundef(); 542 + oom: return js_mkerr(js, "Out of memory"); 543 + } 544 + 545 + static inline ant_value_t console_emit_with_this( 546 + ant_t *js, 547 + ant_value_t this_obj, 548 + bool use_stderr, const char *prefix, 549 + ant_value_t *args, int nargs 550 + ) { 551 + this_obj = console_get_effective_this(js, this_obj); 552 + ant_output_stream_t out = {0}; 553 + ant_output_stream_begin(&out); 554 + 555 + ant_value_t result = console_emit_to_output(js, this_obj, prefix, args, nargs, &out); 556 + if (!is_err(result)) console_write_string( 557 + js, this_obj, use_stderr, 558 + out.buffer.data ? out.buffer.data : "", out.buffer.len 559 + ); 560 + 561 + free(out.buffer.data); 562 + return result; 563 + } 564 + 565 + ant_value_t console_emit( 566 + ant_t *js, 567 + bool use_stderr, const char *prefix, 568 + ant_value_t *args, int nargs 569 + ) { 570 + return console_emit_with_this(js, js_mkundef(), use_stderr, prefix, args, nargs); 571 + } 572 + 573 + ant_value_t console_emit_current( 574 + ant_t *js, 575 + bool use_stderr, const char *prefix, 576 + ant_value_t *args, int nargs 577 + ) { 578 + return console_emit_with_this(js, js_getthis(js), use_stderr, prefix, args, nargs); 416 579 } 417 580 418 581 static void console_write_args_to_output(ant_t *js, ant_output_stream_t *out, ant_value_t *args, int nargs) { ··· 436 599 }} 437 600 438 601 static ant_value_t js_console_log(ant_t *js, ant_value_t *args, int nargs) { 439 - return console_print(js, args, nargs, NULL, stdout); 602 + return console_emit_current(js, false, NULL, args, nargs); 440 603 } 441 604 442 605 static ant_value_t js_console_error(ant_t *js, ant_value_t *args, int nargs) { 443 - return console_print(js, args, nargs, C_RED, stderr); 606 + return console_emit_current(js, true, NULL, args, nargs); 444 607 } 445 608 446 609 static ant_value_t js_console_warn(ant_t *js, ant_value_t *args, int nargs) { 447 - return console_print(js, args, nargs, C_YELLOW, stderr); 610 + return console_emit_current(js, true, NULL, args, nargs); 448 611 } 449 612 450 - static ant_value_t js_console_assert(ant_t *js, ant_value_t *args, int nargs) { 451 - ant_output_stream_t *out = ant_output_stream(stderr); 613 + static ant_value_t js_console_info(ant_t *js, ant_value_t *args, int nargs) { 614 + return console_emit_current(js, false, NULL, args, nargs); 615 + } 452 616 617 + static ant_value_t js_console_debug(ant_t *js, ant_value_t *args, int nargs) { 618 + return console_emit_current(js, false, NULL, args, nargs); 619 + } 620 + 621 + static ant_value_t js_console_assert(ant_t *js, ant_value_t *args, int nargs) { 453 622 if (nargs < 1) return js_mkundef(); 454 - 455 623 bool is_truthy = js_truthy(js, args[0]); 456 624 if (is_truthy) return js_mkundef(); 457 - 458 - ant_output_stream_begin(out); 459 - ant_output_stream_append_cstr(out, "Assertion failed"); 460 - 461 - if (nargs > 1) { 462 - ant_output_stream_append_cstr(out, ": "); 463 - console_write_args_to_output(js, out, args + 1, nargs - 1); 464 - ant_output_stream_putc(out, '\n'); 465 - ant_output_stream_flush(out); 466 - return js_mkundef(); 467 - } 468 - 469 - ant_output_stream_putc(out, '\n'); 470 - ant_output_stream_flush(out); 471 - 472 - return js_mkundef(); 625 + return console_emit_current(js, true, "Assertion failed:", args + 1, nargs - 1); 473 626 } 474 627 475 628 static ant_value_t js_console_trace(ant_t *js, ant_value_t *args, int nargs) { 476 - ant_output_stream_t *out = ant_output_stream(stderr); 477 - 478 - ant_output_stream_begin(out); 479 - ant_output_stream_append_cstr(out, "Trace"); 480 - 481 - if (nargs > 0) { 482 - ant_output_stream_append_cstr(out, ": "); 483 - console_write_args_to_output(js, out, args, nargs); 484 - ant_output_stream_putc(out, '\n'); 485 - } else ant_output_stream_putc(out, '\n'); 486 - 487 - ant_output_stream_flush(out); 488 - js_print_stack_trace_vm(js, stderr); 489 - 629 + ant_value_t this_obj = console_get_effective_this(js, js_getthis(js)); 630 + console_emit_current(js, true, "Trace:", args, nargs); 631 + if (console_get_target_stream(js, this_obj, true) == js_mkundef()) js_print_stack_trace_vm(js, stderr); 632 + else js_print_stack_trace_vm(js, stderr); 490 633 return js_mkundef(); 491 634 } 492 635 493 - static ant_value_t js_console_info(ant_t *js, ant_value_t *args, int nargs) { 494 - return console_print(js, args, nargs, C_CYAN, stdout); 495 - } 496 - 497 - static ant_value_t js_console_debug(ant_t *js, ant_value_t *args, int nargs) { 498 - return console_print(js, args, nargs, C_MAGENTA, stdout); 636 + static ant_value_t js_console_clear(ant_t *js, ant_value_t *args, int nargs) { 637 + ant_value_t this_obj = js_getthis(js); 638 + if (!io_no_color) console_write_string(js, this_obj, false, "\033[2J\033[H", 7); 639 + return js_mkundef(); 499 640 } 500 641 501 - static ant_value_t js_console_clear(ant_t *js, ant_value_t *args, int nargs) { 502 - if (!io_no_color) { 503 - ant_output_stream_t *out = ant_output_stream(stdout); 504 - ant_output_stream_begin(out); 505 - ant_output_stream_append_cstr(out, "\033[2J\033[H"); 506 - ant_output_stream_flush(out); 642 + static ant_value_t js_console_time(ant_t *js, ant_value_t *args, int nargs) { 643 + ant_value_t this_obj = js_getthis(js); 644 + const char *label = "default"; 645 + 646 + if (nargs > 0 && vtype(args[0]) == T_STR) label = js_getstr(js, args[0], NULL); 647 + ant_value_t timers = console_get_state_map(js, this_obj, "timers"); 648 + if (is_special_object(timers) && vtype(js_get(js, timers, label)) != T_UNDEF) { 649 + ant_value_t warn_args[1] = { js_mkstr(js, "Timer already exists", 20) }; 650 + return console_emit_current(js, true, NULL, warn_args, 1); 507 651 } 652 + 653 + js_set(js, timers, label, js_mknum((double)uv_hrtime() / 1e6)); 508 654 return js_mkundef(); 509 655 } 510 656 511 - static struct { char *label; double start_time; } console_timers[64]; 512 - static int console_timer_count = 0; 513 - 514 - static ant_value_t js_console_time(ant_t *js, ant_value_t *args, int nargs) { 657 + static ant_value_t js_console_timeEnd(ant_t *js, ant_value_t *args, int nargs) { 658 + ant_value_t this_obj = js_getthis(js); 515 659 const char *label = "default"; 516 - if (nargs > 0 && vtype(args[0]) == T_STR) { 517 - label = js_getstr(js, args[0], NULL); 660 + 661 + if (nargs > 0 && vtype(args[0]) == T_STR) label = js_getstr(js, args[0], NULL); 662 + ant_value_t timers = console_get_state_map(js, this_obj, "timers"); 663 + ant_value_t start = is_special_object(timers) ? js_get(js, timers, label) : js_mkundef(); 664 + 665 + if (vtype(start) != T_NUM) { 666 + ant_value_t warn_args[1] = { js_mkstr(js, "Timer does not exist", 19) }; 667 + return console_emit_current(js, true, NULL, warn_args, 1); 518 668 } 519 669 520 - for (int i = 0; i < console_timer_count; i++) { 521 - if (strcmp(console_timers[i].label, label) == 0) { 522 - ant_output_stream_t *out = ant_output_stream(stderr); 523 - ant_output_stream_begin(out); 524 - ant_output_stream_appendf(out, "Timer '%s' already exists\n", label); 525 - ant_output_stream_flush(out); 526 - return js_mkundef(); 527 - }} 670 + double elapsed = ((double)uv_hrtime() / 1e6) - js_getnum(start); 671 + js_delete_prop(js, timers, label, strlen(label)); 672 + char buf[256]; 528 673 529 - if (console_timer_count < 64) { 530 - console_timers[console_timer_count].label = strdup(label); 531 - console_timers[console_timer_count].start_time = (double)uv_hrtime() / 1e6; 532 - console_timer_count++; 533 - } 674 + int len = snprintf(buf, sizeof(buf), "%s: %.3fms", label, elapsed); 675 + ant_value_t out_args[1] = { js_mkstr(js, buf, (size_t)(len > 0 ? len : 0)) }; 534 676 535 - return js_mkundef(); 677 + return console_emit_current(js, false, NULL, out_args, 1); 536 678 } 537 679 538 - static ant_value_t js_console_timeEnd(ant_t *js, ant_value_t *args, int nargs) { 680 + static ant_value_t js_console_timeLog(ant_t *js, ant_value_t *args, int nargs) { 681 + ant_value_t this_obj = js_getthis(js); 539 682 const char *label = "default"; 683 + int extra_start = 0; 684 + 540 685 if (nargs > 0 && vtype(args[0]) == T_STR) { 541 686 label = js_getstr(js, args[0], NULL); 687 + extra_start = 1; 542 688 } 543 689 544 - for (int i = 0; i < console_timer_count; i++) { 545 - if (strcmp(console_timers[i].label, label) == 0) { 546 - double elapsed = ((double)uv_hrtime() / 1e6) - console_timers[i].start_time; 547 - ant_output_stream_t *out = ant_output_stream(stdout); 548 - 549 - ant_output_stream_begin(out); 550 - ant_output_stream_appendf(out, "%s: %.3fms\n", label, elapsed); 551 - ant_output_stream_flush(out); 552 - free(console_timers[i].label); 553 - 554 - for (int j = i; j < console_timer_count - 1; j++) { 555 - console_timers[j] = console_timers[j + 1]; 556 - } 557 - 558 - console_timer_count--; 559 - return js_mkundef(); 560 - }} 690 + ant_value_t timers = console_get_state_map(js, this_obj, "timers"); 691 + ant_value_t start = is_special_object(timers) ? js_get(js, timers, label) : js_mkundef(); 561 692 562 - { 563 - ant_output_stream_t *out = ant_output_stream(stderr); 564 - ant_output_stream_begin(out); 565 - ant_output_stream_appendf(out, "Timer '%s' does not exist\n", label); 566 - ant_output_stream_flush(out); 693 + if (vtype(start) != T_NUM) { 694 + ant_value_t warn_args[1] = { js_mkstr(js, "Timer does not exist", 19) }; 695 + return console_emit_current(js, true, NULL, warn_args, 1); 567 696 } 568 697 698 + char buf[256]; 699 + double elapsed = ((double)uv_hrtime() / 1e6) - js_getnum(start); 700 + int len = snprintf(buf, sizeof(buf), "%s: %.3fms", label, elapsed); 701 + 702 + ant_value_t *out_args = malloc((size_t)(nargs - extra_start + 1) * sizeof(ant_value_t)); 703 + if (!out_args) return js_mkerr(js, "Out of memory"); 704 + 705 + out_args[0] = js_mkstr(js, buf, (size_t)(len > 0 ? len : 0)); 706 + for (int i = extra_start; i < nargs; i++) out_args[i - extra_start + 1] = args[i]; 707 + ant_value_t result = console_emit_current(js, false, NULL, out_args, nargs - extra_start + 1); 708 + free(out_args); 709 + 710 + return result; 711 + } 712 + 713 + static ant_value_t js_console_count(ant_t *js, ant_value_t *args, int nargs) { 714 + ant_value_t this_obj = js_getthis(js); 715 + const char *label = "default"; 716 + 717 + if (nargs > 0 && vtype(args[0]) == T_STR) label = js_getstr(js, args[0], NULL); 718 + ant_value_t counts = console_get_state_map(js, this_obj, "counts"); 719 + ant_value_t current = is_special_object(counts) ? js_get(js, counts, label) : js_mkundef(); 720 + 721 + double next = vtype(current) == T_NUM ? js_getnum(current) + 1 : 1; 722 + js_set(js, counts, label, js_mknum(next)); 723 + 724 + char buf[256]; 725 + int len = snprintf(buf, sizeof(buf), "%s: %.0f", label, next); 726 + ant_value_t out_args[1] = { js_mkstr(js, buf, (size_t)(len > 0 ? len : 0)) }; 727 + 728 + return console_emit_current(js, false, NULL, out_args, 1); 729 + } 730 + 731 + static ant_value_t js_console_countReset(ant_t *js, ant_value_t *args, int nargs) { 732 + ant_value_t this_obj = js_getthis(js); 733 + const char *label = "default"; 734 + if (nargs > 0 && vtype(args[0]) == T_STR) label = js_getstr(js, args[0], NULL); 735 + ant_value_t counts = console_get_state_map(js, this_obj, "counts"); 736 + js_delete_prop(js, counts, label, strlen(label)); 569 737 return js_mkundef(); 570 738 } 571 739 740 + static ant_value_t js_console_group(ant_t *js, ant_value_t *args, int nargs) { 741 + ant_value_t this_obj = js_getthis(js); 742 + if (nargs > 0) console_emit_current(js, false, NULL, args, nargs); 743 + console_set_group_level(js, this_obj, console_get_group_level(js, this_obj) + 1); 744 + return js_mkundef(); 745 + } 746 + 747 + static ant_value_t js_console_group_end(ant_t *js, ant_value_t *args, int nargs) { 748 + ant_value_t this_obj = js_getthis(js); 749 + console_set_group_level(js, this_obj, console_get_group_level(js, this_obj) - 1); 750 + return js_mkundef(); 751 + } 752 + 753 + static ant_value_t js_console_group_collapsed(ant_t *js, ant_value_t *args, int nargs) { 754 + return js_console_group(js, args, nargs); 755 + } 756 + 572 757 static const char *get_slot_name(internal_slot_t slot) { 573 758 #define ANT_SLOT_NAME(name) [name] = &#name[5], 574 759 static const char *slot_names[] = { ··· 682 867 fprintf(stream, "<%s @%llu> {\n", type == T_FUNC ? "Function" : (type == T_PROMISE ? "Promise" : "Object"), (u64)obj_off); 683 868 684 869 int inner_depth = depth + 1; 685 - 686 870 inspect_print_indent(stream, inner_depth); 687 871 fprintf(stream, "[[Slots]]: {\n"); 688 872 ··· 761 945 if (i > 0) fprintf(stream, " "); 762 946 inspect_value(js, args[i], stream, 0, &visited); 763 947 } 764 - 948 + 765 949 fprintf(stream, "\n"); 766 950 if (visited.visited) free(visited.visited); 767 - 951 + 768 952 return js_mkundef(); 769 953 } 770 954 771 - ant_value_t console_library(ant_t *js) { 772 - ant_value_t console_obj = js_mkobj(js); 773 - 955 + // TODO: replace stub with real 956 + static ant_value_t js_console_dir(ant_t *js, ant_value_t *args, int nargs) { 957 + return js_console_log(js, args, nargs); 958 + } 959 + 960 + // TODO: replace stub with real 961 + static ant_value_t js_console_dirxml(ant_t *js, ant_value_t *args, int nargs) { 962 + return js_console_log(js, args, nargs); 963 + } 964 + 965 + // TODO: replace stub with real 966 + static ant_value_t js_console_table(ant_t *js, ant_value_t *args, int nargs) { 967 + return js_console_log(js, args, nargs); 968 + } 969 + 970 + static void console_apply_methods(ant_t *js, ant_value_t console_obj) { 774 971 js_set(js, console_obj, "log", js_mkfun(js_console_log)); 775 972 js_set(js, console_obj, "error", js_mkfun(js_console_error)); 776 973 js_set(js, console_obj, "warn", js_mkfun(js_console_warn)); 777 974 js_set(js, console_obj, "info", js_mkfun(js_console_info)); 778 975 js_set(js, console_obj, "debug", js_mkfun(js_console_debug)); 779 976 js_set(js, console_obj, "assert", js_mkfun(js_console_assert)); 977 + js_set(js, console_obj, "dir", js_mkfun(js_console_dir)); 978 + js_set(js, console_obj, "dirxml", js_mkfun(js_console_dirxml)); 979 + js_set(js, console_obj, "table", js_mkfun(js_console_table)); 780 980 js_set(js, console_obj, "trace", js_mkfun(js_console_trace)); 981 + js_set(js, console_obj, "count", js_mkfun(js_console_count)); 982 + js_set(js, console_obj, "countReset", js_mkfun(js_console_countReset)); 781 983 js_set(js, console_obj, "time", js_mkfun(js_console_time)); 984 + js_set(js, console_obj, "timeLog", js_mkfun(js_console_timeLog)); 782 985 js_set(js, console_obj, "timeEnd", js_mkfun(js_console_timeEnd)); 986 + js_set(js, console_obj, "group", js_mkfun(js_console_group)); 987 + js_set(js, console_obj, "groupCollapsed", js_mkfun(js_console_group_collapsed)); 988 + js_set(js, console_obj, "groupEnd", js_mkfun(js_console_group_end)); 783 989 js_set(js, console_obj, "clear", js_mkfun(js_console_clear)); 784 990 js_set(js, console_obj, "inspect", js_mkfun(js_console_inspect)); 991 + } 992 + 993 + static ant_value_t js_console_constructor(ant_t *js, ant_value_t *args, int nargs) { 994 + ant_value_t proto = js_instance_proto_from_new_target(js, g_console_proto); 995 + ant_value_t console_obj = js_mkobj(js); 996 + js_set_proto_init(console_obj, is_special_object(proto) ? proto : g_console_proto); 997 + 998 + ant_value_t stdout_obj = js_mkundef(); 999 + ant_value_t stderr_obj = js_mkundef(); 1000 + int group_indentation = 2; 1001 + 1002 + if (nargs >= 1 && is_special_object(args[0]) && nargs == 1) { 1003 + ant_value_t options = args[0]; 1004 + stdout_obj = js_get(js, options, "stdout"); 1005 + stderr_obj = js_get(js, options, "stderr"); 1006 + ant_value_t gi = js_get(js, options, "groupIndentation"); 1007 + if (vtype(gi) == T_NUM) group_indentation = (int)js_getnum(gi); 1008 + } else { 1009 + if (nargs >= 1) stdout_obj = args[0]; 1010 + if (nargs >= 2) stderr_obj = args[1]; 1011 + } 1012 + 1013 + if (vtype(stderr_obj) == T_UNDEF) stderr_obj = stdout_obj; 1014 + js_set_slot_wb(js, console_obj, SLOT_CONSOLE_STDOUT, stdout_obj); 1015 + js_set_slot_wb(js, console_obj, SLOT_CONSOLE_STDERR, stderr_obj); 1016 + js_set_slot_wb(js, console_obj, SLOT_CONSOLE_COUNTS, js_mkobj(js)); 1017 + js_set_slot_wb(js, console_obj, SLOT_CONSOLE_TIMERS, js_mkobj(js)); 785 1018 1019 + js_set_slot(console_obj, SLOT_CONSOLE_GROUP_INDENT, js_mknum((double)group_indentation)); 1020 + js_set_slot(console_obj, SLOT_CONSOLE_GROUP_LEVEL, js_mknum(0)); 1021 + 1022 + return console_obj; 1023 + } 1024 + 1025 + ant_value_t console_library(ant_t *js) { 1026 + if (!g_console_ctor) { 1027 + g_console_proto = js_mkobj(js); 1028 + console_apply_methods(js, g_console_proto); 1029 + js_set_sym(js, g_console_proto, get_toStringTag_sym(), js_mkstr(js, "console", 7)); 1030 + g_console_ctor = js_make_ctor(js, js_console_constructor, g_console_proto, "Console", 7); 1031 + gc_register_root(&g_console_proto); 1032 + gc_register_root(&g_console_ctor); 1033 + } 1034 + 1035 + ant_value_t console_obj = js_mkobj(js); 1036 + js_set_proto_init(console_obj, g_console_proto); 1037 + js_set_slot_wb(js, console_obj, SLOT_CONSOLE_COUNTS, js_mkobj(js)); 1038 + js_set_slot_wb(js, console_obj, SLOT_CONSOLE_TIMERS, js_mkobj(js)); 1039 + js_set_slot(console_obj, SLOT_CONSOLE_GROUP_INDENT, js_mknum(2)); 1040 + js_set_slot(console_obj, SLOT_CONSOLE_GROUP_LEVEL, js_mknum(0)); 1041 + 1042 + js_set(js, console_obj, "Console", g_console_ctor); 1043 + js_set(js, console_obj, "default", console_obj); 786 1044 js_set_sym(js, console_obj, get_toStringTag_sym(), js_mkstr(js, "console", 7)); 1045 + 787 1046 return console_obj; 788 1047 } 789 1048
+658 -81
src/modules/napi.c
··· 24 24 #include <stdio.h> 25 25 #include <stdlib.h> 26 26 #include <string.h> 27 + #include <stdint.h> 27 28 #include <uthash.h> 28 29 #include <uv.h> 29 30 31 + #if defined(__has_include) 32 + #if __has_include(<uchar.h>) 33 + #include <uchar.h> 34 + #else 35 + typedef uint16_t char16_t; 36 + #endif 37 + #else 38 + typedef uint16_t char16_t; 39 + #endif 40 + 30 41 #include "ant.h" 31 42 #include "descriptors.h" 32 43 #include "errors.h" ··· 34 45 #include "silver/engine.h" 35 46 36 47 #include "modules/buffer.h" 48 + #include "modules/date.h" 37 49 #include "modules/napi.h" 38 50 #include "gc/roots.h" 39 51 #include "gc/modules.h" 52 + #include "utf8.h" 40 53 41 54 typedef struct napi_cleanup_hook_entry { 42 55 napi_cleanup_hook hook; ··· 51 64 bool has_pending_exception; 52 65 napi_value pending_exception; 53 66 uint32_t version; 67 + void *instance_data; 68 + node_api_basic_finalize instance_data_finalize_cb; 69 + void *instance_data_finalize_hint; 54 70 napi_cleanup_hook_entry_t *cleanup_hooks; 55 71 struct napi_ref__ *refs; 56 72 struct napi_deferred__ *deferreds; ··· 128 144 void *native_object; 129 145 node_api_basic_finalize finalize_cb; 130 146 void *finalize_hint; 147 + void *attached_data; 148 + node_api_basic_finalize attached_finalize_cb; 149 + void *attached_finalize_hint; 150 + bool has_wrap; 131 151 UT_hash_handle hh; 132 152 } napi_wrap_entry_t; 133 153 ··· 175 195 struct napi_native_lib *next; 176 196 } napi_native_lib_t; 177 197 198 + typedef enum { 199 + napi_key_include_prototypes = 0, 200 + napi_key_own_only = 1, 201 + } napi_key_collection_mode; 202 + 203 + typedef enum { 204 + napi_key_all_properties = 0, 205 + napi_key_writable = 1 << 0, 206 + napi_key_enumerable = 1 << 1, 207 + napi_key_configurable = 1 << 2, 208 + napi_key_skip_strings = 1 << 3, 209 + napi_key_skip_symbols = 1 << 4, 210 + } napi_key_filter; 211 + 212 + typedef enum { 213 + napi_key_keep_numbers = 0, 214 + napi_key_numbers_to_strings = 1, 215 + } napi_key_conversion; 216 + 217 + typedef struct { 218 + uint8_t sign; 219 + uint8_t pad[3]; 220 + uint32_t limb_count; 221 + uint32_t limbs[]; 222 + } napi_bigint_payload_t; 223 + 178 224 static ant_napi_env_t *g_napi_env = NULL; 179 225 static napi_external_entry_t *g_napi_externals = NULL; 180 226 static napi_wrap_entry_t *g_napi_wraps = NULL; ··· 220 266 default: return "unknown"; 221 267 }} 222 268 223 - static napi_status napi_set_last(napi_env env, napi_status status, const char *message) { 269 + static napi_status napi_set_last_raw(napi_env env, napi_status status, const char *message) { 224 270 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 225 271 if (!nenv) return status; 226 272 ··· 236 282 } 237 283 238 284 return status; 285 + } 286 + 287 + static napi_status napi_set_last(napi_env env, napi_status status, const char *message) { 288 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 289 + if (status == napi_ok && nenv && ( 290 + nenv->has_pending_exception 291 + || (nenv->js && nenv->js->thrown_exists) 292 + ) 293 + ) return napi_set_last_raw(env, napi_pending_exception, "pending exception"); 294 + return napi_set_last_raw(env, status, message); 239 295 } 240 296 241 297 static ant_napi_env_t *napi_get_or_create_env(ant_t *js) { ··· 297 353 napi_set_last(env, napi_pending_exception, "pending exception"); 298 354 } 299 355 356 + NAPI_EXTERN napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error) { 357 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 358 + if (!nenv || !nenv->js) return napi_set_last(env, napi_invalid_arg, "invalid env"); 359 + js_throw(nenv->js, (ant_value_t)error); 360 + napi_mark_pending_exception(env, error); 361 + return napi_pending_exception; 362 + } 363 + 300 364 static napi_status napi_check_pending_from_result(napi_env env, ant_value_t result) { 301 365 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 302 366 if (!nenv || !nenv->js) return napi_set_last(env, napi_invalid_arg, "invalid env"); ··· 311 375 return napi_set_last(env, napi_ok, NULL); 312 376 } 313 377 378 + static napi_status napi_return_pending_if_any(napi_env env) { 379 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 380 + if (!nenv || !nenv->js) return napi_set_last(env, napi_invalid_arg, "invalid env"); 381 + 382 + if (nenv->has_pending_exception) { 383 + napi_set_last(env, napi_pending_exception, "pending exception"); 384 + return napi_pending_exception; 385 + } 386 + 387 + if (nenv->js->thrown_exists) { 388 + napi_mark_pending_exception(env, (napi_value)nenv->js->thrown_value); 389 + return napi_pending_exception; 390 + } 391 + 392 + return napi_set_last(env, napi_ok, NULL); 393 + } 394 + 314 395 static bool napi_slot_get_u64(ant_t *js, ant_value_t obj, internal_slot_t slot, uint64_t *out) { 315 396 ant_value_t value = js_get_slot(obj, slot); 316 397 if (vtype(value) != T_NUM) return false; ··· 424 505 return js_throw(js, ex); 425 506 } 426 507 508 + if (js->thrown_exists) { 509 + return js_throw(js, js->thrown_value); 510 + } 511 + 427 512 if ((ant_value_t)ret == 0) return js_mkundef(); 428 513 return (ant_value_t)ret; 429 514 } ··· 465 550 return js_mkstr(js, s, len); 466 551 } 467 552 553 + static bool napi_checked_add_size(size_t a, size_t b, size_t *out) { 554 + if (a > SIZE_MAX - b) return false; 555 + *out = a + b; 556 + return true; 557 + } 558 + 559 + static bool napi_checked_mul_size(size_t a, size_t b, size_t *out) { 560 + if (a != 0 && b > SIZE_MAX / a) return false; 561 + *out = a * b; 562 + return true; 563 + } 564 + 565 + static bool napi_make_bigint_limbs( 566 + ant_t *js, 567 + const uint32_t *limbs, 568 + size_t count, 569 + bool negative, 570 + ant_value_t *out 571 + ) { 572 + uint32_t zero = 0; 573 + if (!out) return false; 574 + 575 + if (!limbs || count == 0) { 576 + limbs = &zero; 577 + count = 1; 578 + } 579 + 580 + while (count > 1 && limbs[count - 1] == 0) count--; 581 + if (count == 1 && limbs[0] == 0) negative = false; 582 + if (count > UINT32_MAX) return false; 583 + 584 + size_t limbs_bytes = 0; 585 + if (!napi_checked_mul_size(count, sizeof(uint32_t), &limbs_bytes)) return false; 586 + 587 + size_t payload_size = 0; 588 + if (!napi_checked_add_size(offsetof(napi_bigint_payload_t, limbs), limbs_bytes, &payload_size)) { 589 + return false; 590 + } 591 + 592 + napi_bigint_payload_t *payload = (napi_bigint_payload_t *)js_type_alloc( 593 + js, ANT_ALLOC_BIGINT, payload_size, 594 + _Alignof(napi_bigint_payload_t) 595 + ); 596 + 597 + if (!payload) return false; 598 + payload->sign = negative ? 1 : 0; 599 + payload->pad[0] = 0; 600 + payload->pad[1] = 0; 601 + payload->pad[2] = 0; 602 + payload->limb_count = (uint32_t)count; 603 + memcpy(payload->limbs, limbs, limbs_bytes); 604 + *out = mkval(T_BIGINT, (uint64_t)(uintptr_t)payload); 605 + 606 + return true; 607 + } 608 + 609 + static const napi_bigint_payload_t *napi_bigint_payload(napi_value value) { 610 + return (const napi_bigint_payload_t *)(uintptr_t)vdata((ant_value_t)value); 611 + } 612 + 613 + static const uint32_t *napi_bigint_limbs(napi_value value, size_t *count) { 614 + const napi_bigint_payload_t *payload = napi_bigint_payload(value); 615 + if (!payload) { 616 + if (count) *count = 0; 617 + return NULL; 618 + } 619 + 620 + size_t limb_count = payload->limb_count; 621 + if (limb_count == 0) limb_count = 1; 622 + 623 + while (limb_count > 1 && payload->limbs[limb_count - 1] == 0) limb_count--; 624 + if (count) *count = limb_count; 625 + 626 + return payload->limbs; 627 + } 628 + 629 + static bool napi_bigint_is_negative(napi_value value) { 630 + const napi_bigint_payload_t *payload = napi_bigint_payload(value); 631 + return payload && payload->sign == 1; 632 + } 633 + 634 + static bool napi_bigint_limbs_is_zero(const uint32_t *limbs, size_t count) { 635 + return count <= 1 && (!limbs || limbs[0] == 0); 636 + } 637 + 638 + static uint64_t napi_bigint_low_u64(const uint32_t *limbs, size_t count) { 639 + uint64_t out = 0; 640 + if (count > 0 && limbs) out |= (uint64_t)limbs[0]; 641 + if (count > 1 && limbs) out |= ((uint64_t)limbs[1] << 32); 642 + return out; 643 + } 644 + 645 + static bool napi_parse_index_key(const char *str, size_t len, uint32_t *out) { 646 + if (!str || len == 0) return false; 647 + if (len > 1 && str[0] == '0') return false; 648 + 649 + uint64_t acc = 0; 650 + for (size_t i = 0; i < len; i++) { 651 + if (str[i] < '0' || str[i] > '9') return false; 652 + acc = (acc * 10) + (uint64_t)(str[i] - '0'); 653 + if (acc > UINT32_MAX) return false; 654 + } 655 + 656 + if (out) *out = (uint32_t)acc; 657 + return true; 658 + } 659 + 660 + static bool napi_seen_has_key(ant_t *js, ant_value_t seen, ant_value_t key) { 661 + if (vtype(key) == T_SYMBOL) { 662 + return lkp_sym(js, seen, (ant_offset_t)vdata(key)) != 0; 663 + } 664 + 665 + size_t len = 0; 666 + const char *str = js_getstr(js, key, &len); 667 + return str && lkp(js, seen, str, len) != 0; 668 + } 669 + 670 + static bool napi_seen_add_key(ant_t *js, ant_value_t seen, ant_value_t key) { 671 + ant_value_t res = js_setprop(js, seen, key, js_true); 672 + return !is_err(res); 673 + } 674 + 675 + static bool napi_key_passes_filter(const ant_shape_prop_t *prop, napi_key_filter key_filter) { 676 + if (!prop) return false; 677 + if ((key_filter & napi_key_writable) && !(prop->attrs & ANT_PROP_ATTR_WRITABLE)) return false; 678 + if ((key_filter & napi_key_enumerable) && !(prop->attrs & ANT_PROP_ATTR_ENUMERABLE)) return false; 679 + if ((key_filter & napi_key_configurable) && !(prop->attrs & ANT_PROP_ATTR_CONFIGURABLE)) return false; 680 + return true; 681 + } 682 + 683 + static ant_value_t napi_convert_property_key( 684 + ant_t *js, 685 + ant_value_t key, 686 + napi_key_conversion key_conversion 687 + ) { 688 + if (key_conversion != napi_key_keep_numbers || vtype(key) != T_STR) return key; 689 + 690 + size_t len = 0; 691 + const char *str = js_getstr(js, key, &len); 692 + uint32_t idx = 0; 693 + if (!str || !napi_parse_index_key(str, len, &idx)) return key; 694 + return js_mknum((double)idx); 695 + } 696 + 697 + static napi_status napi_create_date_common(napi_env env, double time, napi_value *result) { 698 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 699 + if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 700 + 701 + ant_t *js = nenv->js; 702 + ant_value_t ctor = js_get(js, js_glob(js), "Date"); 703 + if (!is_callable(ctor)) return napi_set_last(env, napi_generic_failure, "Date constructor unavailable"); 704 + 705 + ant_value_t obj = js_mkobj(js); 706 + ant_value_t proto = js_get(js, ctor, "prototype"); 707 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 708 + 709 + ant_value_t argv[1] = {js_mknum(time)}; 710 + ant_value_t saved = js->new_target; 711 + js->new_target = ctor; 712 + ant_value_t out = sv_vm_call(js->vm, js, ctor, obj, argv, 1, NULL, true); 713 + js->new_target = saved; 714 + 715 + if (is_err(out) || js->thrown_exists) return napi_check_pending_from_result(env, out); 716 + *result = NAPI_RETURN(nenv, is_object_type(out) ? out : obj); 717 + return napi_set_last(env, napi_ok, NULL); 718 + } 719 + 468 720 static napi_status napi_make_error_object( 469 721 napi_env env, 470 722 const char *name, ··· 507 759 napi_mark_pending_exception(env, (napi_value)nenv->js->thrown_value); 508 760 return napi_pending_exception; 509 761 } 510 - return napi_set_last(env, napi_generic_failure, "failed to throw"); 762 + if (is_err(error)) return napi_set_last(env, napi_generic_failure, "failed to create error"); 763 + return napi_throw(env, (napi_value)error); 511 764 } 512 765 513 766 static void napi_tsfn_maybe_finish(struct napi_threadsafe_function__ *tsfn); ··· 599 852 if (!is_object_type(module_obj)) return js_mkerr(js, "process.dlopen module must be an object"); 600 853 if (!filename || !filename[0]) return js_mkerr(js, "process.dlopen filename must be a non-empty string"); 601 854 855 + g_pending_napi_module = NULL; 602 856 void *handle = NAPI_DLOPEN(filename, NAPI_RTLD_NOW | NAPI_RTLD_GLOBAL | NAPI_RTLD_LOCAL); 603 857 if (!handle) { 604 858 const char *msg = NAPI_DLERROR(); 605 859 return js_mkerr(js, "Failed to load native module '%s': %s", filename, msg ? msg : "unknown"); 606 860 } 607 861 608 - g_pending_napi_module = NULL; 609 862 napi_register_module_v1_fn reg_fn = (napi_register_module_v1_fn)NAPI_DLSYM(handle, "napi_register_module_v1"); 610 - 611 863 ant_value_t exports = js_get(js, module_obj, "exports"); 864 + 612 865 if (!is_object_type(exports)) { 613 866 exports = js_mkobj(js); 614 867 js_set(js, module_obj, "exports", exports); 615 868 } 616 869 617 870 ant_value_t ret = js_mkundef(); 618 - if (reg_fn) { 619 - ret = (ant_value_t)reg_fn(env, (napi_value)exports); 620 - } else if (g_pending_napi_module && g_pending_napi_module->nm_register_func) { 871 + if (reg_fn) ret = (ant_value_t)reg_fn(env, (napi_value)exports); 872 + else if (g_pending_napi_module && g_pending_napi_module->nm_register_func) { 621 873 ret = (ant_value_t)g_pending_napi_module->nm_register_func(env, (napi_value)exports); 622 - } else { 623 - return js_mkerr(js, "No N-API registration entrypoint found in '%s'", filename); 624 - } 874 + } else return js_mkerr(js, "No N-API registration entrypoint found in '%s'", filename); 625 875 626 876 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 627 877 if (nenv->has_pending_exception || js->thrown_exists) { ··· 765 1015 js_mkstr(nenv->js, "length", 6), 766 1016 js_mknum((double)length) 767 1017 ); 768 - if (is_err(r)) return napi_check_pending_from_result(env, r); 1018 + if (is_err(r) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, r); 769 1019 *result = NAPI_RETURN(nenv, arr); 770 1020 return napi_set_last(env, napi_ok, NULL); 771 1021 } ··· 818 1068 return napi_set_last(env, napi_ok, NULL); 819 1069 } 820 1070 1071 + NAPI_EXTERN napi_status NAPI_CDECL napi_create_date( 1072 + napi_env env, 1073 + double time, 1074 + napi_value *result 1075 + ) { 1076 + return napi_create_date_common(env, time, result); 1077 + } 1078 + 1079 + NAPI_EXTERN napi_status NAPI_CDECL napi_create_bigint_int64( 1080 + napi_env env, 1081 + int64_t value, 1082 + napi_value *result 1083 + ) { 1084 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 1085 + if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1086 + 1087 + uint64_t magnitude = value < 0 1088 + ? (uint64_t)(-(value + 1)) + 1 1089 + : (uint64_t)value; 1090 + 1091 + uint32_t limbs[2] = { 1092 + (uint32_t)(magnitude & 0xffffffffu), 1093 + (uint32_t)(magnitude >> 32) 1094 + }; 1095 + 1096 + size_t count = limbs[1] == 0 ? 1 : 2; 1097 + ant_value_t out = js_mkundef(); 1098 + 1099 + if (!napi_make_bigint_limbs(nenv->js, limbs, count, value < 0, &out)) { 1100 + return napi_set_last(env, napi_generic_failure, "out of memory"); 1101 + } 1102 + 1103 + *result = NAPI_RETURN(nenv, out); 1104 + return napi_set_last(env, napi_ok, NULL); 1105 + } 1106 + 1107 + NAPI_EXTERN napi_status NAPI_CDECL napi_create_bigint_uint64( 1108 + napi_env env, 1109 + uint64_t value, 1110 + napi_value *result 1111 + ) { 1112 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 1113 + if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1114 + 1115 + uint32_t limbs[2] = { 1116 + (uint32_t)(value & 0xffffffffu), 1117 + (uint32_t)(value >> 32) 1118 + }; 1119 + size_t count = limbs[1] == 0 ? 1 : 2; 1120 + 1121 + ant_value_t out = js_mkundef(); 1122 + if (!napi_make_bigint_limbs(nenv->js, limbs, count, false, &out)) { 1123 + return napi_set_last(env, napi_generic_failure, "out of memory"); 1124 + } 1125 + 1126 + *result = NAPI_RETURN(nenv, out); 1127 + return napi_set_last(env, napi_ok, NULL); 1128 + } 1129 + 1130 + NAPI_EXTERN napi_status NAPI_CDECL napi_create_bigint_words( 1131 + napi_env env, 1132 + int sign_bit, 1133 + size_t word_count, 1134 + const uint64_t *words, 1135 + napi_value *result 1136 + ) { 1137 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 1138 + if (!nenv || !nenv->js || !result || (word_count > 0 && !words)) { 1139 + return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1140 + } 1141 + 1142 + size_t limb_count = 1; 1143 + if (word_count > 0 && !napi_checked_mul_size(word_count, 2, &limb_count)) { 1144 + return napi_set_last(env, napi_invalid_arg, "word count overflow"); 1145 + } 1146 + uint32_t *limbs = (uint32_t *)calloc(limb_count, sizeof(uint32_t)); 1147 + if (!limbs) return napi_set_last(env, napi_generic_failure, "out of memory"); 1148 + 1149 + if (word_count == 0) limbs[0] = 0; 1150 + else for (size_t i = 0; i < word_count; i++) { 1151 + limbs[i * 2] = (uint32_t)(words[i] & 0xffffffffu); 1152 + limbs[(i * 2) + 1] = (uint32_t)(words[i] >> 32); 1153 + } 1154 + 1155 + ant_value_t out = js_mkundef(); 1156 + bool ok = napi_make_bigint_limbs(nenv->js, limbs, limb_count, sign_bit != 0, &out); 1157 + free(limbs); 1158 + if (!ok) return napi_set_last(env, napi_generic_failure, "out of memory"); 1159 + 1160 + *result = NAPI_RETURN(nenv, out); 1161 + return napi_set_last(env, napi_ok, NULL); 1162 + } 1163 + 821 1164 NAPI_EXTERN napi_status NAPI_CDECL napi_create_symbol( 822 1165 napi_env env, 823 1166 napi_value description, ··· 831 1174 desc = js_getstr(nenv->js, (ant_value_t)description, NULL); 832 1175 } else if (!is_undefined((ant_value_t)description) && !is_null((ant_value_t)description)) { 833 1176 ant_value_t s = coerce_to_str(nenv->js, (ant_value_t)description); 834 - if (is_err(s)) return napi_check_pending_from_result(env, s); 1177 + if (is_err(s) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, s); 835 1178 desc = js_getstr(nenv->js, s, NULL); 836 1179 } 837 1180 ··· 856 1199 napi_value msg, 857 1200 napi_value *result 858 1201 ) { 859 - (void)code; 860 1202 return napi_make_error_object(env, "Error", msg, result); 861 1203 } 862 1204 ··· 866 1208 napi_value msg, 867 1209 napi_value *result 868 1210 ) { 869 - (void)code; 870 1211 return napi_make_error_object(env, "TypeError", msg, result); 871 1212 } 872 1213 ··· 876 1217 napi_value msg, 877 1218 napi_value *result 878 1219 ) { 879 - (void)code; 880 1220 return napi_make_error_object(env, "RangeError", msg, result); 881 1221 } 882 1222 ··· 1066 1406 return napi_set_last(env, napi_ok, NULL); 1067 1407 } 1068 1408 1409 + NAPI_EXTERN napi_status NAPI_CDECL napi_set_instance_data( 1410 + napi_env env, 1411 + void *data, 1412 + node_api_basic_finalize finalize_cb, 1413 + void *finalize_hint 1414 + ) { 1415 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 1416 + if (!nenv) return napi_set_last(env, napi_invalid_arg, "invalid env"); 1417 + 1418 + nenv->instance_data = data; 1419 + nenv->instance_data_finalize_cb = finalize_cb; 1420 + nenv->instance_data_finalize_hint = finalize_hint; 1421 + return napi_set_last(env, napi_ok, NULL); 1422 + } 1423 + 1424 + NAPI_EXTERN napi_status NAPI_CDECL napi_get_instance_data( 1425 + napi_env env, 1426 + void **data 1427 + ) { 1428 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 1429 + if (!nenv || !data) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1430 + 1431 + *data = nenv->instance_data; 1432 + return napi_set_last(env, napi_ok, NULL); 1433 + } 1434 + 1069 1435 NAPI_EXTERN napi_status NAPI_CDECL napi_create_typedarray( 1070 1436 napi_env env, 1071 1437 napi_typedarray_type type, ··· 1310 1676 return napi_get_string_common(env, value, buf, bufsize, result); 1311 1677 } 1312 1678 1679 + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_utf16( 1680 + napi_env env, 1681 + napi_value value, 1682 + char16_t *buf, 1683 + size_t bufsize, 1684 + size_t *result 1685 + ) { 1686 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 1687 + if (!nenv || !nenv->js) return napi_set_last(env, napi_invalid_arg, "invalid env"); 1688 + if (vtype((ant_value_t)value) != T_STR) return napi_set_last(env, napi_string_expected, "string expected"); 1689 + 1690 + size_t byte_len = 0; 1691 + const char *str = js_getstr(nenv->js, (ant_value_t)value, &byte_len); 1692 + if (!str) return napi_set_last(env, napi_string_expected, "string expected"); 1693 + 1694 + size_t utf16_len = utf16_strlen(str, byte_len); 1695 + if (result) *result = utf16_len; 1696 + if (!buf || bufsize == 0) return napi_set_last(env, napi_ok, NULL); 1697 + 1698 + size_t n = utf16_len < (bufsize - 1) ? utf16_len : (bufsize - 1); 1699 + for (size_t i = 0; i < n; i++) buf[i] = (char16_t)utf16_code_unit_at(str, byte_len, i); 1700 + buf[n] = 0; 1701 + return napi_set_last(env, napi_ok, NULL); 1702 + } 1703 + 1704 + NAPI_EXTERN napi_status NAPI_CDECL napi_get_date_value( 1705 + napi_env env, 1706 + napi_value value, 1707 + double *result 1708 + ) { 1709 + if (!env || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1710 + if (!is_date_instance((ant_value_t)value)) return napi_set_last(env, napi_date_expected, "date expected"); 1711 + 1712 + ant_value_t time_val = js_get_slot((ant_value_t)value, SLOT_DATA); 1713 + *result = vtype(time_val) == T_NUM ? js_getnum(time_val) : JS_NAN; 1714 + return napi_set_last(env, napi_ok, NULL); 1715 + } 1716 + 1717 + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_int64( 1718 + napi_env env, 1719 + napi_value value, 1720 + int64_t *result, 1721 + bool *lossless 1722 + ) { 1723 + if (!env || !result || !lossless) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1724 + if (vtype((ant_value_t)value) != T_BIGINT) return napi_set_last(env, napi_bigint_expected, "bigint expected"); 1725 + 1726 + size_t limb_count = 0; 1727 + const uint32_t *limbs = napi_bigint_limbs(value, &limb_count); 1728 + uint64_t magnitude = napi_bigint_low_u64(limbs, limb_count); 1729 + bool negative = napi_bigint_is_negative(value) && !napi_bigint_limbs_is_zero(limbs, limb_count); 1730 + uint64_t bits = negative ? (uint64_t)(~magnitude + 1) : magnitude; 1731 + 1732 + *result = (int64_t)bits; 1733 + *lossless = limb_count <= 2 1734 + && ((!negative && magnitude <= (uint64_t)INT64_MAX) 1735 + || (negative && magnitude <= (UINT64_C(1) << 63))); 1736 + return napi_set_last(env, napi_ok, NULL); 1737 + } 1738 + 1739 + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_uint64( 1740 + napi_env env, 1741 + napi_value value, 1742 + uint64_t *result, 1743 + bool *lossless 1744 + ) { 1745 + if (!env || !result || !lossless) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1746 + if (vtype((ant_value_t)value) != T_BIGINT) return napi_set_last(env, napi_bigint_expected, "bigint expected"); 1747 + 1748 + size_t limb_count = 0; 1749 + const uint32_t *limbs = napi_bigint_limbs(value, &limb_count); 1750 + uint64_t magnitude = napi_bigint_low_u64(limbs, limb_count); 1751 + bool negative = napi_bigint_is_negative(value) && !napi_bigint_limbs_is_zero(limbs, limb_count); 1752 + 1753 + *result = negative ? (uint64_t)(~magnitude + 1) : magnitude; 1754 + *lossless = !negative && limb_count <= 2; 1755 + return napi_set_last(env, napi_ok, NULL); 1756 + } 1757 + 1758 + NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_words( 1759 + napi_env env, 1760 + napi_value value, 1761 + int *sign_bit, 1762 + size_t *word_count, 1763 + uint64_t *words 1764 + ) { 1765 + if (!env || !word_count) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1766 + if (vtype((ant_value_t)value) != T_BIGINT) return napi_set_last(env, napi_bigint_expected, "bigint expected"); 1767 + 1768 + size_t limb_count = 0; 1769 + const uint32_t *limbs = napi_bigint_limbs(value, &limb_count); 1770 + size_t actual_words = limb_count == 0 ? 0 : (limb_count + 1) / 2; 1771 + size_t capacity = words ? *word_count : 0; 1772 + 1773 + if (sign_bit) { 1774 + bool negative = napi_bigint_is_negative(value) && !napi_bigint_limbs_is_zero(limbs, limb_count); 1775 + *sign_bit = negative ? 1 : 0; 1776 + } 1777 + 1778 + if (words) { 1779 + size_t n = capacity < actual_words ? capacity : actual_words; 1780 + for (size_t i = 0; i < n; i++) { 1781 + uint64_t lo = i * 2 < limb_count ? (uint64_t)limbs[i * 2] : 0; 1782 + uint64_t hi = (i * 2 + 1) < limb_count ? (uint64_t)limbs[i * 2 + 1] : 0; 1783 + words[i] = lo | (hi << 32); 1784 + }} 1785 + 1786 + *word_count = actual_words; 1787 + return napi_set_last(env, napi_ok, NULL); 1788 + } 1789 + 1313 1790 NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_external( 1314 1791 napi_env env, 1315 1792 napi_value value, ··· 1377 1854 } 1378 1855 1379 1856 ant_value_t len = js_get(nenv->js, v, "length"); 1857 + if (is_err(len) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, len); 1380 1858 if (vtype(len) != T_NUM) return napi_set_last(env, napi_array_expected, "array expected"); 1381 1859 *result = (uint32_t)js_getnum(len); 1382 1860 return napi_set_last(env, napi_ok, NULL); ··· 1439 1917 if (type) *type = napi_from_ant_typedarray_type(ta->type); 1440 1918 if (length) *length = ta->length; 1441 1919 if (data) *data = ta->buffer->data + ta->byte_offset; 1442 - if (arraybuffer) *arraybuffer = (napi_value)js_get(nenv->js, (ant_value_t)typedarray, "buffer"); 1920 + if (arraybuffer) { 1921 + ant_value_t buffer = js_get(nenv->js, (ant_value_t)typedarray, "buffer"); 1922 + if (is_err(buffer) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, buffer); 1923 + *arraybuffer = NAPI_RETURN(nenv, buffer); 1924 + } 1443 1925 if (byte_offset) *byte_offset = ta->byte_offset; 1444 1926 return napi_set_last(env, napi_ok, NULL); 1445 1927 } ··· 1451 1933 ) { 1452 1934 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 1453 1935 if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1454 - *result = NAPI_RETURN(nenv, js_get_proto(nenv->js, (ant_value_t)object)); 1936 + ant_value_t proto = js_get_proto(nenv->js, (ant_value_t)object); 1937 + if (is_err(proto) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, proto); 1938 + *result = NAPI_RETURN(nenv, proto); 1455 1939 return napi_set_last(env, napi_ok, NULL); 1456 1940 } 1457 1941 ··· 1479 1963 return napi_set_last(env, napi_ok, NULL); 1480 1964 } 1481 1965 1966 + NAPI_EXTERN napi_status NAPI_CDECL napi_get_all_property_names( 1967 + napi_env env, 1968 + napi_value object, 1969 + napi_key_collection_mode key_mode, 1970 + napi_key_filter key_filter, 1971 + napi_key_conversion key_conversion, 1972 + napi_value *result 1973 + ) { 1974 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 1975 + if (!nenv || !nenv->js || !result || !is_object_type((ant_value_t)object)) { 1976 + return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1977 + } 1978 + 1979 + ant_t *js = nenv->js; 1980 + ant_value_t out = js_mkarr(js); 1981 + ant_value_t seen = js_mkobj(js); 1982 + ant_value_t current = (ant_value_t)object; 1983 + 1984 + while (is_object_type(current)) { 1985 + ant_iter_t iter = js_prop_iter_begin(js, current); 1986 + ant_value_t key = js_mkundef(); 1987 + 1988 + while (js_prop_iter_next_val(&iter, &key, NULL)) { 1989 + ant_object_t *obj_ptr = js_obj_ptr(js_as_obj(current)); 1990 + if (!obj_ptr || !obj_ptr->shape || iter.off == 0) continue; 1991 + 1992 + const ant_shape_prop_t *prop = ant_shape_prop_at(obj_ptr->shape, (uint32_t)(iter.off - 1)); 1993 + if (!napi_key_passes_filter(prop, key_filter)) continue; 1994 + 1995 + uint8_t key_type = vtype(key); 1996 + if ((key_filter & napi_key_skip_strings) && key_type != T_SYMBOL) continue; 1997 + if ((key_filter & napi_key_skip_symbols) && key_type == T_SYMBOL) continue; 1998 + if (napi_seen_has_key(js, seen, key)) continue; 1999 + if (!napi_seen_add_key(js, seen, key)) { 2000 + js_prop_iter_end(&iter); 2001 + return napi_set_last(env, napi_generic_failure, "failed to collect property names"); 2002 + } 2003 + 2004 + js_arr_push(js, out, napi_convert_property_key(js, key, key_conversion)); 2005 + if (js->thrown_exists) { 2006 + js_prop_iter_end(&iter); 2007 + return napi_check_pending_from_result(env, js_mkundef()); 2008 + }} 2009 + 2010 + js_prop_iter_end(&iter); 2011 + if (key_mode == napi_key_own_only) break; 2012 + current = js_get_proto(js, current); 2013 + if (is_err(current) || js->thrown_exists) return napi_check_pending_from_result(env, current); 2014 + } 2015 + 2016 + *result = NAPI_RETURN(nenv, out); 2017 + return napi_set_last(env, napi_ok, NULL); 2018 + } 2019 + 1482 2020 NAPI_EXTERN napi_status NAPI_CDECL napi_get_version( 1483 2021 node_api_basic_env env, 1484 2022 uint32_t *result ··· 1508 2046 return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1509 2047 } 1510 2048 ant_value_t r = js_setprop(nenv->js, (ant_value_t)object, (ant_value_t)key, (ant_value_t)value); 1511 - if (is_err(r)) return napi_check_pending_from_result(env, r); 2049 + if (is_err(r) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, r); 1512 2050 return napi_set_last(env, napi_ok, NULL); 1513 2051 } 1514 2052 ··· 1526 2064 ant_value_t k = (ant_value_t)key; 1527 2065 if (vtype(k) == T_SYMBOL) { 1528 2066 ant_offset_t off = lkp_sym_proto(nenv->js, (ant_value_t)object, (ant_offset_t)vdata(k)); 1529 - *result = NAPI_RETURN(nenv, (off ? js_propref_load(nenv->js, off) : js_mkundef())); 2067 + ant_value_t out = off ? js_propref_load(nenv->js, off) : js_mkundef(); 2068 + if (is_err(out) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, out); 2069 + *result = NAPI_RETURN(nenv, out); 1530 2070 return napi_set_last(env, napi_ok, NULL); 1531 2071 } 1532 2072 ··· 1542 2082 memcpy(name, ks, klen); 1543 2083 name[klen] = '\0'; 1544 2084 1545 - *result = NAPI_RETURN(nenv, js_getprop_fallback(nenv->js, (ant_value_t)object, name)); 2085 + ant_value_t out = js_getprop_fallback(nenv->js, (ant_value_t)object, name); 1546 2086 free(name); 1547 - 2087 + if (is_err(out) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, out); 2088 + *result = NAPI_RETURN(nenv, out); 1548 2089 return napi_set_last(env, napi_ok, NULL); 1549 2090 } 1550 2091 ··· 1566 2107 } 1567 2108 1568 2109 ant_value_t kstr = coerce_to_str(nenv->js, k); 1569 - if (is_err(kstr)) return napi_check_pending_from_result(env, kstr); 2110 + if (is_err(kstr) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, kstr); 1570 2111 size_t len = 0; 1571 2112 const char *s = js_getstr(nenv->js, kstr, &len); 1572 2113 *result = s && lkp_proto(nenv->js, (ant_value_t)object, s, len) != 0; ··· 1591 2132 del_result = js_delete_sym_prop(nenv->js, (ant_value_t)object, k); 1592 2133 } else { 1593 2134 ant_value_t kstr = coerce_to_str(nenv->js, k); 1594 - if (is_err(kstr)) return napi_check_pending_from_result(env, kstr); 2135 + if (is_err(kstr) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, kstr); 1595 2136 size_t len = 0; 1596 2137 const char *s = js_getstr(nenv->js, kstr, &len); 1597 2138 del_result = js_delete_prop(nenv->js, (ant_value_t)object, s, len); 1598 2139 } 1599 2140 1600 - if (is_err(del_result)) return napi_check_pending_from_result(env, del_result); 2141 + if (is_err(del_result) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, del_result); 1601 2142 *result = js_truthy(nenv->js, del_result); 1602 2143 return napi_set_last(env, napi_ok, NULL); 1603 2144 } ··· 1613 2154 return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1614 2155 } 1615 2156 js_set(nenv->js, (ant_value_t)object, utf8name, (ant_value_t)value); 1616 - return napi_set_last(env, napi_ok, NULL); 2157 + return napi_return_pending_if_any(env); 1617 2158 } 1618 2159 1619 2160 NAPI_EXTERN napi_status NAPI_CDECL napi_get_named_property( ··· 1626 2167 if (!nenv || !nenv->js || !result || !utf8name || !is_object_type((ant_value_t)object)) { 1627 2168 return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1628 2169 } 1629 - *result = (napi_value)js_getprop_fallback(nenv->js, (ant_value_t)object, utf8name); 2170 + ant_value_t out = js_getprop_fallback(nenv->js, (ant_value_t)object, utf8name); 2171 + if (is_err(out) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, out); 2172 + *result = NAPI_RETURN(nenv, out); 1630 2173 return napi_set_last(env, napi_ok, NULL); 1631 2174 } 1632 2175 ··· 1662 2205 nenv->js, (ant_value_t)object, 1663 2206 key, (ant_value_t)value 1664 2207 ); 1665 - if (is_err(r)) return napi_check_pending_from_result(env, r); 2208 + if (is_err(r) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, r); 1666 2209 return napi_set_last(env, napi_ok, NULL); 1667 2210 } 1668 2211 ··· 1678 2221 } 1679 2222 char idx[32]; 1680 2223 snprintf(idx, sizeof(idx), "%u", index); 1681 - *result = NAPI_RETURN(nenv, js_get(nenv->js, (ant_value_t)object, idx)); 2224 + ant_value_t out = js_get(nenv->js, (ant_value_t)object, idx); 2225 + if (is_err(out) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, out); 2226 + *result = NAPI_RETURN(nenv, out); 1682 2227 return napi_set_last(env, napi_ok, NULL); 1683 2228 } 1684 2229 ··· 1711 2256 char idx[32]; 1712 2257 snprintf(idx, sizeof(idx), "%u", index); 1713 2258 ant_value_t del = js_delete_prop(nenv->js, (ant_value_t)object, idx, strlen(idx)); 1714 - if (is_err(del)) return napi_check_pending_from_result(env, del); 2259 + if (is_err(del) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, del); 1715 2260 *result = js_truthy(nenv->js, del); 1716 2261 return napi_set_last(env, napi_ok, NULL); 1717 2262 } ··· 1749 2294 if (p->method) { 1750 2295 napi_value fn = 0; 1751 2296 napi_status st = napi_create_function_common( 1752 - env, 1753 - p->utf8name, 2297 + env, p->utf8name, 1754 2298 NAPI_AUTO_LENGTH, 1755 - p->method, 1756 - p->data, 1757 - &fn 2299 + p->method, p->data, &fn 1758 2300 ); 1759 2301 if (st != napi_ok) return st; 1760 2302 value = (ant_value_t)fn; 1761 2303 } else if (p->getter || p->setter) { 1762 2304 napi_value getter_fn = 0; 1763 2305 napi_value setter_fn = 0; 2306 + 1764 2307 if (p->getter) { 1765 2308 napi_status st = napi_create_function_common(env, p->utf8name, NAPI_AUTO_LENGTH, p->getter, p->data, &getter_fn); 1766 2309 if (st != napi_ok) return st; 1767 2310 } 2311 + 1768 2312 if (p->setter) { 1769 2313 napi_status st = napi_create_function_common(env, p->utf8name, NAPI_AUTO_LENGTH, p->setter, p->data, &setter_fn); 1770 2314 if (st != napi_ok) return st; ··· 1772 2316 1773 2317 int flags = napi_desc_flags(p->attributes); 1774 2318 ant_value_t desc_obj = js_as_obj((ant_value_t)object); 2319 + 1775 2320 if (is_symbol) { 1776 2321 if (p->getter) js_set_sym_getter_desc(nenv->js, desc_obj, key, (ant_value_t)getter_fn, flags); 1777 2322 if (p->setter) js_set_sym_setter_desc(nenv->js, desc_obj, key, (ant_value_t)setter_fn, flags); 1778 - } else { 1779 - js_set_accessor_desc( 1780 - nenv->js, 1781 - desc_obj, 1782 - key_str, 1783 - key_len, 1784 - p->getter ? (ant_value_t)getter_fn : js_mkundef(), 1785 - p->setter ? (ant_value_t)setter_fn : js_mkundef(), 1786 - flags 1787 - ); 1788 - } 2323 + } else js_set_accessor_desc( 2324 + nenv->js, desc_obj, 2325 + key_str, key_len, 2326 + p->getter ? (ant_value_t)getter_fn : js_mkundef(), 2327 + p->setter ? (ant_value_t)setter_fn : js_mkundef(), 2328 + flags 2329 + ); 2330 + 2331 + if (nenv->js->thrown_exists) return napi_check_pending_from_result(env, js_mkundef()); 1789 2332 continue; 1790 - } else { 1791 - value = (ant_value_t)p->value; 1792 - } 2333 + } else value = (ant_value_t)p->value; 1793 2334 1794 - if (is_symbol) { 1795 - js_set_sym(nenv->js, (ant_value_t)object, key, value); 1796 - } else { 2335 + if (is_symbol) js_set_sym(nenv->js, (ant_value_t)object, key, value); 2336 + else { 1797 2337 js_set(nenv->js, (ant_value_t)object, key_str, value); 1798 2338 js_set_descriptor(nenv->js, js_as_obj((ant_value_t)object), key_str, key_len, napi_desc_flags(p->attributes)); 1799 2339 } 2340 + 2341 + if (nenv->js->thrown_exists) return napi_check_pending_from_result(env, js_mkundef()); 1800 2342 } 1801 2343 1802 2344 return napi_set_last(env, napi_ok, NULL); ··· 1826 2368 js_mark_constructor(ctor, true); 1827 2369 1828 2370 ant_value_t proto = js_get(nenv->js, ctor, "prototype"); 2371 + if (is_err(proto) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, proto); 1829 2372 if (!is_object_type(proto)) { 1830 2373 proto = js_mkobj(nenv->js); 1831 2374 js_set(nenv->js, ctor, "prototype", proto); 2375 + if (nenv->js->thrown_exists) return napi_check_pending_from_result(env, js_mkundef()); 1832 2376 } 1833 2377 1834 2378 for (size_t i = 0; i < property_count; i++) { ··· 1860 2404 } 1861 2405 1862 2406 ant_value_t kstr = coerce_to_str(nenv->js, k); 1863 - if (is_err(kstr)) return napi_check_pending_from_result(env, kstr); 2407 + if (is_err(kstr) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, kstr); 1864 2408 size_t len = 0; 1865 2409 const char *s = js_getstr(nenv->js, kstr, &len); 1866 2410 *result = s && lkp(nenv->js, (ant_value_t)object, s, len) != 0; ··· 1906 2450 ) { 1907 2451 if (!env || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 1908 2452 *result = vtype((ant_value_t)value) == T_ARR; 2453 + return napi_set_last(env, napi_ok, NULL); 2454 + } 2455 + 2456 + NAPI_EXTERN napi_status NAPI_CDECL napi_is_date( 2457 + napi_env env, 2458 + napi_value value, 2459 + bool *result 2460 + ) { 2461 + if (!env || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 2462 + *result = is_date_instance((ant_value_t)value); 1909 2463 return napi_set_last(env, napi_ok, NULL); 1910 2464 } 1911 2465 ··· 2004 2558 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 2005 2559 if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 2006 2560 ant_value_t r = do_instanceof(nenv->js, (ant_value_t)object, (ant_value_t)constructor); 2007 - if (is_err(r)) return napi_check_pending_from_result(env, r); 2561 + if (is_err(r) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, r); 2008 2562 *result = js_truthy(nenv->js, r); 2009 2563 return napi_set_last(env, napi_ok, NULL); 2010 2564 } 2011 - 2012 - static _Thread_local int napi_strict_eq_call_count = 0; 2013 2565 2014 2566 NAPI_EXTERN napi_status NAPI_CDECL napi_strict_equals( 2015 2567 napi_env env, ··· 2020 2572 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 2021 2573 if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 2022 2574 *result = strict_eq_values(nenv->js, (ant_value_t)lhs, (ant_value_t)rhs); 2023 - if (getenv("ANT_NAPI_TRACE")) { 2024 - napi_strict_eq_call_count++; 2025 - if (napi_strict_eq_call_count >= 2000 && napi_strict_eq_call_count < 2030) { 2026 - fprintf(stderr, "[napi-seq] call#%d lhs=0x%llx(vt=%d) rhs=0x%llx(vt=%d) => %d\n", 2027 - napi_strict_eq_call_count, 2028 - (unsigned long long)(ant_value_t)lhs, vtype((ant_value_t)lhs), 2029 - (unsigned long long)(ant_value_t)rhs, vtype((ant_value_t)rhs), 2030 - *result); 2031 - } 2032 - } 2033 2575 return napi_set_last(env, napi_ok, NULL); 2034 2576 } 2035 2577 ··· 2105 2647 ) { 2106 2648 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 2107 2649 if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 2108 - *result = NAPI_RETURN(nenv, js_bool(js_truthy(nenv->js, (ant_value_t)value))); 2650 + bool truthy = js_truthy(nenv->js, (ant_value_t)value); 2651 + if (nenv->js->thrown_exists) return napi_check_pending_from_result(env, js_mkundef()); 2652 + *result = NAPI_RETURN(nenv, js_bool(truthy)); 2109 2653 return napi_set_last(env, napi_ok, NULL); 2110 2654 } 2111 2655 ··· 2116 2660 ) { 2117 2661 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 2118 2662 if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 2119 - *result = NAPI_RETURN(nenv, js_mknum(js_to_number(nenv->js, (ant_value_t)value))); 2663 + double num = js_to_number(nenv->js, (ant_value_t)value); 2664 + if (nenv->js->thrown_exists) return napi_check_pending_from_result(env, js_mkundef()); 2665 + *result = NAPI_RETURN(nenv, js_mknum(num)); 2120 2666 return napi_set_last(env, napi_ok, NULL); 2121 2667 } 2122 2668 ··· 2134 2680 } 2135 2681 2136 2682 ant_value_t obj_ctor = js_get(nenv->js, js_glob(nenv->js), "Object"); 2683 + if (is_err(obj_ctor) || nenv->js->thrown_exists) return napi_check_pending_from_result(env, obj_ctor); 2137 2684 if (!is_callable(obj_ctor)) return napi_set_last(env, napi_generic_failure, "Object constructor missing"); 2138 2685 ant_value_t arg = (ant_value_t)value; 2139 2686 ant_value_t out = sv_vm_call(nenv->js->vm, nenv->js, obj_ctor, js_mkundef(), &arg, 1, NULL, false); ··· 2155 2702 return napi_set_last(env, napi_ok, NULL); 2156 2703 } 2157 2704 2158 - NAPI_EXTERN napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error) { 2159 - ant_napi_env_t *nenv = (ant_napi_env_t *)env; 2160 - if (!nenv || !nenv->js) return napi_set_last(env, napi_invalid_arg, "invalid env"); 2161 - js_throw(nenv->js, (ant_value_t)error); 2162 - napi_mark_pending_exception(env, error); 2163 - return napi_pending_exception; 2164 - } 2165 - 2166 2705 NAPI_EXTERN napi_status NAPI_CDECL napi_throw_error( 2167 2706 napi_env env, 2168 2707 const char *code, ··· 2197 2736 ant_napi_env_t *nenv = (ant_napi_env_t *)env; 2198 2737 if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); 2199 2738 *result = nenv->has_pending_exception || nenv->js->thrown_exists; 2200 - return napi_set_last(env, napi_ok, NULL); 2739 + return napi_set_last_raw(env, napi_ok, NULL); 2201 2740 } 2202 2741 2203 2742 NAPI_EXTERN napi_status NAPI_CDECL napi_get_and_clear_last_exception( ··· 2269 2808 entry->native_object = native_object; 2270 2809 entry->finalize_cb = finalize_cb; 2271 2810 entry->finalize_hint = finalize_hint; 2811 + entry->has_wrap = true; 2272 2812 2273 2813 if (result) { 2274 2814 return napi_create_reference(env, js_object, 0, result); ··· 2276 2816 return napi_set_last(env, napi_ok, NULL); 2277 2817 } 2278 2818 2819 + NAPI_EXTERN napi_status NAPI_CDECL napi_add_finalizer( 2820 + napi_env env, 2821 + napi_value js_object, 2822 + void *finalize_data, 2823 + node_api_basic_finalize finalize_cb, 2824 + void *finalize_hint, 2825 + napi_ref *result 2826 + ) { 2827 + ant_napi_env_t *nenv = (ant_napi_env_t *)env; 2828 + if (!nenv || !nenv->js || !is_object_type((ant_value_t)js_object)) { 2829 + return napi_set_last(env, napi_invalid_arg, "invalid argument"); 2830 + } 2831 + 2832 + napi_wrap_entry_t *entry = napi_find_wrap(nenv->js, js_object); 2833 + if (!entry) { 2834 + entry = (napi_wrap_entry_t *)calloc(1, sizeof(*entry)); 2835 + if (!entry) return napi_set_last(env, napi_generic_failure, "out of memory"); 2836 + entry->id = g_napi_wrap_next_id++; 2837 + HASH_ADD(hh, g_napi_wraps, id, sizeof(entry->id), entry); 2838 + napi_slot_set_u64(nenv->js, (ant_value_t)js_object, SLOT_NAPI_WRAP_ID, entry->id); 2839 + } 2840 + 2841 + entry->attached_data = finalize_data; 2842 + entry->attached_finalize_cb = finalize_cb; 2843 + entry->attached_finalize_hint = finalize_hint; 2844 + 2845 + if (result) return napi_create_reference(env, js_object, 0, result); 2846 + return napi_set_last(env, napi_ok, NULL); 2847 + } 2848 + 2279 2849 NAPI_EXTERN napi_status NAPI_CDECL napi_unwrap( 2280 2850 napi_env env, 2281 2851 napi_value js_object, ··· 2286 2856 return napi_set_last(env, napi_invalid_arg, "invalid argument"); 2287 2857 } 2288 2858 napi_wrap_entry_t *entry = napi_find_wrap(nenv->js, js_object); 2289 - if (!entry) return napi_set_last(env, napi_invalid_arg, "object not wrapped"); 2859 + if (!entry || !entry->has_wrap) return napi_set_last(env, napi_invalid_arg, "object not wrapped"); 2290 2860 *result = entry->native_object; 2291 2861 return napi_set_last(env, napi_ok, NULL); 2292 2862 } ··· 2302 2872 } 2303 2873 2304 2874 napi_wrap_entry_t *entry = napi_find_wrap(nenv->js, js_object); 2305 - if (!entry) return napi_set_last(env, napi_invalid_arg, "object not wrapped"); 2875 + if (!entry || !entry->has_wrap) return napi_set_last(env, napi_invalid_arg, "object not wrapped"); 2306 2876 2307 2877 if (result) *result = entry->native_object; 2308 - HASH_DEL(g_napi_wraps, entry); 2309 - free(entry); 2310 - js_set_slot((ant_value_t)js_object, SLOT_NAPI_WRAP_ID, js_mkundef()); 2878 + entry->native_object = NULL; 2879 + entry->finalize_cb = NULL; 2880 + entry->finalize_hint = NULL; 2881 + entry->has_wrap = false; 2882 + 2883 + if (!entry->attached_finalize_cb) { 2884 + HASH_DEL(g_napi_wraps, entry); 2885 + free(entry); 2886 + js_set_slot((ant_value_t)js_object, SLOT_NAPI_WRAP_ID, js_mkundef()); 2887 + } 2311 2888 return napi_set_last(env, napi_ok, NULL); 2312 2889 } 2313 2890
+2
src/modules/path.c
··· 853 853 854 854 js_set(js, variant, "sep", js_mkstr(js, path_sep_str(style), 1)); 855 855 js_set(js, variant, "delimiter", js_mkstr(js, path_delimiter_str(style), 1)); 856 + js_set(js, variant, "default", variant); 856 857 857 858 return variant; 858 859 } ··· 869 870 js_set(js, lib, "posix", posix); 870 871 js_set(js, lib, "win32", win32); 871 872 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "path", 4)); 873 + js_set(js, lib, "default", lib); 872 874 873 875 return lib; 874 876 }
+2 -5
src/modules/process.c
··· 1819 1819 1820 1820 ant_value_t process_library(ant_t *js) { 1821 1821 ant_value_t process_obj = js_get(js, js_glob(js), "process"); 1822 + js_set(js, process_obj, "default", process_obj); 1822 1823 js_set_slot_wb(js, process_obj, SLOT_DEFAULT, process_obj); 1823 1824 return process_obj; 1824 1825 } ··· 1862 1863 1863 1864 js_set(js, process_obj, "pid", js_mknum((double)getpid())); 1864 1865 js_set(js, process_obj, "ppid", js_mknum((double)getppid())); 1865 - 1866 - char version_str[128]; 1867 - snprintf(version_str, sizeof(version_str), "v%s", ANT_VERSION); 1868 - js_set(js, process_obj, "version", js_mkstr(js, version_str, strlen(version_str))); 1869 - 1866 + 1870 1867 ant_value_t versions_obj = js_mkobj(js); 1871 1868 js_set(js, versions_obj, "ant", js_mkstr(js, ANT_VERSION, strlen(ANT_VERSION))); 1872 1869 char uv_ver[32];
+21 -1
src/modules/reflect.c
··· 171 171 if (vtype(target) != T_FUNC && vtype(target) != T_CFUNC) { 172 172 return js_mkerr(js, "Reflect.apply: first argument must be a function"); 173 173 } 174 + 175 + if (vtype(args_arr) == T_UNDEF || vtype(args_arr) == T_NULL) return js_mkerr_typed( 176 + js, JS_ERR_TYPE, 177 + "Reflect.apply: third argument must be an array-like object" 178 + ); 179 + 180 + ant_value_t result; 181 + if (vtype(args_arr) == T_ARR) { 182 + ant_value_t *call_args = NULL; 183 + int arg_count = extract_array_args(js, args_arr, &call_args); 184 + result = sv_vm_call_explicit_this( 185 + js->vm, js, target, this_arg, 186 + call_args, arg_count 187 + ); 188 + if (call_args) free(call_args); 189 + return result; 190 + } 174 191 175 192 ant_value_t length_val = js_get(js, args_arr, "length"); 176 193 int arg_count = 0; ··· 190 207 } 191 208 } 192 209 193 - ant_value_t result = sv_vm_call(js->vm, js, target, this_arg, call_args, arg_count, NULL, false); 210 + result = sv_vm_call_explicit_this( 211 + js->vm, js, target, this_arg, 212 + call_args, arg_count 213 + ); 194 214 195 215 if (call_args) free(call_args); 196 216 return result;
+35
src/modules/stream.c
··· 1619 1619 return js_mkundef(); 1620 1620 } 1621 1621 1622 + static void stream_web_copy_global(ant_t *js, ant_value_t obj, const char *name) { 1623 + ant_value_t value = js_get(js, js->global, name); 1624 + if (is_err(value)) return; 1625 + js_set(js, obj, name, value); 1626 + } 1627 + 1628 + // TODO: remove copy-on-start 1629 + static void stream_web_define_common(ant_t *js, ant_value_t obj) { 1630 + stream_web_copy_global(js, obj, "ReadableStream"); 1631 + stream_web_copy_global(js, obj, "ReadableStreamDefaultReader"); 1632 + stream_web_copy_global(js, obj, "ReadableStreamDefaultController"); 1633 + stream_web_copy_global(js, obj, "WritableStream"); 1634 + stream_web_copy_global(js, obj, "WritableStreamDefaultWriter"); 1635 + stream_web_copy_global(js, obj, "WritableStreamDefaultController"); 1636 + stream_web_copy_global(js, obj, "TransformStream"); 1637 + stream_web_copy_global(js, obj, "TransformStreamDefaultController"); 1638 + stream_web_copy_global(js, obj, "ByteLengthQueuingStrategy"); 1639 + stream_web_copy_global(js, obj, "CountQueuingStrategy"); 1640 + stream_web_copy_global(js, obj, "TextEncoderStream"); 1641 + stream_web_copy_global(js, obj, "TextDecoderStream"); 1642 + stream_web_copy_global(js, obj, "CompressionStream"); 1643 + stream_web_copy_global(js, obj, "DecompressionStream"); 1644 + } 1645 + 1622 1646 ant_value_t stream_library(ant_t *js) { 1623 1647 ant_value_t lib = js_mkobj(js); 1624 1648 ant_value_t promises = js_mkobj(js); ··· 1670 1694 1671 1695 return promises; 1672 1696 } 1697 + 1698 + ant_value_t stream_web_library(ant_t *js) { 1699 + ant_value_t lib = js_mkobj(js); 1700 + 1701 + stream_web_define_common(js, lib); 1702 + js_set(js, lib, "default", lib); 1703 + js_set_slot_wb(js, lib, SLOT_DEFAULT, lib); 1704 + js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "stream/web", 10)); 1705 + 1706 + return lib; 1707 + }
+21 -2
src/modules/tty.c
··· 1 1 #include <compat.h> // IWYU pragma: keep 2 2 3 3 #include <stdbool.h> 4 + #include <errno.h> 4 5 #include <stdio.h> 5 6 #include <stdlib.h> 6 7 #include <string.h> ··· 17 18 #define ANT_STDOUT_FD 1 18 19 #define ANT_STDERR_FD 2 19 20 #else 21 + #include <signal.h> 20 22 #include <sys/ioctl.h> 21 23 #include <termios.h> 22 24 #include <unistd.h> ··· 264 266 bool active; 265 267 struct termios saved; 266 268 } raw_state = { .fd = -1, .active = false }; 269 + 270 + static int tty_tcsetattr_no_sigtou(int fd, int optional_actions, const struct termios *tio) { 271 + #ifdef SIGTTOU 272 + sigset_t block_set; 273 + sigset_t prev_set; 274 + if (sigemptyset(&block_set) == 0 275 + && sigaddset(&block_set, SIGTTOU) == 0 276 + && sigprocmask(SIG_BLOCK, &block_set, &prev_set) == 0) { 277 + int rc = tcsetattr(fd, optional_actions, tio); 278 + int saved_errno = errno; 279 + sigprocmask(SIG_SETMASK, &prev_set, NULL); 280 + errno = saved_errno; 281 + return rc; 282 + } 283 + #endif 284 + return tcsetattr(fd, optional_actions, tio); 285 + } 267 286 #endif 268 287 269 288 bool tty_set_raw_mode(int fd, bool enable) { ··· 302 321 #ifdef VLNEXT 303 322 raw.c_cc[VLNEXT] = _POSIX_VDISABLE; 304 323 #endif 305 - if (tcsetattr(fd, TCSANOW, &raw) == -1) return false; 324 + if (tty_tcsetattr_no_sigtou(fd, TCSANOW, &raw) == -1) return false; 306 325 raw_state.fd = fd; 307 326 raw_state.saved = saved; 308 327 raw_state.active = true; ··· 310 329 } 311 330 312 331 if (!(raw_state.active && raw_state.fd == fd)) return true; 313 - if (tcsetattr(fd, TCSANOW, &raw_state.saved) == -1) return false; 332 + if (tty_tcsetattr_no_sigtou(fd, TCSANOW, &raw_state.saved) == -1) return false; 314 333 raw_state.fd = -1; 315 334 raw_state.active = false; 316 335 return true;
+256
src/modules/url.c
··· 1339 1339 return make_url_obj(js, s); 1340 1340 } 1341 1341 1342 + typedef struct { 1343 + char *buf; 1344 + size_t len; 1345 + size_t cap; 1346 + } url_fmt_buf_t; 1347 + 1348 + static bool url_fmt_reserve(url_fmt_buf_t *b, size_t extra) { 1349 + if (extra <= b->cap - b->len) return true; 1350 + 1351 + size_t needed = b->len + extra + 1; 1352 + size_t next = b->cap ? b->cap : 128; 1353 + while (next < needed) next *= 2; 1354 + 1355 + char *buf = realloc(b->buf, next); 1356 + if (!buf) return false; 1357 + b->buf = buf; 1358 + b->cap = next; 1359 + 1360 + return true; 1361 + } 1362 + 1363 + static bool url_fmt_append_n(url_fmt_buf_t *b, const char *s, size_t n) { 1364 + if (!s || n == 0) return true; 1365 + if (!url_fmt_reserve(b, n)) return false; 1366 + memcpy(b->buf + b->len, s, n); 1367 + b->len += n; 1368 + b->buf[b->len] = '\0'; 1369 + return true; 1370 + } 1371 + 1372 + static bool url_fmt_append(url_fmt_buf_t *b, const char *s) { 1373 + return url_fmt_append_n(b, s, s ? strlen(s) : 0); 1374 + } 1375 + 1376 + static bool url_fmt_append_c(url_fmt_buf_t *b, char c) { 1377 + if (!url_fmt_reserve(b, 1)) return false; 1378 + b->buf[b->len++] = c; 1379 + b->buf[b->len] = '\0'; 1380 + return true; 1381 + } 1382 + 1383 + static bool url_fmt_append_value_string(ant_t *js, url_fmt_buf_t *b, ant_value_t value) { 1384 + ant_value_t str_val = vtype(value) == T_STR ? value : js_tostring_val(js, value); 1385 + if (is_err(str_val)) return false; 1386 + 1387 + size_t len = 0; 1388 + const char *str = js_getstr(js, str_val, &len); 1389 + return str && url_fmt_append_n(b, str, len); 1390 + } 1391 + 1392 + static bool url_fmt_get_string_prop( 1393 + ant_t *js, 1394 + ant_value_t obj, 1395 + const char *name, 1396 + ant_value_t *out, 1397 + const char **str, 1398 + size_t *len 1399 + ) { 1400 + *out = js_get(js, obj, name); 1401 + if (is_undefined(*out) || is_null(*out)) return false; 1402 + 1403 + ant_value_t str_val = vtype(*out) == T_STR ? *out : js_tostring_val(js, *out); 1404 + if (is_err(str_val)) return false; 1405 + 1406 + *out = str_val; 1407 + *str = js_getstr(js, str_val, len); 1408 + return *str != NULL; 1409 + } 1410 + 1411 + static bool url_fmt_is_query_unescaped(unsigned char c) { 1412 + return isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~'; 1413 + } 1414 + 1415 + static bool url_fmt_append_query_component(url_fmt_buf_t *b, const char *s, size_t len) { 1416 + static const char hex[] = "0123456789ABCDEF"; 1417 + 1418 + for (size_t i = 0; i < len; i++) { 1419 + unsigned char c = (unsigned char)s[i]; 1420 + if (url_fmt_is_query_unescaped(c)) { 1421 + if (!url_fmt_append_c(b, (char)c)) return false; 1422 + } else { 1423 + char esc[3] = { '%', hex[c >> 4], hex[c & 0x0f] }; 1424 + if (!url_fmt_append_n(b, esc, sizeof(esc))) return false; 1425 + }} 1426 + return true; 1427 + } 1428 + 1429 + static bool url_fmt_append_query_object(ant_t *js, url_fmt_buf_t *b, ant_value_t query) { 1430 + if (!is_special_object(query)) return true; 1431 + 1432 + bool first = true; 1433 + ant_iter_t it = js_prop_iter_begin(js, query); 1434 + 1435 + const char *key; 1436 + size_t key_len; 1437 + ant_value_t val; 1438 + 1439 + while (js_prop_iter_next(&it, &key, &key_len, &val)) { 1440 + if (!first && !url_fmt_append_c(b, '&')) { 1441 + js_prop_iter_end(&it); 1442 + return false; 1443 + } 1444 + first = false; 1445 + 1446 + if (!url_fmt_append_query_component(b, key, key_len)) { 1447 + js_prop_iter_end(&it); 1448 + return false; 1449 + } 1450 + 1451 + if (!url_fmt_append_c(b, '=')) { 1452 + js_prop_iter_end(&it); 1453 + return false; 1454 + } 1455 + 1456 + ant_value_t str_val = vtype(val) == T_STR ? val : js_tostring_val(js, val); 1457 + if (is_err(str_val)) { 1458 + js_prop_iter_end(&it); 1459 + return false; 1460 + } 1461 + 1462 + size_t val_len = 0; 1463 + const char *val_str = js_getstr(js, str_val, &val_len); 1464 + if (!val_str || !url_fmt_append_query_component(b, val_str, val_len)) { 1465 + js_prop_iter_end(&it); 1466 + return false; 1467 + } 1468 + } 1469 + 1470 + js_prop_iter_end(&it); 1471 + return true; 1472 + } 1473 + 1474 + static bool url_fmt_protocol_needs_slashes(const char *protocol, size_t len) { 1475 + if (len > 0 && protocol[len - 1] == ':') len--; 1476 + return 1477 + (len == 4 && memcmp(protocol, "http", 4) == 0) || 1478 + (len == 5 && memcmp(protocol, "https", 5) == 0) || 1479 + (len == 3 && memcmp(protocol, "ftp", 3) == 0) || 1480 + (len == 4 && memcmp(protocol, "file", 4) == 0) || 1481 + (len == 2 && memcmp(protocol, "ws", 2) == 0) || 1482 + (len == 3 && memcmp(protocol, "wss", 3) == 0); 1483 + } 1484 + 1485 + static ant_value_t builtin_url_format(ant_t *js, ant_value_t *args, int nargs) { 1486 + if (nargs < 1 || !is_object_type(args[0])) 1487 + return js_mkerr_typed(js, JS_ERR_TYPE, "url.format() requires a URL or object argument"); 1488 + 1489 + url_state_t *state = url_get_state(args[0]); 1490 + if (state) { 1491 + char *href = build_href(state); 1492 + ant_value_t ret = js_mkstr(js, href, strlen(href)); 1493 + free(href); 1494 + return ret; 1495 + } 1496 + 1497 + ant_value_t obj = args[0]; 1498 + ant_value_t tmp; 1499 + 1500 + const char *protocol = NULL, *auth = NULL, *host = NULL, *hostname = NULL; 1501 + const char *port = NULL, *pathname = NULL, *search = NULL, *hash = NULL; 1502 + 1503 + size_t protocol_len = 0, auth_len = 0, host_len = 0, hostname_len = 0; 1504 + size_t port_len = 0, pathname_len = 0, search_len = 0, hash_len = 0; 1505 + 1506 + url_fmt_get_string_prop(js, obj, "protocol", &tmp, &protocol, &protocol_len); 1507 + url_fmt_get_string_prop(js, obj, "auth", &tmp, &auth, &auth_len); 1508 + url_fmt_get_string_prop(js, obj, "host", &tmp, &host, &host_len); 1509 + url_fmt_get_string_prop(js, obj, "hostname", &tmp, &hostname, &hostname_len); 1510 + url_fmt_get_string_prop(js, obj, "port", &tmp, &port, &port_len); 1511 + url_fmt_get_string_prop(js, obj, "pathname", &tmp, &pathname, &pathname_len); 1512 + url_fmt_get_string_prop(js, obj, "search", &tmp, &search, &search_len); 1513 + url_fmt_get_string_prop(js, obj, "hash", &tmp, &hash, &hash_len); 1514 + 1515 + url_fmt_buf_t b = {0}; 1516 + 1517 + if (protocol && protocol_len > 0) { 1518 + if (!url_fmt_append_n(&b, protocol, protocol_len)) goto oom; 1519 + if (protocol[protocol_len - 1] != ':' && !url_fmt_append_c(&b, ':')) goto oom; 1520 + } 1521 + 1522 + bool has_host = (host && host_len > 0) || (hostname && hostname_len > 0); 1523 + ant_value_t slashes_val = js_get(js, obj, "slashes"); 1524 + 1525 + bool needs_slashes = 1526 + js_truthy(js, slashes_val) || 1527 + (protocol && url_fmt_protocol_needs_slashes(protocol, protocol_len)); 1528 + 1529 + if (needs_slashes && (has_host || (protocol && protocol_len >= 4 && memcmp(protocol, "file", 4) == 0))) { 1530 + if (!url_fmt_append(&b, "//")) goto oom; 1531 + } 1532 + 1533 + if (auth && auth_len > 0) { 1534 + if (!url_fmt_append_n(&b, auth, auth_len)) goto oom; 1535 + if (!url_fmt_append_c(&b, '@')) goto oom; 1536 + } 1537 + 1538 + if (host && host_len > 0) { 1539 + if (!url_fmt_append_n(&b, host, host_len)) goto oom; 1540 + } else if (hostname && hostname_len > 0) { 1541 + if (!url_fmt_append_n(&b, hostname, hostname_len)) goto oom; 1542 + if (port && port_len > 0) { 1543 + if (!url_fmt_append_c(&b, ':')) goto oom; 1544 + if (!url_fmt_append_n(&b, port, port_len)) goto oom; 1545 + }} 1546 + 1547 + if (pathname && pathname_len > 0) { 1548 + if (has_host && pathname[0] != '/' && !url_fmt_append_c(&b, '/')) goto oom; 1549 + if (!url_fmt_append_n(&b, pathname, pathname_len)) goto oom; 1550 + } 1551 + 1552 + if (search && search_len > 0) { 1553 + if (search[0] != '?' && !url_fmt_append_c(&b, '?')) goto oom; 1554 + if (!url_fmt_append_n(&b, search, search_len)) goto oom; 1555 + } else { 1556 + ant_value_t query = js_get(js, obj, "query"); 1557 + if (vtype(query) == T_STR) { 1558 + size_t qlen = 0; 1559 + const char *q = js_getstr(js, query, &qlen); 1560 + if (q && qlen > 0) { 1561 + if (!url_fmt_append_c(&b, '?')) goto oom; 1562 + if (!url_fmt_append_n(&b, q, qlen)) goto oom; 1563 + } 1564 + } else if (is_special_object(query)) { 1565 + url_fmt_buf_t qb = {0}; 1566 + if (!url_fmt_append_query_object(js, &qb, query)) { 1567 + free(qb.buf); 1568 + goto oom; 1569 + } 1570 + if (qb.len > 0) { 1571 + if (!url_fmt_append_c(&b, '?')) { 1572 + free(qb.buf); 1573 + goto oom; 1574 + } 1575 + if (!url_fmt_append_n(&b, qb.buf, qb.len)) { 1576 + free(qb.buf); 1577 + goto oom; 1578 + }} 1579 + free(qb.buf); 1580 + } 1581 + } 1582 + 1583 + if (hash && hash_len > 0) { 1584 + if (hash[0] != '#' && !url_fmt_append_c(&b, '#')) goto oom; 1585 + if (!url_fmt_append_n(&b, hash, hash_len)) goto oom; 1586 + } 1587 + 1588 + ant_value_t ret = js_mkstr(js, b.buf ? b.buf : "", b.len); 1589 + free(b.buf); 1590 + return ret; 1591 + 1592 + oom: 1593 + free(b.buf); 1594 + return js_mkerr(js, "allocation failure"); 1595 + } 1596 + 1342 1597 ant_value_t url_library(ant_t *js) { 1343 1598 ant_value_t lib = js_mkobj(js); 1344 1599 ant_value_t glob = js_glob(js); ··· 1347 1602 js_set(js, lib, "URLSearchParams",js_get(js, glob, "URLSearchParams")); 1348 1603 js_set(js, lib, "fileURLToPath", js_mkfun(builtin_fileURLToPath)); 1349 1604 js_set(js, lib, "pathToFileURL", js_mkfun(builtin_pathToFileURL)); 1605 + js_set(js, lib, "format", js_mkfun(builtin_url_format)); 1350 1606 js_set(js, lib, "default", lib); 1351 1607 1352 1608 return lib;
+302
src/modules/util.c
··· 34 34 const char *close; 35 35 } util_style_entry_t; 36 36 37 + typedef enum { 38 + UTIL_PARSE_ARG_TYPE_BOOLEAN = 0, 39 + UTIL_PARSE_ARG_TYPE_STRING, 40 + } util_parse_arg_type_t; 41 + 42 + typedef struct { 43 + char *name; 44 + char short_name; 45 + util_parse_arg_type_t type; 46 + bool multiple; 47 + ant_value_t default_value; 48 + } util_parse_arg_option_t; 49 + 37 50 // TODO: migrate to crprintf 38 51 static const util_style_entry_t util_styles[] = { 39 52 {"bold", "\x1b[1m", "\x1b[22m"}, ··· 111 124 size_t len = 0; 112 125 const char *s = js_getstr(js, json, &len); 113 126 return s ? util_sb_append_n(sb, s, len) : util_sb_append_n(sb, "[Circular]", 10); 127 + } 128 + 129 + static bool util_set_named_value( 130 + ant_t *js, 131 + ant_value_t values, 132 + const char *name, 133 + ant_value_t value, 134 + bool multiple 135 + ) { 136 + if (!multiple) { 137 + js_set(js, values, name, value); 138 + return !js->thrown_exists; 139 + } 140 + 141 + ant_value_t existing = js_get(js, values, name); 142 + if (is_err(existing)) return false; 143 + 144 + if (vtype(existing) == T_UNDEF) { 145 + ant_value_t arr = js_mkarr(js); 146 + js_arr_push(js, arr, value); 147 + js_set(js, values, name, arr); 148 + return !js->thrown_exists; 149 + } 150 + 151 + if (vtype(existing) == T_ARR) { 152 + js_arr_push(js, existing, value); 153 + return !js->thrown_exists; 154 + } 155 + 156 + ant_value_t arr = js_mkarr(js); 157 + js_arr_push(js, arr, existing); 158 + js_arr_push(js, arr, value); 159 + js_set(js, values, name, arr); 160 + return !js->thrown_exists; 161 + } 162 + 163 + static util_parse_arg_option_t *util_find_option_by_name( 164 + util_parse_arg_option_t *options, 165 + size_t option_count, 166 + const char *name 167 + ) { 168 + for (size_t i = 0; i < option_count; i++) { 169 + if (strcmp(options[i].name, name) == 0) return &options[i]; 170 + } 171 + return NULL; 172 + } 173 + 174 + static util_parse_arg_option_t *util_find_option_by_short( 175 + util_parse_arg_option_t *options, 176 + size_t option_count, 177 + char short_name 178 + ) { 179 + for (size_t i = 0; i < option_count; i++) { 180 + if (options[i].short_name == short_name) return &options[i]; 181 + } 182 + return NULL; 183 + } 184 + 185 + static void util_free_parse_options(util_parse_arg_option_t *options, size_t option_count) { 186 + if (!options) return; 187 + for (size_t i = 0; i < option_count; i++) free(options[i].name); 188 + free(options); 189 + } 190 + 191 + static ant_value_t util_parse_args(ant_t *js, ant_value_t *args, int nargs) { 192 + ant_value_t config = nargs > 0 ? args[0] : js_mkundef(); 193 + if (!is_object_type(config)) { 194 + return js_mkerr_typed(js, JS_ERR_TYPE, "parseArgs(config) requires an options object"); 195 + } 196 + 197 + ant_value_t args_list = js_get(js, config, "args"); 198 + if (is_err(args_list)) return args_list; 199 + if (vtype(args_list) == T_UNDEF) { 200 + ant_value_t process_obj = js_get(js, js_glob(js), "process"); 201 + if (is_err(process_obj)) return process_obj; 202 + args_list = js_get(js, process_obj, "argv"); 203 + if (is_err(args_list)) return args_list; 204 + } 205 + 206 + ant_value_t options_obj = js_get(js, config, "options"); 207 + if (is_err(options_obj)) return options_obj; 208 + if (!is_object_type(options_obj)) options_obj = js_mkobj(js); 209 + 210 + bool strict = js_truthy(js, js_get(js, config, "strict")); 211 + if (vtype(js_get(js, config, "strict")) == T_UNDEF) strict = true; 212 + bool allow_positionals = js_truthy(js, js_get(js, config, "allowPositionals")); 213 + 214 + size_t option_count = 0; 215 + { 216 + ant_iter_t iter = js_prop_iter_begin(js, options_obj); 217 + const char *key = NULL; 218 + size_t key_len = 0; 219 + while (js_prop_iter_next(&iter, &key, &key_len, NULL)) option_count++; 220 + js_prop_iter_end(&iter); 221 + } 222 + 223 + util_parse_arg_option_t *options = NULL; 224 + if (option_count > 0) { 225 + options = calloc(option_count, sizeof(*options)); 226 + if (!options) return js_mkerr(js, "Out of memory"); 227 + 228 + ant_iter_t iter = js_prop_iter_begin(js, options_obj); 229 + const char *key = NULL; 230 + size_t key_len = 0; 231 + size_t idx = 0; 232 + 233 + while (idx < option_count && js_prop_iter_next(&iter, &key, &key_len, NULL)) { 234 + ant_value_t spec = js_get(js, options_obj, key); 235 + ant_value_t type_val = is_object_type(spec) ? js_get(js, spec, "type") : js_mkundef(); 236 + ant_value_t short_val = is_object_type(spec) ? js_get(js, spec, "short") : js_mkundef(); 237 + ant_value_t multiple_val = is_object_type(spec) ? js_get(js, spec, "multiple") : js_mkundef(); 238 + ant_value_t default_val = is_object_type(spec) ? js_get(js, spec, "default") : js_mkundef(); 239 + 240 + options[idx].name = strndup(key, key_len); 241 + if (!options[idx].name) { 242 + js_prop_iter_end(&iter); 243 + util_free_parse_options(options, option_count); 244 + return js_mkerr(js, "Out of memory"); 245 + } 246 + 247 + options[idx].type = UTIL_PARSE_ARG_TYPE_BOOLEAN; 248 + if (vtype(type_val) == T_STR) { 249 + size_t type_len = 0; 250 + const char *type_str = js_getstr(js, type_val, &type_len); 251 + if (type_str && type_len == 6 && memcmp(type_str, "string", 6) == 0) { 252 + options[idx].type = UTIL_PARSE_ARG_TYPE_STRING; 253 + } 254 + } 255 + 256 + if (vtype(short_val) == T_STR) { 257 + size_t short_len = 0; 258 + const char *short_str = js_getstr(js, short_val, &short_len); 259 + if (short_str && short_len > 0) options[idx].short_name = short_str[0]; 260 + } 261 + 262 + options[idx].multiple = js_truthy(js, multiple_val); 263 + options[idx].default_value = default_val; 264 + idx++; 265 + } 266 + 267 + js_prop_iter_end(&iter); 268 + } 269 + 270 + ant_value_t values = js_mkobj(js); 271 + ant_value_t positionals = js_mkarr(js); 272 + ant_value_t out = js_mkobj(js); 273 + 274 + for (size_t i = 0; i < option_count; i++) { 275 + if (vtype(options[i].default_value) != T_UNDEF) { 276 + if (!util_set_named_value(js, values, options[i].name, options[i].default_value, options[i].multiple)) { 277 + util_free_parse_options(options, option_count); 278 + return js->thrown_exists ? js->thrown_value : js_mkerr(js, "parseArgs failed to set default"); 279 + }}} 280 + 281 + ant_offset_t arg_len = js_arr_len(js, args_list); 282 + bool stop_parsing = false; 283 + 284 + for (ant_offset_t i = 0; i < arg_len; i++) { 285 + ant_value_t arg_val = js_arr_get(js, args_list, i); 286 + if (vtype(arg_val) != T_STR) continue; 287 + 288 + size_t arg_slen = 0; 289 + const char *arg = js_getstr(js, arg_val, &arg_slen); 290 + if (!arg) continue; 291 + 292 + if (stop_parsing) { 293 + js_arr_push(js, positionals, arg_val); 294 + continue; 295 + } 296 + 297 + if (arg_slen == 2 && memcmp(arg, "--", 2) == 0) { 298 + stop_parsing = true; 299 + continue; 300 + } 301 + 302 + if (arg_slen > 2 && arg[0] == '-' && arg[1] == '-') { 303 + const char *name = arg + 2; 304 + size_t name_len = arg_slen - 2; 305 + const char *inline_value = NULL; 306 + size_t inline_len = 0; 307 + const char *eq = memchr(name, '=', name_len); 308 + 309 + if (eq) { 310 + name_len = (size_t)(eq - name); 311 + inline_value = eq + 1; 312 + inline_len = (size_t)(arg + arg_slen - inline_value); 313 + } 314 + 315 + char *name_buf = strndup(name, name_len); 316 + if (!name_buf) { 317 + util_free_parse_options(options, option_count); 318 + return js_mkerr(js, "Out of memory"); 319 + } 320 + 321 + util_parse_arg_option_t *opt = util_find_option_by_name(options, option_count, name_buf); 322 + if (!opt) { 323 + if (strict) { 324 + free(name_buf); 325 + util_free_parse_options(options, option_count); 326 + return js_mkerr_typed(js, JS_ERR_TYPE, "Unknown option '--%.*s'", (int)name_len, name); 327 + } 328 + 329 + ant_value_t unknown = inline_value 330 + ? js_mkstr(js, inline_value, inline_len) 331 + : js_true; 332 + 333 + js_set(js, values, name_buf, unknown); 334 + free(name_buf); 335 + 336 + continue; 337 + } 338 + 339 + ant_value_t parsed_value = js_true; 340 + if (opt->type == UTIL_PARSE_ARG_TYPE_STRING) { 341 + if (inline_value) parsed_value = js_mkstr(js, inline_value, inline_len); 342 + else if (i + 1 < arg_len) parsed_value = js_arr_get(js, args_list, ++i); 343 + else { 344 + free(name_buf); 345 + util_free_parse_options(options, option_count); 346 + return js_mkerr_typed(js, JS_ERR_TYPE, "Option '--%s' requires a value", opt->name); 347 + } 348 + } else if (inline_value) parsed_value = js_mkstr(js, inline_value, inline_len); 349 + 350 + if (!util_set_named_value(js, values, opt->name, parsed_value, opt->multiple)) { 351 + free(name_buf); 352 + util_free_parse_options(options, option_count); 353 + return js->thrown_exists ? js->thrown_value : js_mkerr(js, "parseArgs failed"); 354 + } 355 + 356 + free(name_buf); 357 + continue; 358 + } 359 + 360 + if (arg_slen > 1 && arg[0] == '-') { 361 + for (size_t j = 1; j < arg_slen; j++) { 362 + util_parse_arg_option_t *opt = util_find_option_by_short(options, option_count, arg[j]); 363 + if (!opt) { 364 + if (strict) { 365 + util_free_parse_options(options, option_count); 366 + return js_mkerr_typed(js, JS_ERR_TYPE, "Unknown option '-%c'", arg[j]); 367 + } 368 + char key[2] = {arg[j], '\0'}; 369 + js_set(js, values, key, js_true); 370 + continue; 371 + } 372 + 373 + ant_value_t parsed_value = js_true; 374 + if (opt->type == UTIL_PARSE_ARG_TYPE_STRING) { 375 + if (j + 1 < arg_slen) { 376 + parsed_value = js_mkstr(js, arg + j + 1, arg_slen - (j + 1)); 377 + j = arg_slen; 378 + } else if (i + 1 < arg_len) { 379 + parsed_value = js_arr_get(js, args_list, ++i); 380 + j = arg_slen; 381 + } else { 382 + util_free_parse_options(options, option_count); 383 + return js_mkerr_typed(js, JS_ERR_TYPE, "Option '-%c' requires a value", arg[j]); 384 + } 385 + } 386 + 387 + if (!util_set_named_value(js, values, opt->name, parsed_value, opt->multiple)) { 388 + util_free_parse_options(options, option_count); 389 + return js->thrown_exists ? js->thrown_value : js_mkerr(js, "parseArgs failed"); 390 + } 391 + 392 + if (opt->type == UTIL_PARSE_ARG_TYPE_STRING) break; 393 + } 394 + continue; 395 + } 396 + 397 + if (!allow_positionals) { 398 + util_free_parse_options(options, option_count); 399 + return js_mkerr_typed(js, JS_ERR_TYPE, "Unexpected positional argument '%s'", arg); 400 + } 401 + js_arr_push(js, positionals, arg_val); 402 + } 403 + 404 + util_free_parse_options(options, option_count); 405 + js_set(js, out, "values", values); 406 + js_set(js, out, "positionals", positionals); 407 + 408 + return out; 114 409 } 115 410 116 411 static ant_value_t util_format_impl(ant_t *js, ant_value_t *args, int nargs, int fmt_index) { ··· 1024 1319 return js_mkundef(); 1025 1320 } 1026 1321 1322 + static ant_value_t util_is_deep_strict_equal(ant_t *js, ant_value_t *args, int nargs) { 1323 + if (nargs < 2) return js_bool(true); 1324 + return js_bool(js_deep_equal(js, args[0], args[1], true)); 1325 + } 1326 + 1027 1327 ant_value_t util_library(ant_t *js) { 1028 1328 ant_value_t lib = js_mkobj(js); 1029 1329 ant_value_t types = util_get_types_object(js); ··· 1034 1334 js_set(js, lib, "inspect", js_mkfun(util_inspect)); 1035 1335 js_set(js, lib, "deprecate", js_mkfun(util_deprecate)); 1036 1336 js_set(js, lib, "inherits", js_mkfun(util_inherits)); 1337 + js_set(js, lib, "isDeepStrictEqual", js_mkfun(util_is_deep_strict_equal)); 1338 + js_set(js, lib, "parseArgs", js_mkfun(util_parse_args)); 1037 1339 js_set(js, lib, "parseEnv", js_mkfun(util_parse_env)); 1038 1340 js_set(js, lib, "promisify", js_mkfun(util_promisify)); 1039 1341 js_set(js, lib, "callbackify", js_mkfun(util_callbackify));
+22 -17
src/pkg/resolver.zig
··· 769 769 self.http.initiateTarballConnectionsAsync(); 770 770 771 771 const ConstraintInfo = struct { 772 + package_name: []const u8, 772 773 constraint: Constraint, 773 774 constraint_str: []const u8, 774 775 requester: []const u8, ··· 780 781 var iter = all_constraints.iterator(); 781 782 while (iter.next()) |entry| { 782 783 for (entry.value_ptr.items) |info| { 784 + self.allocator.free(info.package_name); 783 785 self.allocator.free(info.constraint_str); 784 786 self.allocator.free(info.requester); 785 787 } ··· 955 957 956 958 const spec = dependencySpec(item.name, item.constraint_str); 957 959 const constraint = Constraint.parse(spec.constraint) catch continue; 958 - const gop = try all_constraints.getOrPut(spec.package_name); 960 + const gop = try all_constraints.getOrPut(spec.install_name); 959 961 if (!gop.found_existing) { 960 - gop.key_ptr.* = try self.allocator.dupe(u8, spec.package_name); 962 + gop.key_ptr.* = try self.allocator.dupe(u8, spec.install_name); 961 963 gop.value_ptr.* = .{}; 962 964 } 963 965 try gop.value_ptr.append(self.allocator, .{ 966 + .package_name = try self.allocator.dupe(u8, spec.package_name), 964 967 .constraint = constraint, 965 968 .constraint_str = try self.allocator.dupe(u8, spec.constraint), 966 969 .requester = try self.allocator.dupe(u8, item.requester), ··· 1024 1027 1025 1028 var pkg_iter = all_constraints.iterator(); 1026 1029 while (pkg_iter.next()) |entry| { 1027 - const pkg_name = entry.key_ptr.*; 1030 + const install_name = entry.key_ptr.*; 1028 1031 const constraint_list = entry.value_ptr.items; 1032 + if (constraint_list.len == 0) continue; 1029 1033 1030 - if (self.metadata_cache.get(pkg_name)) |metadata| { 1034 + const package_name = constraint_list[0].package_name; 1035 + if (self.metadata_cache.get(package_name)) |metadata| { 1031 1036 var plain_constraints = try self.allocator.alloc(Constraint, constraint_list.len); 1032 1037 defer self.allocator.free(plain_constraints); 1033 1038 for (constraint_list, 0..) |info, i| { ··· 1035 1040 } 1036 1041 1037 1042 if (self.selectBestVersionForConstraints(&metadata, plain_constraints)) |best| { 1038 - try optimal_versions.put(pkg_name, best); 1043 + try optimal_versions.put(install_name, best); 1039 1044 } else { 1040 1045 const best = self.selectVersionSatisfyingMost(&metadata, constraint_list); 1041 1046 if (best) |b| { 1042 1047 if (b.version.prerelease) |pre| { 1043 1048 debug.log(" {s}: optimal={d}.{d}.{d}-{s} (satisfies {d}/{d} constraints)", .{ 1044 - pkg_name, b.version.major, b.version.minor, b.version.patch, pre, 1049 + install_name, b.version.major, b.version.minor, b.version.patch, pre, 1045 1050 self.countSatisfied(&metadata, b, constraint_list), constraint_list.len, 1046 1051 }); 1047 1052 } else { 1048 1053 debug.log(" {s}: optimal={d}.{d}.{d} (satisfies {d}/{d} constraints)", .{ 1049 - pkg_name, b.version.major, b.version.minor, b.version.patch, 1054 + install_name, b.version.major, b.version.minor, b.version.patch, 1050 1055 self.countSatisfied(&metadata, b, constraint_list), constraint_list.len, 1051 1056 }); 1052 1057 } 1053 - try optimal_versions.put(pkg_name, b); 1058 + try optimal_versions.put(install_name, b); 1054 1059 } 1055 1060 } 1056 1061 } ··· 1245 1250 1246 1251 var metadata = try self.fetchMetadata(dep_spec.package_name); 1247 1252 const version_info = blk: { 1248 - if (optimal_versions.get(dep_spec.package_name)) |optimal| { 1253 + if (optimal_versions.get(dep_spec.install_name)) |optimal| { 1249 1254 if (constraint.satisfies(optimal.version) and optimal.matchesPlatform()) break :blk optimal; 1250 1255 } 1251 1256 break :blk self.selectBestVersion(&metadata, constraint) orelse return error.NoMatchingVersion; ··· 1253 1258 1254 1259 if (!version_info.matchesPlatform()) return error.PlatformMismatch; 1255 1260 1256 - const cons_gop = try self.constraints.getOrPut(dep_spec.package_name); 1261 + const cons_gop = try self.constraints.getOrPut(dep_spec.install_name); 1257 1262 if (!cons_gop.found_existing) { 1258 - cons_gop.key_ptr.* = try self.allocator.dupe(u8, dep_spec.package_name); 1263 + cons_gop.key_ptr.* = try self.allocator.dupe(u8, dep_spec.install_name); 1259 1264 cons_gop.value_ptr.* = .{}; 1260 1265 } 1261 1266 try cons_gop.value_ptr.append(self.allocator, constraint); ··· 1332 1337 return existing_pkg; 1333 1338 } 1334 1339 1335 - const cons_gop = try self.constraints.getOrPut(dep_spec.package_name); 1340 + const cons_gop = try self.constraints.getOrPut(dep_spec.install_name); 1336 1341 if (!cons_gop.found_existing) { 1337 - cons_gop.key_ptr.* = try self.allocator.dupe(u8, dep_spec.package_name); 1342 + cons_gop.key_ptr.* = try self.allocator.dupe(u8, dep_spec.install_name); 1338 1343 cons_gop.value_ptr.* = .{}; 1339 1344 } 1340 1345 try cons_gop.value_ptr.append(self.allocator, constraint); ··· 1400 1405 } 1401 1406 } 1402 1407 1403 - const cons_gop = try self.constraints.getOrPut(dep_spec.package_name); 1408 + const cons_gop = try self.constraints.getOrPut(dep_spec.install_name); 1404 1409 if (!cons_gop.found_existing) { 1405 - cons_gop.key_ptr.* = try self.allocator.dupe(u8, dep_spec.package_name); 1410 + cons_gop.key_ptr.* = try self.allocator.dupe(u8, dep_spec.install_name); 1406 1411 cons_gop.value_ptr.* = .{}; 1407 1412 } 1408 1413 try cons_gop.value_ptr.append(self.allocator, constraint); ··· 1563 1568 1564 1569 const dep_spec = dependencySpec(name, constraint_str); 1565 1570 const constraint = try Constraint.parse(dep_spec.constraint); 1566 - const cons_gop = try self.constraints.getOrPut(dep_spec.package_name); 1571 + const cons_gop = try self.constraints.getOrPut(dep_spec.install_name); 1567 1572 1568 1573 if (!cons_gop.found_existing) { 1569 - cons_gop.key_ptr.* = try self.allocator.dupe(u8, dep_spec.package_name); 1574 + cons_gop.key_ptr.* = try self.allocator.dupe(u8, dep_spec.install_name); 1570 1575 cons_gop.value_ptr.* = .{}; 1571 1576 } 1572 1577 try cons_gop.value_ptr.append(self.allocator, constraint);
+1 -1
src/repl.c
··· 425 425 static cmd_result_t cmd_stats(ant_t *js, history_t *history, const char *arg) { 426 426 ant_value_t stats_fn = js_get(js, rt->ant_obj, "stats"); 427 427 ant_value_t result = sv_vm_call(js->vm, js, stats_fn, js_mkundef(), NULL, 0, NULL, false); 428 - console_print(js, &result, 1, NULL, stdout); 428 + console_emit(js, false, NULL, &result, 1); 429 429 return CMD_OK; 430 430 } 431 431
+30 -2
src/runtime.c
··· 36 36 static code_block_t *code_arena_current = NULL; 37 37 static intern_entry_t *code_interns = NULL; 38 38 39 + static void code_interns_prune_for_block_range( 40 + const code_block_t *block, 41 + size_t start_offset 42 + ) { 43 + if (!block) return; 44 + 45 + const char *start = block->data + start_offset; 46 + const char *end = block->data + block->capacity; 47 + intern_entry_t *entry = NULL; 48 + intern_entry_t *tmp = NULL; 49 + 50 + HASH_ITER(hh, code_interns, entry, tmp) 51 + if (entry->ptr >= start && entry->ptr < end) { 52 + HASH_DEL(code_interns, entry); 53 + free(entry); 54 + } 55 + } 56 + 57 + static void code_interns_prune_for_blocks(code_block_t *first) { 58 + for ( 59 + code_block_t *block = first; 60 + block; block = block->next 61 + ) code_interns_prune_for_block_range(block, 0); 62 + } 63 + 39 64 static code_block_t *code_arena_new_block(size_t min_size) { 40 65 size_t capacity = CODE_ARENA_BLOCK_SIZE; 41 66 if (min_size > capacity) capacity = min_size; ··· 60 85 if (!code_arena_current || code_arena_current->used + alloc_size > code_arena_current->capacity) { 61 86 code_block_t *new_block = code_arena_new_block(alloc_size); 62 87 if (!new_block) return NULL; 63 - 64 88 if (!code_arena_head) code_arena_head = new_block; 65 89 else if (code_arena_current) code_arena_current->next = new_block; 66 - 67 90 code_arena_current = new_block; 68 91 } 69 92 ··· 114 137 code_block_t *target = (code_block_t *)mark.block; 115 138 116 139 if (!target) { 140 + code_interns_prune_for_blocks(code_arena_head); 117 141 code_block_t *block = code_arena_head; 118 142 119 143 while (block) { ··· 126 150 127 151 return; 128 152 } 153 + 154 + size_t clamped_used = mark.used <= target->capacity ? mark.used : target->capacity; 155 + code_interns_prune_for_block_range(target, clamped_used); 156 + code_interns_prune_for_blocks(target->next); 129 157 130 158 if (mark.used <= target->capacity) target->used = mark.used; 131 159 code_block_t *b = target->next;
+24 -3
src/silver/ast.c
··· 3 3 #include "silver/directives.h" 4 4 5 5 #include "escape.h" 6 + #include "debug.h" 6 7 #include "errors.h" 7 8 #include "internal.h" 8 9 #include "tokens.h" 9 10 10 11 #include <runtime.h> 12 + #include <stdio.h> 11 13 #include <stdlib.h> 12 14 #include <string.h> 13 15 ··· 1293 1295 fn->flags |= FN_GENERATOR; 1294 1296 } 1295 1297 1296 - if (NEXT() == TOK_IDENTIFIER) { 1298 + if (is_ident_like_tok(NEXT())) { 1297 1299 fn->str = tok_ident_str(p, &fn->len); 1298 1300 sv_strict_check_binding_ident(p, fn->str, fn->len); 1299 1301 CONSUME(); ··· 1334 1336 static sv_ast_t *parse_class(P) { 1335 1337 sv_ast_t *cls = mk(N_CLASS); 1336 1338 1337 - if (NEXT() == TOK_IDENTIFIER && 1339 + if (is_ident_like_tok(NEXT()) && 1338 1340 !(TLEN == 7 && memcmp(tok_str(p), "extends", 7) == 0)) { 1339 1341 cls->str = tok_ident_str(p, &cls->len); 1340 1342 CONSUME(); ··· 2107 2109 } 2108 2110 2109 2111 sv_ast_t *sv_parse(ant_t *js, const char *code, ant_offset_t clen, bool strict){ 2112 + if (sv_parse_trace_unlikely) { 2113 + fprintf(stderr, "[parse] start len=%u strict=%d\n", (unsigned)clen, strict ? 1 : 0); 2114 + } 2115 + 2110 2116 sv_parser_t parser = { .js = js }; 2111 2117 sv_parser_t *p = &parser; 2112 2118 sv_lexer_init(&p->lx, js, code, clen, strict); 2113 2119 2114 2120 sv_ast_t *program = mk(N_PROGRAM); 2115 2121 sv_parse_stmt_list(p, &program->args, false, true); 2116 - if (js->thrown_exists) return NULL; 2122 + if (sv_parse_trace_unlikely) fprintf( 2123 + stderr, "[parse] after-stmt-list thrown=%d strict=%d body=%d\n", 2124 + js->thrown_exists ? 1 : 0, 2125 + p->lx.strict ? 1 : 0, 2126 + program ? program->args.count : -1 2127 + ); 2128 + 2129 + if (js->thrown_exists) { 2130 + if (sv_parse_trace_unlikely) fprintf(stderr, "[parse] return null\n"); 2131 + return NULL; 2132 + } 2133 + 2117 2134 if (p->lx.strict) program->flags |= FN_PARSE_STRICT; 2135 + if (sv_parse_trace_unlikely) fprintf(stderr, 2136 + "[parse] return program strict=%d body=%d\n", 2137 + p->lx.strict ? 1 : 0, program->args.count 2138 + ); 2118 2139 2119 2140 return program; 2120 2141 }
+186
src/silver/compile_ctx.c
··· 1 + #include "silver/compiler.h" 2 + #include "silver/compile_ctx.h" 3 + 4 + #include <stdlib.h> 5 + #include <string.h> 6 + 7 + static void sv_compile_ctx_rebuild_local_lookup(sv_compiler_t *ctx, int cap) { 8 + if (cap < 16) cap = 16; 9 + int *heads = malloc((size_t)cap * sizeof(int)); 10 + if (!heads) return; 11 + 12 + for (int i = 0; i < cap; i++) heads[i] = -1; 13 + for (int i = 0; i < ctx->local_count; i++) { 14 + sv_local_t *loc = &ctx->locals[i]; 15 + int bucket = (int)(loc->name_hash & (uint32_t)(cap - 1)); 16 + loc->lookup_next = heads[bucket]; 17 + heads[bucket] = i; 18 + } 19 + 20 + free(ctx->local_lookup_heads); 21 + ctx->local_lookup_heads = heads; 22 + ctx->local_lookup_cap = cap; 23 + } 24 + 25 + void sv_compile_ctx_init_root( 26 + sv_compiler_t *ctx, ant_t *js, 27 + const char *filename, 28 + const char *source, 29 + ant_offset_t source_len, 30 + sv_compile_mode_t mode, 31 + bool is_strict, 32 + sv_line_table_t *line_table 33 + ) { 34 + memset(ctx, 0, sizeof(*ctx)); 35 + ctx->js = js; 36 + ctx->filename = filename; 37 + ctx->source = source; 38 + ctx->source_len = source_len; 39 + ctx->mode = mode; 40 + ctx->is_strict = is_strict; 41 + ctx->strict_args_local = -1; 42 + ctx->new_target_local = -1; 43 + ctx->super_local = -1; 44 + ctx->line_table = line_table; 45 + } 46 + 47 + void sv_compile_ctx_init_child( 48 + sv_compiler_t *ctx, 49 + sv_compiler_t *enclosing, 50 + sv_ast_t *node, 51 + sv_compile_mode_t mode 52 + ) { 53 + memset(ctx, 0, sizeof(*ctx)); 54 + ctx->js = enclosing->js; 55 + ctx->filename = enclosing->filename; 56 + ctx->source = enclosing->source; 57 + ctx->source_len = enclosing->source_len; 58 + ctx->line_table = enclosing->line_table; 59 + ctx->enclosing = enclosing; 60 + ctx->scope_depth = 0; 61 + ctx->is_arrow = node && !!(node->flags & FN_ARROW); 62 + ctx->is_async = node && !!(node->flags & FN_ASYNC); 63 + ctx->is_strict = enclosing->is_strict; 64 + ctx->mode = mode; 65 + ctx->strict_args_local = -1; 66 + ctx->new_target_local = -1; 67 + ctx->super_local = -1; 68 + ctx->param_count = node ? node->args.count : 0; 69 + } 70 + 71 + void sv_compile_ctx_cleanup(sv_compiler_t *ctx) { 72 + free(ctx->code); 73 + free(ctx->constants); 74 + free(ctx->atoms); 75 + free(ctx->locals); 76 + free(ctx->local_lookup_heads); 77 + free(ctx->upval_descs); 78 + free(ctx->loops); 79 + free(ctx->srcpos); 80 + free(ctx->slot_types); 81 + free(ctx->deferred_exports); 82 + 83 + const_dedup_entry_t *entry; 84 + const_dedup_entry_t *tmp; 85 + HASH_ITER(hh, ctx->const_dedup, entry, tmp) { 86 + HASH_DEL(ctx->const_dedup, entry); 87 + free(entry); 88 + } 89 + } 90 + 91 + sv_line_table_t *sv_compile_ctx_build_line_table(const char *source, ant_offset_t source_len) { 92 + if (!source || source_len <= 0) return NULL; 93 + 94 + sv_line_table_t *lt = malloc(sizeof(sv_line_table_t)); 95 + if (!lt) return NULL; 96 + 97 + int cap = (int)(source_len / 32) + 64; 98 + lt->offsets = malloc((size_t)cap * sizeof(uint32_t)); 99 + lt->count = 0; 100 + lt->offsets[lt->count++] = 0; 101 + 102 + for (ant_offset_t i = 0; i < source_len; i++) { 103 + if (source[i] == '\n') { 104 + if (lt->count >= cap) { 105 + cap *= 2; 106 + lt->offsets = realloc(lt->offsets, (size_t)cap * sizeof(uint32_t)); 107 + } 108 + lt->offsets[lt->count++] = (uint32_t)(i + 1); 109 + }} 110 + 111 + return lt; 112 + } 113 + 114 + void sv_compile_ctx_free_line_table(sv_line_table_t *lt) { 115 + if (!lt) return; 116 + free(lt->offsets); 117 + free(lt); 118 + } 119 + 120 + void sv_compile_ctx_line_table_lookup( 121 + sv_line_table_t *lt, 122 + uint32_t off, 123 + uint32_t *out_line, 124 + uint32_t *out_col 125 + ) { 126 + int lo = 0; 127 + int hi = lt->count - 1; 128 + 129 + while (lo < hi) { 130 + int mid = lo + (hi - lo + 1) / 2; 131 + if (lt->offsets[mid] <= off) lo = mid; 132 + else hi = mid - 1; 133 + } 134 + 135 + *out_line = (uint32_t)(lo + 1); 136 + *out_col = off - lt->offsets[lo] + 1; 137 + } 138 + 139 + uint32_t sv_compile_ctx_hash_local_name(const char *name, uint32_t len) { 140 + uint32_t hash = 2166136261u; 141 + for (uint32_t i = 0; i < len; i++) { 142 + hash ^= (uint8_t)name[i]; 143 + hash *= 16777619u; 144 + } 145 + return hash; 146 + } 147 + 148 + void sv_compile_ctx_ensure_local_lookup_capacity(sv_compiler_t *ctx, int next_count) { 149 + int needed = 16; 150 + while (needed < next_count * 2) needed <<= 1; 151 + if (needed <= ctx->local_lookup_cap) return; 152 + sv_compile_ctx_rebuild_local_lookup(ctx, needed); 153 + } 154 + 155 + void sv_compile_ctx_local_lookup_insert(sv_compiler_t *ctx, int idx) { 156 + if (idx < 0 || idx >= ctx->local_count) return; 157 + if (!ctx->local_lookup_heads || ctx->local_lookup_cap <= 0) return; 158 + 159 + sv_local_t *loc = &ctx->locals[idx]; 160 + int bucket = (int)(loc->name_hash & (uint32_t)(ctx->local_lookup_cap - 1)); 161 + loc->lookup_next = ctx->local_lookup_heads[bucket]; 162 + ctx->local_lookup_heads[bucket] = idx; 163 + } 164 + 165 + void sv_compile_ctx_local_lookup_remove(sv_compiler_t *ctx, int idx) { 166 + if ( 167 + !ctx->local_lookup_heads || ctx->local_lookup_cap <= 0 || 168 + idx < 0 || idx >= ctx->local_count 169 + ) return; 170 + 171 + sv_local_t *loc = &ctx->locals[idx]; 172 + int bucket = (int)(loc->name_hash & (uint32_t)(ctx->local_lookup_cap - 1)); 173 + int cur = ctx->local_lookup_heads[bucket]; 174 + int prev = -1; 175 + 176 + while (cur != -1) { 177 + if (cur == idx) { 178 + int next = ctx->locals[cur].lookup_next; 179 + if (prev == -1) ctx->local_lookup_heads[bucket] = next; 180 + else ctx->locals[prev].lookup_next = next; 181 + break; 182 + } 183 + prev = cur; 184 + cur = ctx->locals[cur].lookup_next; 185 + } 186 + }
+194 -373
src/silver/compiler.c
··· 1 1 #include "silver/ast.h" 2 + #include "silver/compile_ctx.h" 2 3 #include "silver/engine.h" 3 4 #include "silver/compiler.h" 4 5 #include "silver/directives.h" 5 6 6 7 #include "internal.h" 8 + #include "debug.h" 7 9 #include "tokens.h" 8 10 #include "runtime.h" 9 11 #include "ops/coercion.h" ··· 18 20 SV_ITER_HINT_STRING = 4, 19 21 }; 20 22 21 - typedef struct { 22 - const char *name; 23 - uint32_t name_len; 24 - int depth; 25 - bool is_const; 26 - bool captured; 27 - bool is_tdz; 28 - uint8_t inferred_type; 29 - } sv_local_t; 30 - 31 - typedef struct { 32 - int *offsets; 33 - int count; 34 - int cap; 35 - } sv_patch_list_t; 36 - 37 - typedef struct { 38 - int loop_start; 39 - sv_patch_list_t breaks; 40 - sv_patch_list_t continues; 41 - int scope_depth; 42 - const char *label; 43 - uint32_t label_len; 44 - bool is_switch; 45 - } sv_loop_t; 46 - 47 - typedef struct { 48 - const char *name; 49 - uint32_t len; 50 - } sv_deferred_export_t; 51 - 52 - typedef struct sv_compiler { 53 - ant_t *js; 54 - const char *filename; 55 - const char *source; 56 - ant_offset_t source_len; 57 - 58 - uint8_t *code; 59 - int code_len; 60 - int code_cap; 61 - 62 - ant_value_t *constants; 63 - int const_count; 64 - int const_cap; 65 - 66 - sv_atom_t *atoms; 67 - int atom_count; 68 - int atom_cap; 69 - int ic_count; 70 - 71 - sv_local_t *locals; 72 - int local_count; 73 - int local_cap; 74 - int max_local_count; 75 - int scope_depth; 76 - int param_locals; 77 - 78 - sv_upval_desc_t *upval_descs; 79 - int upvalue_count; 80 - int upvalue_cap; 81 - 82 - sv_loop_t *loops; 83 - int loop_count; 84 - int loop_cap; 85 - 86 - struct sv_compiler *enclosing; 87 - sv_ast_t **field_inits; 88 - 89 - int field_init_count; 90 - int *computed_key_locals; 91 - int param_count; 92 - 93 - bool is_arrow; 94 - bool is_async; 95 - bool is_strict; 96 - sv_compile_mode_t mode; 97 - 98 - bool is_tla; 99 - int try_depth; 100 - int with_depth; 101 - int strict_args_local; 102 - int new_target_local; 103 - int super_local; 104 - 105 - const char *pending_label; 106 - uint32_t pending_label_len; 107 - 108 - const char *inferred_name; 109 - uint32_t inferred_name_len; 110 - 111 - sv_srcpos_t *srcpos; 112 - int srcpos_count; 113 - int srcpos_cap; 114 - uint32_t last_srcpos_off; 115 - uint32_t last_srcpos_end; 116 - 117 - sv_type_info_t *slot_types; 118 - int slot_type_cap; 119 - 120 - sv_deferred_export_t *deferred_exports; 121 - int deferred_export_count; 122 - int deferred_export_cap; 123 - 124 - struct const_dedup_entry *const_dedup; 125 - struct sv_line_table *line_table; 126 - } sv_compiler_t; 127 - 128 - typedef struct const_dedup_entry { 129 - const char *str; 130 - size_t len; 131 - int index; 132 - UT_hash_handle hh; 133 - } const_dedup_entry_t; 134 - 135 - typedef struct sv_line_table { 136 - uint32_t *offsets; 137 - int count; 138 - } sv_line_table_t; 139 - 140 - static sv_line_table_t *build_line_table(const char *source, ant_offset_t source_len) { 141 - if (!source || source_len <= 0) return NULL; 142 - sv_line_table_t *lt = malloc(sizeof(sv_line_table_t)); 143 - if (!lt) return NULL; 144 - 145 - int cap = (int)(source_len / 32) + 64; 146 - lt->offsets = malloc((size_t)cap * sizeof(uint32_t)); 147 - lt->count = 0; 148 - lt->offsets[lt->count++] = 0; 149 - 150 - for (ant_offset_t i = 0; i < source_len; i++) { 151 - if (source[i] == '\n') { 152 - if (lt->count >= cap) { 153 - cap *= 2; 154 - lt->offsets = realloc(lt->offsets, (size_t)cap * sizeof(uint32_t)); 155 - } 156 - lt->offsets[lt->count++] = (uint32_t)(i + 1); 157 - } 158 - } 159 - return lt; 160 - } 161 - 162 - static void free_line_table(sv_line_table_t *lt) { 163 - if (!lt) return; 164 - free(lt->offsets); 165 - free(lt); 166 - } 167 - 168 - static void line_table_lookup(sv_line_table_t *lt, uint32_t off, uint32_t *out_line, uint32_t *out_col) { 169 - int lo = 0, hi = lt->count - 1; 170 - while (lo < hi) { 171 - int mid = lo + (hi - lo + 1) / 2; 172 - if (lt->offsets[mid] <= off) lo = mid; 173 - else hi = mid - 1; 174 - } 175 - *out_line = (uint32_t)(lo + 1); 176 - *out_col = off - lt->offsets[lo] + 1; 177 - } 178 - 179 - static sv_func_t *compile_function_body( 180 - sv_compiler_t *enclosing, 181 - sv_ast_t *node, sv_compile_mode_t mode 182 - ); 183 - 184 - static void compile_expr(sv_compiler_t *c, sv_ast_t *node); 185 - static void compile_stmt(sv_compiler_t *c, sv_ast_t *node); 186 - static void compile_stmts(sv_compiler_t *c, sv_ast_list_t *list); 187 - static void compile_binary(sv_compiler_t *c, sv_ast_t *node); 188 - static void compile_unary(sv_compiler_t *c, sv_ast_t *node); 189 - static void compile_update(sv_compiler_t *c, sv_ast_t *node); 190 - static void compile_assign(sv_compiler_t *c, sv_ast_t *node); 191 - static void compile_lhs_set(sv_compiler_t *c, sv_ast_t *target, bool keep); 192 - static void compile_ternary(sv_compiler_t *c, sv_ast_t *node); 193 - static void compile_tail_return_expr(sv_compiler_t *c, sv_ast_t *expr); 194 - static void compile_typeof(sv_compiler_t *c, sv_ast_t *node); 195 - static void compile_delete(sv_compiler_t *c, sv_ast_t *node); 196 - static void compile_template(sv_compiler_t *c, sv_ast_t *node); 197 - static void compile_call(sv_compiler_t *c, sv_ast_t *node); 198 - static void compile_new(sv_compiler_t *c, sv_ast_t *node); 199 - static void compile_member(sv_compiler_t *c, sv_ast_t *node); 200 - static void compile_optional(sv_compiler_t *c, sv_ast_t *node); 201 - static void compile_optional_get(sv_compiler_t *c, sv_ast_t *node); 202 - static void compile_array(sv_compiler_t *c, sv_ast_t *node); 203 - static void compile_object(sv_compiler_t *c, sv_ast_t *node); 204 - static void compile_func_expr(sv_compiler_t *c, sv_ast_t *node); 205 - static void compile_array_destructure(sv_compiler_t *c, sv_ast_t *pat, bool keep); 206 - static void compile_object_destructure(sv_compiler_t *c, sv_ast_t *pat, bool keep); 207 - static void compile_var_decl(sv_compiler_t *c, sv_ast_t *node); 208 - static void compile_import_decl(sv_compiler_t *c, sv_ast_t *node); 209 - static void compile_export_decl(sv_compiler_t *c, sv_ast_t *node); 210 - static void compile_destructure_binding(sv_compiler_t *c, sv_ast_t *pat, sv_var_kind_t kind); 211 - static void compile_if(sv_compiler_t *c, sv_ast_t *node); 212 - static void compile_while(sv_compiler_t *c, sv_ast_t *node); 213 - static void compile_do_while(sv_compiler_t *c, sv_ast_t *node); 214 - static void compile_for(sv_compiler_t *c, sv_ast_t *node); 215 - static void compile_for_in(sv_compiler_t *c, sv_ast_t *node); 216 - static void compile_for_of(sv_compiler_t *c, sv_ast_t *node); 217 - static void compile_break(sv_compiler_t *c, sv_ast_t *node); 218 - static void compile_continue(sv_compiler_t *c, sv_ast_t *node); 219 - static void compile_try(sv_compiler_t *c, sv_ast_t *node); 220 - static void compile_switch(sv_compiler_t *c, sv_ast_t *node); 221 - static void compile_label(sv_compiler_t *c, sv_ast_t *node); 222 - static void compile_class(sv_compiler_t *c, sv_ast_t *node); 223 - 224 - static uint8_t infer_expr_type(sv_compiler_t *c, sv_ast_t *node); 225 - 226 23 static const char *pin_source_text(const char *source, ant_offset_t source_len) { 227 24 if (!source || source_len <= 0) return source; 228 25 const char *pinned = code_arena_alloc(source, (size_t)source_len); ··· 276 73 if (c->srcpos_count > 0 && c->last_srcpos_off == off && c->last_srcpos_end == end) return; 277 74 278 75 uint32_t line, col; 279 - if (c->line_table) { 280 - line_table_lookup(c->line_table, off, &line, &col); 281 - } else if (c->srcpos_count > 0 && off >= c->last_srcpos_off) { 76 + if (c->line_table) sv_compile_ctx_line_table_lookup(c->line_table, off, &line, &col); 77 + else if (c->srcpos_count > 0 && off >= c->last_srcpos_off) { 282 78 line = c->srcpos[c->srcpos_count - 1].line; 283 79 col = c->srcpos[c->srcpos_count - 1].col; 284 80 for (uint32_t i = c->last_srcpos_off; i < off; i++) { ··· 600 396 } 601 397 602 398 static int resolve_local(sv_compiler_t *c, const char *name, uint32_t len) { 399 + if (c->local_lookup_heads && c->local_lookup_cap > 0) { 400 + uint32_t hash = sv_compile_ctx_hash_local_name(name, len); 401 + int bucket = (int)(hash & (uint32_t)(c->local_lookup_cap - 1)); 402 + for (int i = c->local_lookup_heads[bucket]; i != -1; i = c->locals[i].lookup_next) { 403 + sv_local_t *loc = &c->locals[i]; 404 + if ( 405 + loc->name_hash == hash && 406 + loc->name_len == len && 407 + memcmp(loc->name, name, len) == 0 408 + ) return i; 409 + } 410 + return -1; 411 + } 412 + 603 413 for (int i = c->local_count - 1; i >= 0; i--) { 604 414 sv_local_t *loc = &c->locals[i]; 605 - if (loc->name_len == len && memcmp(loc->name, name, len) == 0) 606 - return i; 415 + if (loc->name_len == len && memcmp(loc->name, name, len) == 0) return i; 607 416 } 417 + 608 418 return -1; 609 419 } 610 420 ··· 612 422 sv_compiler_t *c, const char *name, uint32_t len, 613 423 bool is_const, int depth 614 424 ) { 425 + sv_compile_ctx_ensure_local_lookup_capacity(c, c->local_count + 1); 615 426 if (c->local_count >= c->local_cap) { 616 427 c->local_cap = c->local_cap ? c->local_cap * 2 : 16; 617 428 c->locals = realloc(c->locals, (size_t)c->local_cap * sizeof(sv_local_t)); ··· 621 432 c->max_local_count = c->local_count; 622 433 c->locals[idx] = (sv_local_t){ 623 434 .name = name, .name_len = len, 435 + .name_hash = sv_compile_ctx_hash_local_name(name, len), 436 + .lookup_next = -1, 624 437 .depth = depth, .is_const = is_const, .captured = false, 625 438 .inferred_type = SV_TI_UNKNOWN, 626 439 }; 440 + sv_compile_ctx_local_lookup_insert(c, idx); 627 441 return idx; 628 442 } 629 443 ··· 875 689 emit_op(c, OP_CLOSE_UPVAL); 876 690 emit_u16(c, (uint16_t)frame_slot); 877 691 } 692 + sv_compile_ctx_local_lookup_remove(c, c->local_count - 1); 878 693 c->local_count--; 879 694 } 880 695 c->scope_depth--; ··· 1406 1221 emit_u16(c, base_slot); 1407 1222 } 1408 1223 1409 - static void compile_expr(sv_compiler_t *c, sv_ast_t *node) { 1224 + void compile_expr(sv_compiler_t *c, sv_ast_t *node) { 1410 1225 if (!node) { emit_op(c, OP_UNDEF); return; } 1411 1226 emit_srcpos(c, node); 1412 1227 ··· 1648 1463 } 1649 1464 } 1650 1465 1651 - static void compile_binary(sv_compiler_t *c, sv_ast_t *node) { 1466 + void compile_binary(sv_compiler_t *c, sv_ast_t *node) { 1652 1467 uint8_t op = node->op; 1653 1468 1654 1469 if (op == TOK_LAND) { ··· 1734 1549 } 1735 1550 } 1736 1551 1737 - static void compile_unary(sv_compiler_t *c, sv_ast_t *node) { 1552 + void compile_unary(sv_compiler_t *c, sv_ast_t *node) { 1738 1553 compile_expr(c, node->right); 1739 1554 switch (node->op) { 1740 1555 case TOK_NOT: emit_op(c, OP_NOT); break; ··· 1746 1561 } 1747 1562 1748 1563 1749 - static void compile_update(sv_compiler_t *c, sv_ast_t *node) { 1564 + void compile_update(sv_compiler_t *c, sv_ast_t *node) { 1750 1565 bool prefix = (node->flags & 1); 1751 1566 bool is_inc = (node->op == TOK_POSTINC); 1752 1567 sv_ast_t *target = node->right; ··· 1794 1609 } 1795 1610 } 1796 1611 1797 - static void compile_assign(sv_compiler_t *c, sv_ast_t *node) { 1612 + void compile_assign(sv_compiler_t *c, sv_ast_t *node) { 1798 1613 sv_ast_t *target = node->left; 1799 1614 uint8_t op = node->op; 1800 1615 ··· 1978 1793 } 1979 1794 } 1980 1795 1981 - static void compile_lhs_set(sv_compiler_t *c, sv_ast_t *target, bool keep) { 1796 + void compile_lhs_set(sv_compiler_t *c, sv_ast_t *target, bool keep) { 1982 1797 if (target->type == N_IDENT) { 1983 1798 emit_set_var(c, target->str, target->len, keep); 1984 1799 } else if (target->type == N_MEMBER && !(target->flags & 1)) { ··· 1999 1814 } 2000 1815 } 2001 1816 2002 - static void compile_ternary(sv_compiler_t *c, sv_ast_t *node) { 1817 + void compile_ternary(sv_compiler_t *c, sv_ast_t *node) { 2003 1818 compile_expr(c, node->cond); 2004 1819 int else_jump = emit_jump(c, OP_JMP_FALSE); 2005 1820 compile_expr(c, node->left); ··· 2009 1824 patch_jump(c, end_jump); 2010 1825 } 2011 1826 2012 - static void compile_typeof(sv_compiler_t *c, sv_ast_t *node) { 1827 + void compile_typeof(sv_compiler_t *c, sv_ast_t *node) { 2013 1828 sv_ast_t *arg = node->right; 2014 1829 if (arg->type == N_IDENT) { 2015 1830 int local = resolve_local(c, arg->str, arg->len); ··· 2068 1883 patch_jump(c, end_jump); 2069 1884 } 2070 1885 2071 - static void compile_delete(sv_compiler_t *c, sv_ast_t *node) { 1886 + void compile_delete(sv_compiler_t *c, sv_ast_t *node) { 2072 1887 sv_ast_t *arg = node->right; 2073 1888 if (arg->type == N_OPTIONAL) { 2074 1889 compile_delete_optional(c, arg); ··· 2092 1907 } 2093 1908 } 2094 1909 2095 - static void compile_template(sv_compiler_t *c, sv_ast_t *node) { 1910 + void compile_template(sv_compiler_t *c, sv_ast_t *node) { 2096 1911 int n = node->args.count; 2097 1912 if (n == 0) { 2098 1913 emit_constant(c, js_mkstr_permanent(c->js, "", 0)); ··· 2302 2117 compile_optional_call_after_setup(c, node, kind, has_spread); 2303 2118 } 2304 2119 2305 - static void compile_call(sv_compiler_t *c, sv_ast_t *node) { 2120 + void compile_call(sv_compiler_t *c, sv_ast_t *node) { 2306 2121 sv_ast_t *callee = node->left; 2307 2122 bool has_spread = call_has_spread_arg(node); 2308 2123 ··· 2371 2186 compile_call_emit_invoke(c, node, kind, has_spread); 2372 2187 } 2373 2188 2374 - static void compile_new(sv_compiler_t *c, sv_ast_t *node) { 2189 + void compile_new(sv_compiler_t *c, sv_ast_t *node) { 2375 2190 compile_expr(c, node->left); 2376 2191 emit_op(c, OP_DUP); 2377 2192 if (call_has_spread_arg(node)) { ··· 2387 2202 } 2388 2203 } 2389 2204 2390 - static void compile_member(sv_compiler_t *c, sv_ast_t *node) { 2205 + void compile_member(sv_compiler_t *c, sv_ast_t *node) { 2391 2206 if (is_ident_name(node->left, "super")) { 2392 2207 emit_op(c, OP_THIS); 2393 2208 emit_get_var(c, "super", 5); ··· 2423 2238 if (end_jump >= 0) patch_jump(c, end_jump); 2424 2239 } 2425 2240 2426 - static void compile_optional_get(sv_compiler_t *c, sv_ast_t *node) { 2241 + void compile_optional_get(sv_compiler_t *c, sv_ast_t *node) { 2427 2242 if (node->flags & 1) { 2428 2243 compile_expr(c, node->right); 2429 2244 emit_op(c, OP_GET_ELEM_OPT); ··· 2433 2248 } 2434 2249 } 2435 2250 2436 - static void compile_optional(sv_compiler_t *c, sv_ast_t *node) { 2251 + void compile_optional(sv_compiler_t *c, sv_ast_t *node) { 2437 2252 compile_expr(c, node->left); 2438 2253 compile_optional_get(c, node); 2439 2254 } 2440 2255 2441 - static void compile_array(sv_compiler_t *c, sv_ast_t *node) { 2256 + void compile_array(sv_compiler_t *c, sv_ast_t *node) { 2442 2257 int count = node->args.count; 2443 2258 bool has_spread = false; 2444 2259 for (int i = 0; i < count; i++) { ··· 2471 2286 } 2472 2287 } 2473 2288 2474 - static void compile_object(sv_compiler_t *c, sv_ast_t *node) { 2289 + void compile_object(sv_compiler_t *c, sv_ast_t *node) { 2475 2290 emit_op(c, OP_OBJECT); 2476 2291 for (int i = 0; i < node->args.count; i++) { 2477 2292 sv_ast_t *prop = node->args.items[i]; ··· 2528 2343 } 2529 2344 } 2530 2345 2531 - static void compile_func_expr(sv_compiler_t *c, sv_ast_t *node) { 2346 + void compile_func_expr(sv_compiler_t *c, sv_ast_t *node) { 2532 2347 bool has_name = node->str && node->len > 0 && !(node->flags & FN_ARROW); 2533 2348 int name_local = -1; 2534 2349 ··· 2753 2568 if (consume_source) emit_op(c, OP_POP); 2754 2569 } 2755 2570 2756 - static void compile_array_destructure(sv_compiler_t *c, sv_ast_t *pat, 2571 + void compile_array_destructure(sv_compiler_t *c, sv_ast_t *pat, 2757 2572 bool keep) { 2758 2573 compile_destructure_pattern(c, pat, keep, true, DESTRUCTURE_ASSIGN, SV_VAR_LET); 2759 2574 } 2760 2575 2761 - static void compile_object_destructure(sv_compiler_t *c, sv_ast_t *pat, 2576 + void compile_object_destructure(sv_compiler_t *c, sv_ast_t *pat, 2762 2577 bool keep) { 2763 2578 compile_destructure_pattern(c, pat, keep, true, DESTRUCTURE_ASSIGN, SV_VAR_LET); 2764 2579 } ··· 2792 2607 emit_u16(c, (uint16_t)argc); 2793 2608 } 2794 2609 2795 - static void compile_tail_return_expr(sv_compiler_t *c, sv_ast_t *expr) { 2610 + void compile_tail_return_expr(sv_compiler_t *c, sv_ast_t *expr) { 2796 2611 if (expr->type == N_TERNARY) { 2797 2612 compile_expr(c, expr->cond); 2798 2613 int else_jump = emit_jump(c, OP_JMP_FALSE); ··· 2819 2634 emit_op(c, OP_RETURN); 2820 2635 } 2821 2636 2822 - static void compile_stmts(sv_compiler_t *c, sv_ast_list_t *list) { 2637 + void compile_stmts(sv_compiler_t *c, sv_ast_list_t *list) { 2823 2638 for (int i = 0; i < list->count; i++) 2824 2639 compile_stmt(c, list->items[i]); 2825 2640 } 2826 2641 2827 - static void compile_stmt(sv_compiler_t *c, sv_ast_t *node) { 2642 + void compile_stmt(sv_compiler_t *c, sv_ast_t *node) { 2828 2643 if (!node) return; 2829 2644 emit_srcpos(c, node); 2830 2645 ··· 2944 2759 IMPORT_BIND_NAMESPACE = 1 << 1, 2945 2760 }; 2946 2761 2947 - static void compile_import_decl(sv_compiler_t *c, sv_ast_t *node) { 2762 + void compile_import_decl(sv_compiler_t *c, sv_ast_t *node) { 2948 2763 if (!node->right) return; 2949 2764 bool repl_top = is_repl_top_level(c); 2950 2765 ··· 3065 2880 } 3066 2881 } 3067 2882 3068 - static void compile_export_decl(sv_compiler_t *c, sv_ast_t *node) { 2883 + void compile_export_decl(sv_compiler_t *c, sv_ast_t *node) { 3069 2884 if (!node) return; 3070 2885 3071 2886 if (node->flags & EX_DEFAULT) { ··· 3151 2966 } 3152 2967 } 3153 2968 3154 - static void compile_var_decl(sv_compiler_t *c, sv_ast_t *node) { 2969 + void compile_var_decl(sv_compiler_t *c, sv_ast_t *node) { 3155 2970 sv_var_kind_t kind = node->var_kind; 3156 2971 bool is_const = (kind == SV_VAR_CONST); 3157 2972 bool repl_top = is_repl_top_level(c); ··· 3215 3030 } 3216 3031 } 3217 3032 3218 - static void compile_destructure_binding(sv_compiler_t *c, sv_ast_t *pat, 3033 + void compile_destructure_binding(sv_compiler_t *c, sv_ast_t *pat, 3219 3034 sv_var_kind_t kind) { 3220 3035 compile_destructure_pattern(c, pat, false, false, DESTRUCTURE_BIND, kind); 3221 3036 } ··· 3255 3070 } 3256 3071 3257 3072 3258 - static void compile_if(sv_compiler_t *c, sv_ast_t *node) { 3073 + void compile_if(sv_compiler_t *c, sv_ast_t *node) { 3259 3074 bool folded_truth = false; 3260 3075 if (fold_static_typeof_compare(c, node->cond, &folded_truth)) { 3261 3076 if (folded_truth) compile_stmt(c, node->left); ··· 3277 3092 } 3278 3093 3279 3094 3280 - static void compile_while(sv_compiler_t *c, sv_ast_t *node) { 3095 + void compile_while(sv_compiler_t *c, sv_ast_t *node) { 3281 3096 int loop_start = c->code_len; 3282 3097 push_loop(c, loop_start, NULL, 0, false); 3283 3098 ··· 3295 3110 } 3296 3111 3297 3112 3298 - static void compile_do_while(sv_compiler_t *c, sv_ast_t *node) { 3113 + void compile_do_while(sv_compiler_t *c, sv_ast_t *node) { 3299 3114 int loop_start = c->code_len; 3300 3115 push_loop(c, loop_start, NULL, 0, false); 3301 3116 ··· 3378 3193 } 3379 3194 } 3380 3195 3381 - static void compile_for(sv_compiler_t *c, sv_ast_t *node) { 3196 + void compile_for(sv_compiler_t *c, sv_ast_t *node) { 3382 3197 begin_scope(c); 3383 3198 3384 3199 int *iter_slots = NULL; ··· 3408 3223 3409 3224 int iter_inner_start = -1; 3410 3225 if (iter_count > 0) { 3411 - begin_scope(c); 3412 - iter_inner_start = c->local_count; 3413 - for (int i = 0; i < iter_count; i++) { 3414 - int outer_idx = iter_slots[i]; 3415 - sv_local_t outer = c->locals[outer_idx]; 3416 - emit_get_local(c, outer_idx); 3417 - int inner_idx = add_local(c, outer.name, outer.name_len, 3418 - outer.is_const, c->scope_depth); 3419 - emit_put_local(c, inner_idx); 3420 - } 3421 - } 3226 + begin_scope(c); 3227 + iter_inner_start = c->local_count; 3228 + for (int i = 0; i < iter_count; i++) { 3229 + int outer_idx = iter_slots[i]; 3230 + sv_local_t outer = c->locals[outer_idx]; 3231 + emit_get_local(c, outer_idx); 3232 + int inner_idx = add_local(c, outer.name, outer.name_len, outer.is_const, c->scope_depth); 3233 + emit_put_local(c, inner_idx); 3234 + }} 3422 3235 3423 3236 compile_stmt(c, node->body); 3424 - 3425 3237 sv_loop_t *loop = &c->loops[c->loop_count - 1]; 3426 3238 for (int i = 0; i < loop->continues.count; i++) 3427 3239 patch_jump(c, loop->continues.offsets[i]); ··· 3479 3291 3480 3292 static void compile_for_each(sv_compiler_t *c, sv_ast_t *node, bool is_for_of); 3481 3293 3482 - static void compile_for_in(sv_compiler_t *c, sv_ast_t *node) { 3294 + void compile_for_in(sv_compiler_t *c, sv_ast_t *node) { 3483 3295 compile_for_each(c, node, false); 3484 3296 } 3485 3297 3486 3298 3487 - static void compile_for_of(sv_compiler_t *c, sv_ast_t *node) { 3299 + void compile_for_of(sv_compiler_t *c, sv_ast_t *node) { 3488 3300 compile_for_each(c, node, true); 3489 3301 } 3490 3302 ··· 3709 3521 } 3710 3522 }} 3711 3523 3712 - static void compile_break(sv_compiler_t *c, sv_ast_t *node) { 3524 + void compile_break(sv_compiler_t *c, sv_ast_t *node) { 3713 3525 if (c->loop_count == 0) return; 3714 3526 3715 3527 int target = c->loop_count - 1; ··· 3730 3542 } 3731 3543 3732 3544 3733 - static void compile_continue(sv_compiler_t *c, sv_ast_t *node) { 3545 + void compile_continue(sv_compiler_t *c, sv_ast_t *node) { 3734 3546 for (int i = c->loop_count - 1; i >= 0; i--) { 3735 3547 if (node->str) { 3736 3548 if ( ··· 3758 3570 static void compile_catch_body(sv_compiler_t *c, sv_ast_t *node) { 3759 3571 begin_scope(c); 3760 3572 if (node->catch_param && node->catch_param->type == N_IDENT) { 3761 - int loc = add_local(c, node->catch_param->str, 3762 - node->catch_param->len, false, c->scope_depth); 3573 + int loc = add_local(c, node->catch_param->str, node->catch_param->len, false, c->scope_depth); 3763 3574 emit_put_local(c, loc); 3764 3575 } else if (node->catch_param && is_destructure_pattern_node(node->catch_param)) { 3765 3576 compile_destructure_binding(c, node->catch_param, SV_VAR_LET); 3766 3577 emit_op(c, OP_POP); 3767 - } else { 3768 - emit_op(c, OP_POP); 3769 - } 3578 + } else emit_op(c, OP_POP); 3579 + 3770 3580 compile_stmt(c, node->catch_body); 3771 3581 end_scope(c); 3772 3582 } 3773 3583 3774 - static void compile_try(sv_compiler_t *c, sv_ast_t *node) { 3584 + void compile_try(sv_compiler_t *c, sv_ast_t *node) { 3775 3585 c->try_depth++; 3776 3586 int try_jump = emit_jump(c, OP_TRY_PUSH); 3777 3587 ··· 3829 3639 } 3830 3640 3831 3641 3832 - static void compile_switch(sv_compiler_t *c, sv_ast_t *node) { 3642 + void compile_switch(sv_compiler_t *c, sv_ast_t *node) { 3833 3643 int case_count = node->args.count; 3834 3644 int default_case = -1; 3835 3645 int *match_to_stub = NULL; ··· 3899 3709 3900 3710 3901 3711 static inline bool is_loop_node(sv_ast_t *n) { 3902 - return n && (n->type == N_WHILE || n->type == N_DO_WHILE || 3903 - n->type == N_FOR || n->type == N_FOR_IN || 3904 - n->type == N_FOR_OF || n->type == N_FOR_AWAIT_OF); 3712 + return n && 3713 + (n->type == N_WHILE || n->type == N_DO_WHILE || 3714 + n->type == N_FOR || n->type == N_FOR_IN || 3715 + n->type == N_FOR_OF || n->type == N_FOR_AWAIT_OF); 3905 3716 } 3906 3717 3907 - static void compile_label(sv_compiler_t *c, sv_ast_t *node) { 3718 + void compile_label(sv_compiler_t *c, sv_ast_t *node) { 3908 3719 if (is_loop_node(node->body)) { 3909 3720 c->pending_label = node->str; 3910 3721 c->pending_label_len = node->len; ··· 4003 3814 return loc; 4004 3815 } 4005 3816 4006 - static void compile_class(sv_compiler_t *c, sv_ast_t *node) { 3817 + void compile_class(sv_compiler_t *c, sv_ast_t *node) { 4007 3818 int outer_name_local = -1; 4008 3819 bool class_repl_top = is_repl_top_level(c); 3820 + 4009 3821 sv_ast_t *ctor_method = NULL; 4010 3822 bool has_static_name = false; 3823 + 4011 3824 int field_count = 0; 4012 3825 int computed_method_count = 0; 4013 3826 ··· 4089 3902 c->computed_key_locals = NULL; 4090 3903 } else if (field_count > 0) { 4091 3904 c->computed_key_locals = computed_key_locals; 4092 - sv_compiler_t comp = {0}; 4093 - comp.js = c->js; 4094 - comp.filename = c->filename; 4095 - comp.source = c->source; 4096 - comp.source_len = c->source_len; 4097 - comp.line_table = c->line_table; 4098 - comp.enclosing = c; 4099 - comp.scope_depth = 0; 4100 - comp.is_strict = c->is_strict; 4101 - comp.param_count = 0; 3905 + sv_compiler_t comp; 3906 + sv_compile_ctx_init_child(&comp, c, NULL, c->mode); 3907 + 3908 + if (node->left) { 3909 + emit_op(&comp, OP_THIS); 3910 + emit_op(&comp, OP_SPECIAL_OBJ); 3911 + emit(&comp, 2); 3912 + emit_op(&comp, OP_SWAP); 3913 + emit_op(&comp, OP_SPECIAL_OBJ); 3914 + emit(&comp, 0); 3915 + emit_op(&comp, OP_SUPER_APPLY); 3916 + emit_u16(&comp, 1); 3917 + emit_op(&comp, OP_POP); 3918 + } 4102 3919 4103 3920 emit_field_inits(&comp, field_inits, field_count); 4104 3921 emit_op(&comp, OP_RETURN_UNDEF); ··· 4153 3970 name[node->len] = '\0'; 4154 3971 fn->name = name; 4155 3972 } 4156 - free(comp.code); free(comp.constants); free(comp.atoms); 4157 - free(comp.locals); free(comp.upval_descs); free(comp.loops); 4158 - free(comp.slot_types); 3973 + sv_compile_ctx_cleanup(&comp); 4159 3974 4160 3975 int idx = add_constant(c, mkval(T_CFUNC, (uintptr_t)fn)); 4161 3976 emit_op(c, OP_CLOSURE); ··· 4182 3997 emit_put_local(c, proto_local); 4183 3998 emit_put_local(c, ctor_local); 4184 3999 4000 + if (inner_name_local >= 0) { 4001 + emit_get_local(c, ctor_local); 4002 + emit_put_local(c, inner_name_local); 4003 + } 4004 + 4185 4005 for (int i = 0; i < node->args.count; i++) { 4186 4006 sv_ast_t *m = node->args.items[i]; 4187 4007 if (m->type == N_STATIC_BLOCK) { ··· 4205 4025 4206 4026 free(method_comp_keys); 4207 4027 emit_get_local(c, ctor_local); 4208 - 4209 - if (inner_name_local >= 0) { 4210 - emit_op(c, OP_DUP); 4211 - emit_put_local(c, inner_name_local); 4212 - } 4213 4028 4214 4029 if (class_repl_top && node->str) { 4215 4030 emit_op(c, OP_DUP); ··· 4223 4038 if (node->str) end_scope(c); 4224 4039 } 4225 4040 4226 - static sv_func_t *compile_function_body( 4041 + sv_func_t *compile_function_body( 4227 4042 sv_compiler_t *enclosing, 4228 4043 sv_ast_t *node, 4229 4044 sv_compile_mode_t mode 4230 4045 ) { 4231 - sv_compiler_t comp = {0}; 4232 - comp.js = enclosing->js; 4233 - comp.filename = enclosing->filename; 4234 - comp.source = enclosing->source; 4235 - comp.source_len = enclosing->source_len; 4236 - comp.line_table = enclosing->line_table; 4237 - comp.enclosing = enclosing; 4238 - comp.scope_depth = 0; 4239 - comp.is_arrow = !!(node->flags & FN_ARROW); 4240 - comp.is_async = !!(node->flags & FN_ASYNC); 4241 - comp.is_strict = enclosing->is_strict; 4242 - comp.mode = mode; 4243 - comp.strict_args_local = -1; 4244 - comp.new_target_local = -1; 4245 - comp.super_local = -1; 4246 - comp.param_count = node->args.count; 4046 + sv_compiler_t comp; 4047 + sv_compile_ctx_init_child(&comp, enclosing, node, mode); 4247 4048 4248 4049 for (int i = 0; i < node->args.count; i++) { 4249 4050 sv_ast_t *p = node->args.items[i]; ··· 4473 4274 } 4474 4275 4475 4276 free(param_bind_locals); 4277 + sv_ast_t *body = node->body; 4476 4278 4477 - if (node->body) { 4478 - if (node->body->type == N_BLOCK) { 4479 - if (!repl_top) { 4480 - for (int i = 0; i < node->body->args.count; i++) 4481 - hoist_var_decls(&comp, node->body->args.items[i]); 4482 - hoist_lexical_decls(&comp, &node->body->args); 4483 - } 4484 - hoist_func_decls(&comp, &node->body->args); 4485 - } else if (!repl_top) hoist_var_decls(&comp, node->body); 4279 + if (body && body->type != N_BLOCK) { 4280 + if (!repl_top) hoist_var_decls(&comp, body); 4281 + } else if (body) { 4282 + if (!repl_top) { 4283 + for (int i = 0; i < body->args.count; i++) 4284 + hoist_var_decls(&comp, body->args.items[i]); 4285 + hoist_lexical_decls(&comp, &body->args); 4286 + } 4287 + hoist_func_decls(&comp, &body->args); 4486 4288 } 4487 4289 } 4488 4290 ··· 4630 4432 } 4631 4433 4632 4434 if (func->is_async) { 4633 - const uint8_t *ip = func->code; 4634 - const uint8_t *end = func->code + func->code_len; 4635 - while (ip < end) { 4636 - sv_op_t op = (sv_op_t)*ip; 4637 - if (op == OP_AWAIT || op == OP_FOR_AWAIT_OF || op == OP_AWAIT_ITER_NEXT) { 4638 - func->has_await = true; 4639 - break; 4640 - } 4641 - int sz = sv_op_size[op]; 4642 - if (sz <= 0) break; 4643 - ip += sz; 4435 + const uint8_t *ip = func->code; 4436 + const uint8_t *end = func->code + func->code_len; 4437 + while (ip < end) { 4438 + sv_op_t op = (sv_op_t)*ip; 4439 + if (op == OP_AWAIT || op == OP_FOR_AWAIT_OF || op == OP_AWAIT_ITER_NEXT) { 4440 + func->has_await = true; 4441 + break; 4644 4442 } 4645 - } 4646 - 4647 - free(comp.code); 4648 - free(comp.constants); 4649 - free(comp.atoms); 4650 - free(comp.locals); 4651 - free(comp.upval_descs); 4652 - free(comp.loops); 4653 - free(comp.srcpos); 4654 - free(comp.slot_types); 4655 - 4656 - { 4657 - const_dedup_entry_t *e, *tmp; 4658 - HASH_ITER(hh, comp.const_dedup, e, tmp) { 4659 - HASH_DEL(comp.const_dedup, e); free(e); 4443 + int sz = sv_op_size[op]; 4444 + if (sz <= 0) break; 4445 + ip += sz; 4660 4446 }} 4661 4447 4448 + sv_compile_ctx_cleanup(&comp); 4662 4449 return func; 4663 4450 } 4664 4451 ··· 4830 4617 fprintf(stderr, "\n"); 4831 4618 4832 4619 for (int i = 0; i < func->const_count; i++) { 4833 - if (vtype(func->constants[i]) == T_CFUNC) { 4834 - sv_func_t *child = (sv_func_t *)(uintptr_t)vdata(func->constants[i]); 4835 - char child_label[256]; 4836 - snprintf(child_label, sizeof(child_label), "%s/closure[%d]", label, i); 4837 - sv_disasm(js, child, child_label); 4838 - } 4839 - } 4620 + if (vtype(func->constants[i]) == T_CFUNC) { 4621 + sv_func_t *child = (sv_func_t *)(uintptr_t)vdata(func->constants[i]); 4622 + char child_label[256]; 4623 + snprintf(child_label, sizeof(child_label), "%s/closure[%d]", label, i); 4624 + sv_disasm(js, child, child_label); 4625 + }} 4840 4626 } 4841 4627 4842 4628 sv_func_t *sv_compile(ant_t *js, sv_ast_t *program, sv_compile_mode_t mode, const char *source, ant_offset_t source_len) { 4843 4629 if (!program || program->type != N_PROGRAM) return NULL; 4630 + if (sv_compile_trace_unlikely) fprintf( 4631 + stderr, "[compile] start kind=program mode=%d len=%u body=%d strict=%d\n", 4632 + (int)mode, (unsigned)source_len, 4633 + program->args.count, (program->flags & FN_PARSE_STRICT) != 0 ? 1 : 0 4634 + ); 4844 4635 4845 4636 static const char *k_top_name_script = "<script>"; 4846 4637 static const char *k_top_name_module = "<module>"; ··· 4868 4659 top_fn.body = sv_ast_new(N_BLOCK); 4869 4660 top_fn.body->args = program->args; 4870 4661 4871 - sv_compiler_t root = {0}; 4872 - root.js = js; 4873 - root.filename = js->filename; 4874 - root.source = pin_source_text(source, source_len); 4875 - root.source_len = source_len; 4876 - root.mode = mode; 4877 - root.is_strict = ((program->flags & FN_PARSE_STRICT) != 0); 4878 - root.line_table = build_line_table(root.source, source_len); 4662 + sv_compiler_t root; 4663 + sv_compile_ctx_init_root( 4664 + &root, js, js->filename, 4665 + pin_source_text(source, source_len), 4666 + source_len, mode, 4667 + (program->flags & FN_PARSE_STRICT) != 0, NULL 4668 + ); 4669 + 4670 + root.line_table = sv_compile_ctx_build_line_table(root.source, source_len); 4879 4671 sv_func_t *func = compile_function_body(&root, &top_fn, mode); 4880 - free_line_table(root.line_table); 4672 + sv_compile_ctx_free_line_table(root.line_table); 4673 + 4674 + if (sv_compile_trace_unlikely) fprintf( 4675 + stderr, "[compile] end kind=program mode=%d thrown=%d func=%p\n", 4676 + (int)mode, js->thrown_exists ? 1 : 0, (void *)func 4677 + ); 4678 + 4881 4679 if (js->thrown_exists || !func) return NULL; 4882 - 4883 4680 return func; 4884 4681 } 4885 4682 4886 4683 sv_func_t *sv_compile_function(ant_t *js, const char *source, size_t len, bool is_async, bool is_generator) { 4684 + if (sv_compile_trace_unlikely) fprintf( 4685 + stderr, "[compile] start kind=function len=%u async=%d generator=%d\n", 4686 + (unsigned)len, is_async ? 1 : 0, is_generator ? 1 : 0 4687 + ); 4688 + 4887 4689 const char *prefix = is_async 4888 4690 ? "(async function" 4889 4691 : (is_generator ? "(function*" : "(function"); ··· 4912 4714 4913 4715 if (!func_node) { free(wrapped); return NULL; } 4914 4716 4915 - sv_compiler_t root = {0}; 4916 - root.js = js; 4917 - root.filename = js->filename; 4918 - root.source = pin_source_text(wrapped, (ant_offset_t)wrapped_len); 4919 - root.source_len = (ant_offset_t)wrapped_len; 4920 - root.mode = SV_COMPILE_SCRIPT; 4921 - root.is_strict = (program->flags & FN_PARSE_STRICT) != 0; 4922 - 4717 + sv_compiler_t root; 4718 + sv_compile_ctx_init_root( 4719 + &root, js, js->filename, 4720 + pin_source_text(wrapped, (ant_offset_t)wrapped_len), 4721 + (ant_offset_t)wrapped_len, SV_COMPILE_SCRIPT, 4722 + (program->flags & FN_PARSE_STRICT) != 0, NULL 4723 + ); 4724 + 4725 + root.line_table = sv_compile_ctx_build_line_table(root.source, (ant_offset_t)wrapped_len); 4923 4726 sv_func_t *func = compile_function_body(&root, func_node, SV_COMPILE_SCRIPT); 4727 + sv_compile_ctx_free_line_table(root.line_table); 4924 4728 free(wrapped); 4925 4729 4730 + if (sv_compile_trace_unlikely) fprintf( 4731 + stderr, "[compile] end kind=function thrown=%d func=%p\n", 4732 + js->thrown_exists ? 1 : 0, (void *)func 4733 + ); 4734 + 4926 4735 if (js->thrown_exists || !func) return NULL; 4927 4736 return func; 4928 4737 } ··· 4935 4744 size_t body_len, 4936 4745 bool is_async 4937 4746 ) { 4747 + if (sv_compile_trace_unlikely) fprintf( 4748 + stderr, "[compile] start kind=function-with-params len=%u params=%d async=%d\n", 4749 + (unsigned)body_len, param_count, is_async ? 1 : 0 4750 + ); 4751 + 4938 4752 if (!body) { 4939 4753 body = ""; 4940 4754 body_len = 0; ··· 4980 4794 if (!top_fn.body) return NULL; 4981 4795 top_fn.body->args = program->args; 4982 4796 4983 - sv_compiler_t root = {0}; 4984 - root.js = js; 4985 - root.filename = js->filename; 4986 - root.source = pin_source_text(body, (ant_offset_t)body_len); 4987 - root.source_len = (ant_offset_t)body_len; 4988 - root.mode = SV_COMPILE_SCRIPT; 4989 - root.is_strict = ((program->flags & FN_PARSE_STRICT) != 0); 4990 - 4797 + sv_compiler_t root; 4798 + sv_compile_ctx_init_root( 4799 + &root, js, js->filename, 4800 + pin_source_text(body, (ant_offset_t)body_len), 4801 + (ant_offset_t)body_len, SV_COMPILE_SCRIPT, 4802 + (program->flags & FN_PARSE_STRICT) != 0, NULL 4803 + ); 4804 + 4805 + root.line_table = sv_compile_ctx_build_line_table(root.source, (ant_offset_t)body_len); 4991 4806 sv_func_t *func = compile_function_body(&root, &top_fn, SV_COMPILE_SCRIPT); 4992 - if (js->thrown_exists || !func) return NULL; 4807 + sv_compile_ctx_free_line_table(root.line_table); 4993 4808 4809 + if (sv_compile_trace_unlikely) fprintf( 4810 + stderr, "[compile] end kind=function-with-params thrown=%d func=%p\n", 4811 + js->thrown_exists ? 1 : 0, (void *)func 4812 + ); 4813 + 4814 + if (js->thrown_exists || !func) return NULL; 4994 4815 return func; 4995 4816 }
+1 -2
src/silver/glue.c
··· 360 360 uint8_t ot = vtype(obj); 361 361 if (ot == T_NULL || ot == T_UNDEF) { 362 362 jit_set_error_site_from_func(js, func, bc_off); 363 - return js_mkerr_typed(js, JS_ERR_TYPE, 364 - "Cannot read properties of %s", ot == T_NULL ? "null" : "undefined"); 363 + return sv_mk_nullish_read_error_by_key(js, obj, key); 365 364 } 366 365 if (vtype(obj) == T_ARR && vtype(key) == T_NUM) { 367 366 double d = tod(key);
+25 -4
src/silver/ops/property.h
··· 32 32 return coerce_to_str(js, key); 33 33 } 34 34 35 + static inline ant_value_t sv_mk_nullish_read_error_by_key( 36 + ant_t *js, ant_value_t obj, ant_value_t key 37 + ) { 38 + uint8_t ot = vtype(obj); 39 + ant_value_t key_str = sv_key_to_propstr(js, key); 40 + 41 + if (!is_err(key_str) && vtype(key_str) == T_STR) { 42 + ant_offset_t klen = 0; 43 + ant_offset_t koff = vstr(js, key_str, &klen); 44 + const char *kptr = (const char *)(uintptr_t)(koff); 45 + 46 + return js_mkerr_typed(js, JS_ERR_TYPE, 47 + "Cannot read properties of %s (reading '%.*s')", 48 + ot == T_NULL ? "null" : "undefined", (int)klen, kptr 49 + ); 50 + } 51 + 52 + return js_mkerr_typed(js, JS_ERR_TYPE, 53 + "Cannot read properties of %s", 54 + ot == T_NULL ? "null" : "undefined" 55 + ); 56 + } 57 + 35 58 static inline ant_object_t *sv_array_obj_ptr(ant_value_t obj) { 36 59 if (!is_object_type(obj)) return NULL; 37 60 ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); ··· 622 645 623 646 if (ot == T_NULL || ot == T_UNDEF) { 624 647 if (func && ip) js_set_error_site_from_bc(js, func, (int)(ip - func->code), func->filename); 625 - return js_mkerr_typed(js, JS_ERR_TYPE, 626 - "Cannot read properties of %s", ot == T_NULL ? "null" : "undefined"); 648 + return sv_mk_nullish_read_error_by_key(js, obj, key); 627 649 } 628 650 629 651 if (vtype(obj) == T_ARR && vtype(key) == T_NUM) { ··· 656 678 uint8_t ot = vtype(obj); 657 679 if (ot == T_NULL || ot == T_UNDEF) { 658 680 if (func && ip) js_set_error_site_from_bc(js, func, (int)(ip - func->code), func->filename); 659 - return js_mkerr_typed(js, JS_ERR_TYPE, 660 - "Cannot read properties of %s", ot == T_NULL ? "null" : "undefined"); 681 + return sv_mk_nullish_read_error_by_key(js, obj, key); 661 682 } 662 683 663 684 if (vtype(obj) == T_ARR && vtype(key) == T_NUM) {
+13 -2
src/types/modules/crypto.d.ts
··· 1 1 declare module 'crypto' { 2 + type BufferSource = ArrayBuffer | ArrayBufferView; 3 + 4 + interface SubtleCrypto { 5 + digest(algorithm: string | { name: string }, data: BufferSource): Promise<ArrayBuffer>; 6 + } 7 + 2 8 interface WebCrypto { 3 9 random(): number; 4 10 randomBytes(length: number): number[]; 5 11 randomUUID(): string; 6 12 randomUUIDv7(): string; 7 13 getRandomValues<T extends ArrayBufferView>(array: T): T; 14 + subtle: SubtleCrypto; 8 15 } 9 16 10 17 const webcrypto: WebCrypto; 11 18 19 + function createHash(algorithm: string): { 20 + update(data: string | BufferSource, inputEncoding?: string): any; 21 + digest(encoding?: string): string | Uint8Array; 22 + }; 12 23 function randomBytes(length: number): number[]; 13 24 function randomUUID(): string; 14 25 function getRandomValues<T extends ArrayBufferView>(array: T): T; 15 26 } 16 27 17 28 declare module 'ant:crypto' { 18 - export * from 'events'; 29 + export * from 'crypto'; 19 30 } 20 31 21 32 declare module 'node:crypto' { 22 - export * from 'events'; 33 + export * from 'crypto'; 23 34 }
+22
src/types/modules/events.d.ts
··· 1 1 declare module 'events' { 2 + interface Disposable { 3 + dispose(): void; 4 + } 5 + 6 + interface EventListenerOptions { 7 + signal?: AbortSignal; 8 + } 9 + 2 10 class EventEmitter { 3 11 on(event: string, listener: (...args: unknown[]) => void): this; 4 12 addListener(event: string, listener: (...args: unknown[]) => void): this; ··· 10 18 listenerCount(event: string): number; 11 19 eventNames(): string[]; 12 20 } 21 + 22 + function once( 23 + emitter: EventEmitter | EventTarget | AbortSignal, 24 + eventName: string | symbol, 25 + options?: EventListenerOptions 26 + ): Promise<unknown[]>; 27 + function on( 28 + emitter: EventEmitter | EventTarget, 29 + eventName: string | symbol, 30 + options?: EventListenerOptions 31 + ): AsyncIterableIterator<unknown[]>; 32 + function addAbortListener(signal: AbortSignal, listener: (event: Event) => void): Disposable; 33 + function setMaxListeners(n: number, ...eventTargets: Array<EventEmitter | EventTarget>): void; 34 + function getMaxListeners(eventTarget: EventEmitter | EventTarget): number; 13 35 } 14 36 15 37 declare module 'ant:events' {
+12
src/types/modules/util.d.ts
··· 53 53 function formatWithOptions(inspectOptions: unknown, format?: unknown, ...args: unknown[]): string; 54 54 function inspect(value: unknown, options?: unknown): string; 55 55 function inherits(ctor: (...args: unknown[]) => unknown, superCtor: (...args: unknown[]) => unknown): void; 56 + function isDeepStrictEqual(a: unknown, b: unknown): boolean; 57 + function parseArgs(config: { 58 + args?: string[]; 59 + options?: Record<string, { 60 + type?: 'boolean' | 'string'; 61 + short?: string; 62 + multiple?: boolean; 63 + default?: unknown; 64 + }>; 65 + strict?: boolean; 66 + allowPositionals?: boolean; 67 + }): { values: Record<string, unknown>; positionals: string[] }; 56 68 function parseEnv(content: string): Record<string, string>; 57 69 function promisify(fn: (...args: unknown[]) => unknown): (...args: unknown[]) => Promise<unknown>; 58 70 function callbackify(fn: (...args: unknown[]) => Promise<unknown>): (...args: unknown[]) => void;
+13 -1
src/types/process.d.ts
··· 12 12 } 13 13 14 14 interface Versions { 15 + ant: string; 15 16 node: string; 16 - ant: string; 17 + brotli: string; 18 + llhttp: string; 19 + nghttp2: string; 20 + simdjson: string; 21 + pcre2: string; 22 + libffi: string; 23 + lmdb: string; 24 + utf8proc: string; 25 + zlib: string; 17 26 v8: string; 18 27 uv: string; 19 28 modules: string; 29 + napi: string; 30 + wamr: string; 31 + boringssl: string; 20 32 } 21 33 22 34 interface Release {
+2
tests/node_modules/cond-exports/import.js
··· 1 + export const branch = "import"; 2 + export default { branch };
+10
tests/node_modules/cond-exports/package.json
··· 1 + { 2 + "name": "cond-exports", 3 + "exports": { 4 + ".": { 5 + "import": "./import.js", 6 + "require": "./require.cjs", 7 + "default": "./require.cjs" 8 + } 9 + } 10 + }
+1
tests/node_modules/cond-exports/require.cjs
··· 1 + module.exports = { branch: "require" };
+12
tests/repro_crypto_create_hash_digest.mjs
··· 1 + import { createHash } from "node:crypto"; 2 + 3 + console.log("createHash:", typeof createHash); 4 + 5 + const hash = createHash("sha256"); 6 + 7 + console.log("hash:", typeof hash); 8 + console.log("hash.update:", typeof (hash && hash.update)); 9 + console.log("hash.digest:", typeof (hash && hash.digest)); 10 + 11 + const out = hash.update("ant").digest("hex"); 12 + console.log("digest:", out);
+25
tests/test_crypto_subtle_digest.mjs
··· 1 + import assert from "node:assert"; 2 + 3 + const data = new TextEncoder().encode("ant"); 4 + const digest = await crypto.subtle.digest("SHA-256", data); 5 + 6 + assert.ok(digest instanceof ArrayBuffer); 7 + 8 + const hex = Array.from(new Uint8Array(digest)) 9 + .map((byte) => byte.toString(16).padStart(2, "0")) 10 + .join(""); 11 + 12 + assert.equal(hex, "67a333356cdc566e6e346b5718447308ec0e25f47e623161fb03962b327a651f"); 13 + 14 + const viaModule = await (await import("node:crypto")).webcrypto.subtle.digest( 15 + { name: "SHA-256" }, 16 + data, 17 + ); 18 + 19 + assert.ok(viaModule instanceof ArrayBuffer); 20 + assert.equal( 21 + Array.from(new Uint8Array(viaModule)) 22 + .map((byte) => byte.toString(16).padStart(2, "0")) 23 + .join(""), 24 + hex, 25 + );
+38
tests/test_eventemitter_max_listeners.cjs
··· 1 + const assert = require('node:assert'); 2 + const events = require('node:events'); 3 + const { EventEmitter } = events; 4 + const { PassThrough } = require('node:stream'); 5 + 6 + const ee = new EventEmitter(); 7 + assert.equal(typeof ee.once, 'function'); 8 + assert.equal(typeof ee.prependListener, 'function'); 9 + assert.equal(typeof ee.prependOnceListener, 'function'); 10 + assert.equal(typeof ee.listenerCount, 'function'); 11 + assert.equal(typeof ee.setMaxListeners, 'function'); 12 + assert.equal(typeof ee.getMaxListeners, 'function'); 13 + assert.equal(typeof ee.rawListeners, 'function'); 14 + 15 + assert.equal(ee.getMaxListeners(), 10); 16 + assert.equal(ee.setMaxListeners(3), ee); 17 + assert.equal(ee.getMaxListeners(), 3); 18 + 19 + function a() {} 20 + function b() {} 21 + ee.on('x', a); 22 + ee.prependOnceListener('x', b); 23 + 24 + assert.equal(ee.listenerCount('x'), 2); 25 + assert.deepEqual(ee.rawListeners('x'), [b, a]); 26 + 27 + const pt = new PassThrough(); 28 + assert.equal(typeof pt.setMaxListeners, 'function'); 29 + assert.equal(typeof pt.getMaxListeners, 'function'); 30 + assert.equal(typeof pt.once, 'function'); 31 + assert.equal(typeof pt.prependListener, 'function'); 32 + assert.equal(typeof pt.prependOnceListener, 'function'); 33 + assert.equal(typeof pt.listenerCount, 'function'); 34 + assert.equal(typeof pt.rawListeners, 'function'); 35 + 36 + pt.setMaxListeners(30); 37 + assert.equal(pt.getMaxListeners(), 30); 38 + assert.equal(events.getMaxListeners(pt), 30);
+29
tests/test_function_call_apply_bind.cjs
··· 54 54 let calc = { base: 5 }; 55 55 console.log("Test 10 - chained:", multiply.call(calc, 3)); // 15 56 56 57 + // Test 11: Reflect.apply with arguments object from a class method 58 + class Forwarder { 59 + invoke() { 60 + return Reflect.apply(targetMethod, receiver, arguments); 61 + } 62 + } 63 + let receiver = { prefix: "sum" }; 64 + function targetMethod(a, b, c) { 65 + return this.prefix + ":" + [a, b, c].join(","); 66 + } 67 + console.log("Test 11 - Reflect.apply(arguments):", new Forwarder().invoke(1, 2, 3)); // sum:1,2,3 68 + 69 + // Test 12: Reflect.apply with omitted trailing args preserved by arguments length 70 + class ActiveSpanLike { 71 + startActiveSpan(name, options, callback) { 72 + return Reflect.apply(targetStartActiveSpan, this, arguments); 73 + } 74 + } 75 + function targetStartActiveSpan(name, options, callback) { 76 + return { argc: arguments.length, name, hasCallback: typeof callback === "function" }; 77 + } 78 + let activeSpanResult = new ActiveSpanLike().startActiveSpan("plugin", { enabled: true }, () => {}); 79 + console.log( 80 + "Test 12 - Reflect.apply method args:", 81 + activeSpanResult.argc, 82 + activeSpanResult.name, 83 + activeSpanResult.hasCallback 84 + ); // 3 plugin true 85 + 57 86 console.log("\nAll Function.prototype.call/apply/bind tests completed!");
+145
tests/test_inherited_class_constructor.cjs
··· 1 + const assert = require("node:assert"); 2 + 3 + class BaseBox { 4 + constructor(left, right) { 5 + this.left = left; 6 + this.right = right; 7 + } 8 + 9 + get pair() { 10 + return `${this.left}:${this.right}`; 11 + } 12 + } 13 + 14 + class DerivedBox extends BaseBox {} 15 + 16 + const derived = new DerivedBox("a", "b"); 17 + 18 + assert.equal(derived.left, "a"); 19 + assert.equal(derived.right, "b"); 20 + assert.equal(derived.pair, "a:b"); 21 + 22 + class URIBase { 23 + constructor(scheme, authority, path, query, fragment, strict = false, platform) { 24 + if (typeof scheme === "object") { 25 + this.scheme = scheme.scheme || ""; 26 + this.authority = scheme.authority || ""; 27 + this.path = scheme.path || ""; 28 + this.query = scheme.query || ""; 29 + this.fragment = scheme.fragment || ""; 30 + this.platform = scheme.platform; 31 + } else { 32 + this.scheme = scheme || ""; 33 + this.authority = authority || ""; 34 + this.path = path || ""; 35 + this.query = query || ""; 36 + this.fragment = fragment || ""; 37 + this.platform = platform; 38 + this.strict = strict; 39 + } 40 + } 41 + 42 + get fsPath() { 43 + if (this.authority && this.path.length > 1 && this.scheme === "file") { 44 + return `//${this.authority}${this.path}`; 45 + } 46 + if ( 47 + this.path.charCodeAt(0) === 47 && 48 + ((this.path.charCodeAt(1) >= 65 && this.path.charCodeAt(1) <= 90) || 49 + (this.path.charCodeAt(1) >= 97 && this.path.charCodeAt(1) <= 122)) && 50 + this.path.charCodeAt(2) === 58 51 + ) { 52 + return this.path[1].toLowerCase() + this.path.substring(2); 53 + } 54 + return this.path; 55 + } 56 + } 57 + 58 + class URIChild extends URIBase {} 59 + 60 + const uri = new URIChild("file", "", "/Users/test/project", "", "", false, "posix"); 61 + 62 + assert.equal(uri.scheme, "file"); 63 + assert.equal(uri.authority, ""); 64 + assert.equal(uri.path, "/Users/test/project"); 65 + assert.equal(uri.platform, "posix"); 66 + assert.equal(uri.fsPath, "/Users/test/project"); 67 + 68 + console.log("ok"); 69 + 70 + class FieldBase { 71 + scheme; 72 + authority; 73 + path; 74 + query; 75 + fragment; 76 + platform; 77 + 78 + constructor(scheme, authority, path, query, fragment, strict = false, platform) { 79 + if (typeof scheme === "object") { 80 + this.scheme = scheme.scheme || ""; 81 + this.authority = scheme.authority || ""; 82 + this.path = scheme.path || ""; 83 + this.query = scheme.query || ""; 84 + this.fragment = scheme.fragment || ""; 85 + this.platform = scheme.platform; 86 + } else { 87 + this.scheme = scheme || ""; 88 + this.authority = authority || ""; 89 + this.path = path || ""; 90 + this.query = query || ""; 91 + this.fragment = fragment || ""; 92 + this.platform = platform; 93 + this.strict = strict; 94 + } 95 + } 96 + } 97 + 98 + class FieldChild extends FieldBase { 99 + _formatted = null; 100 + _fsPath = null; 101 + 102 + get fsPath() { 103 + if (!this._fsPath) { 104 + if ( 105 + this.path.charCodeAt(0) === 47 && 106 + ((this.path.charCodeAt(1) >= 65 && this.path.charCodeAt(1) <= 90) || 107 + (this.path.charCodeAt(1) >= 97 && this.path.charCodeAt(1) <= 122)) && 108 + this.path.charCodeAt(2) === 58 109 + ) { 110 + this._fsPath = this.path[1].toLowerCase() + this.path.substring(2); 111 + } else { 112 + this._fsPath = this.path; 113 + } 114 + } 115 + return this._fsPath; 116 + } 117 + 118 + toJSON() { 119 + return { 120 + scheme: this.scheme, 121 + authority: this.authority, 122 + path: this.path, 123 + query: this.query, 124 + fragment: this.fragment, 125 + platform: this.platform, 126 + _formatted: this._formatted, 127 + _fsPath: this._fsPath, 128 + }; 129 + } 130 + } 131 + 132 + const fieldUri = new FieldChild("file", "", "/Users/test/project", "", "", false, "posix"); 133 + 134 + assert.equal(fieldUri.scheme, "file"); 135 + assert.equal(fieldUri.authority, ""); 136 + assert.equal(fieldUri.path, "/Users/test/project"); 137 + assert.equal(fieldUri.query, ""); 138 + assert.equal(fieldUri.fragment, ""); 139 + assert.equal(fieldUri.platform, "posix"); 140 + assert.equal(fieldUri._formatted, null); 141 + assert.equal(fieldUri._fsPath, null); 142 + assert.equal(fieldUri.fsPath, "/Users/test/project"); 143 + assert.equal(fieldUri._fsPath, "/Users/test/project"); 144 + 145 + console.log("ok-fields");
+39
tests/test_legacy_accessors.js
··· 1 + let pass = true; 2 + 3 + const target = {}; 4 + let seen = 0; 5 + 6 + target.__defineSetter__('parseInt8', function (val) { 7 + seen = val ? 1 : -1; 8 + }); 9 + 10 + target.parseInt8 = true; 11 + if (seen !== 1) { 12 + console.log('FAIL: __defineSetter__ should install a working setter'); 13 + pass = false; 14 + } 15 + 16 + const setter = target.__lookupSetter__('parseInt8'); 17 + if (typeof setter !== 'function') { 18 + console.log('FAIL: __lookupSetter__ should return the setter function'); 19 + pass = false; 20 + } 21 + 22 + const proto = {}; 23 + proto.__defineGetter__('value', function () { 24 + return 42; 25 + }); 26 + 27 + const child = Object.create(proto); 28 + const getter = child.__lookupGetter__('value'); 29 + if (typeof getter !== 'function') { 30 + console.log('FAIL: __lookupGetter__ should find getters on the prototype chain'); 31 + pass = false; 32 + } 33 + 34 + if (child.value !== 42) { 35 + console.log('FAIL: getter installed with __defineGetter__ should be used'); 36 + pass = false; 37 + } 38 + 39 + if (pass) console.log('PASS');
+46
tests/test_property_error_messages.cjs
··· 1 + function assert(condition, message) { 2 + if (!condition) throw new Error(message); 3 + } 4 + 5 + function expectThrow(fn, label) { 6 + try { 7 + fn(); 8 + } catch (error) { 9 + return error; 10 + } 11 + throw new Error(`${label} did not throw`); 12 + } 13 + 14 + const dotError = expectThrow(() => { 15 + const value = undefined; 16 + return value.call; 17 + }, 'dot access'); 18 + 19 + assert( 20 + dotError.message === "Cannot read properties of undefined (reading 'call')", 21 + `unexpected dot access message: ${dotError.message}` 22 + ); 23 + 24 + const stringKey = 'digest'; 25 + const computedStringError = expectThrow(() => { 26 + const value = undefined; 27 + return value[stringKey]; 28 + }, 'computed string access'); 29 + 30 + assert( 31 + computedStringError.message === "Cannot read properties of undefined (reading 'digest')", 32 + `unexpected computed string message: ${computedStringError.message}` 33 + ); 34 + 35 + const numericKey = 0; 36 + const computedNumericError = expectThrow(() => { 37 + const value = undefined; 38 + return value[numericKey]; 39 + }, 'computed numeric access'); 40 + 41 + assert( 42 + computedNumericError.message === "Cannot read properties of undefined (reading '0')", 43 + `unexpected computed numeric message: ${computedNumericError.message}` 44 + ); 45 + 46 + console.log('property error message test passed');
+7
tests/test_require_exports_conditions.cjs
··· 1 + const pkg = require("cond-exports"); 2 + 3 + if (pkg.branch !== "require") { 4 + console.log("FAIL: require() should prefer package exports.require, got", pkg.branch); 5 + } else { 6 + console.log("PASS"); 7 + }
+15
tests/test_static.js
··· 115 115 test('static computed value TWO_PI', MathHelper.TWO_PI, 3.14159 * 2); 116 116 test('static method using static property', MathHelper.circleArea(1), 3.14159); 117 117 118 + console.log('\n=== Named Class Self-Reference in Static Fields ==='); 119 + 120 + const FilterTranslator = class _FilterTranslator { 121 + static LOGICAL_OPERATORS = ["$and", "$or"]; 122 + static DEFAULT_OPERATORS = { 123 + logical: _FilterTranslator.LOGICAL_OPERATORS 124 + }; 125 + }; 126 + 127 + test( 128 + 'named class expression static self-reference', 129 + FilterTranslator.DEFAULT_OPERATORS.logical[1], 130 + "$or" 131 + ); 132 + 118 133 console.log('\n=== Summary ==='); 119 134 console.log('Passed:', passed); 120 135 console.log('Failed:', failed);
+11
tests/test_util_is_deep_strict_equal.cjs
··· 1 + const { isDeepStrictEqual } = require('node:util'); 2 + 3 + function assert(cond, msg) { 4 + if (!cond) throw new Error(msg); 5 + } 6 + 7 + assert(isDeepStrictEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }) === true, 'expected matching objects to be deeply strict equal'); 8 + assert(isDeepStrictEqual({ a: 1 }, { a: '1' }) === false, 'expected strict comparison for nested values'); 9 + assert(isDeepStrictEqual([1, { x: 2 }], [1, { x: 3 }]) === false, 'expected differing nested arrays to be unequal'); 10 + 11 + console.log('PASS');
+37
tests/test_util_parse_args.cjs
··· 1 + const { parseArgs } = require('node:util'); 2 + 3 + function assert(cond, msg) { 4 + if (!cond) throw new Error(msg); 5 + } 6 + 7 + let result = parseArgs({ 8 + args: ['--prompt', 'hello', '-c', 'positional'], 9 + options: { 10 + prompt: { type: 'string', short: 'p' }, 11 + continue: { type: 'boolean', short: 'c', default: false }, 12 + format: { type: 'string', default: 'default' } 13 + }, 14 + strict: false, 15 + allowPositionals: true 16 + }); 17 + 18 + assert(result.values.prompt === 'hello', 'expected string long option'); 19 + assert(result.values.continue === true, 'expected boolean short option'); 20 + assert(result.values.format === 'default', 'expected default string option'); 21 + assert(Array.isArray(result.positionals), 'expected positionals array'); 22 + assert(result.positionals.length === 1 && result.positionals[0] === 'positional', 'expected positional passthrough'); 23 + 24 + result = parseArgs({ 25 + args: ['-phello', '--format=json'], 26 + options: { 27 + prompt: { type: 'string', short: 'p' }, 28 + format: { type: 'string' } 29 + }, 30 + strict: false, 31 + allowPositionals: true 32 + }); 33 + 34 + assert(result.values.prompt === 'hello', 'expected attached short string value'); 35 + assert(result.values.format === 'json', 'expected inline long string value'); 36 + 37 + console.log('PASS');