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.

fix use-after-free by scanning WeakMap entries during mark phase

additional correctness fixes:
- Mark await_coro in gc_mark_promise_handler via gc_mark_coroutine
- Defer active_async_coro unlinking until after js_resolve/reject_promise
in sv_start_tla, sv_start_async_closure, and resume_coroutine_if_suspended,
so the coroutine's VM is scanned if GC triggers during promise resolution
- Walk prototype chain in js_for_in_keys for spec-compliant for...in
- Refactor Object.assign to use an is_enumerable_prop helper

+1161 -331
+1 -1
examples/npm/tar/index.js
··· 20 20 console.log('FAIL โ€” archive not created'); 21 21 process.exit(1); 22 22 } 23 - console.log(' OK โ€” archive created (%d bytes)', fs.statSync(archive).size); 23 + console.log(` OK โ€” archive created (${fs.statSync(archive).size} bytes)`); 24 24 25 25 console.log('2. Extracting tarball...'); 26 26 await tar.extract({ file: archive, cwd: dest });
+3 -1
include/ant.h
··· 90 90 ant_value_t js_mksym_for(ant_t *, const char *key); 91 91 ant_value_t js_symbol_to_string(ant_t *js, ant_value_t sym); 92 92 ant_value_t js_get_sym(ant_t *, ant_value_t obj, ant_value_t sym); 93 + ant_value_t js_get_sym_with_receiver(ant_t *, ant_value_t obj, ant_value_t sym, ant_value_t receiver); 93 94 94 95 ant_value_t js_mkobj(ant_t *); 95 96 ant_value_t js_mkobj_with_inobj_limit(ant_t *, uint8_t inobj_limit); ··· 142 143 } ant_iter_t; 143 144 144 145 ant_iter_t js_prop_iter_begin(ant_t *js, ant_value_t obj); 146 + void js_prop_iter_end(ant_iter_t *iter); 145 147 146 148 bool js_prop_iter_next(ant_iter_t *iter, const char **key, size_t *key_len, ant_value_t *value); 147 - void js_prop_iter_end(ant_iter_t *iter); 149 + bool js_prop_iter_next_val(ant_iter_t *iter, ant_value_t *key_out, ant_value_t *value); 148 150 149 151 ant_value_t js_obj_to_func(ant_value_t obj); 150 152 ant_value_t js_obj_to_func_ex(ant_value_t obj, uint8_t flags);
+2 -1
include/common.h
··· 113 113 BRAND_WASM_MEMORY, 114 114 BRAND_WASM_TABLE, 115 115 BRAND_WASM_TAG, 116 - BRAND_WASM_EXCEPTION 116 + BRAND_WASM_EXCEPTION, 117 + BRAND_DATE 117 118 } object_brand_id_t; 118 119 119 120 static inline void *mantissa_chk(void *p, const char *func) {
+1
include/gc/modules.h
··· 30 30 void gc_mark_compression_streams(ant_t *js, gc_mark_fn mark); 31 31 void gc_mark_zlib(ant_t *js, gc_mark_fn mark); 32 32 void gc_mark_wasm(ant_t *js, gc_mark_fn mark); 33 + void gc_mark_napi(ant_t *js, gc_mark_fn mark); 33 34 void gc_mark_abort_signal_object(ant_t *js, ant_value_t signal, gc_mark_fn mark); 34 35 35 36 #endif
+1
include/modules/date.h
··· 70 70 } date_method_entry_t; 71 71 72 72 void init_date_module(void); 73 + bool is_date_instance(ant_value_t value); 73 74 74 75 ant_value_t get_date_string( 75 76 ant_t *js, ant_value_t this_val,
+12
include/modules/events.h
··· 15 15 ant_value_t listener, bool once 16 16 ); 17 17 18 + bool eventemitter_add_listener_val( 19 + ant_t *js, 20 + ant_value_t target, ant_value_t key, 21 + ant_value_t listener, bool once 22 + ); 23 + 18 24 bool eventemitter_emit_args( 19 25 ant_t *js, 20 26 ant_value_t target, const char *event_type, 27 + ant_value_t *args, int nargs 28 + ); 29 + 30 + bool eventemitter_emit_args_val( 31 + ant_t *js, 32 + ant_value_t target, ant_value_t key, 21 33 ant_value_t *args, int nargs 22 34 ); 23 35
+1
include/silver/opcode.h
··· 162 162 OP_DEF( TAIL_CALL_METHOD, 3, 2, 0, npop) 163 163 OP_DEF( NEW, 3, 2, 1, npop) /* func new.target args -> obj */ 164 164 OP_DEF( APPLY, 3, 3, 1, u16) /* func this [args] -> result */ 165 + OP_DEF( NEW_APPLY, 3, 2, 1, u16) /* func new.target [args] -> obj */ 165 166 OP_DEF( EVAL, 5, 1, 1, npop) /* direct eval */ 166 167 OP_DEF( RETURN, 1, 1, 0, none) 167 168 OP_DEF( RETURN_UNDEF, 1, 0, 0, none)
+121 -88
src/ant.c
··· 1140 1140 1141 1141 // todo: split into smaller functions 1142 1142 static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { 1143 - ant_value_t obj_proto = js_get_proto(js, obj); 1144 - ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); 1145 - if (obj_proto == date_proto) return strdate(js, obj, buf, len); 1143 + if (is_date_instance(obj)) return strdate(js, obj, buf, len); 1146 1144 1147 1145 int ref = get_circular_ref(obj); 1148 1146 if (ref) return ref > 0 ? (size_t) snprintf(buf, len, "[Circular *%d]", ref) : cpy(buf, len, "[Circular]", 10); ··· 2439 2437 ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uint8_t attrs) { 2440 2438 obj = js_as_obj(obj); 2441 2439 ant_object_t *ptr = js_obj_ptr(obj); 2440 + 2442 2441 if (!ptr || !ptr->shape) return js_mkerr(js, "invalid object"); 2443 - 2444 2442 if (!attrs) attrs = ANT_PROP_ATTR_DEFAULT; 2445 2443 2446 2444 uint32_t slot = 0; ··· 5432 5430 return js_mkundef(); 5433 5431 } 5434 5432 5433 + static inline ant_value_t for_in_keys_collect_chain( 5434 + ant_t *js, ant_value_t out, ant_value_t seen, ant_value_t obj 5435 + ) { 5436 + ant_value_t cur = obj; 5437 + GC_ROOT_PIN(js, cur); 5438 + 5439 + for (int depth = 0; is_object_type(cur) && depth < MAX_PROTO_CHAIN_DEPTH; depth++) { 5440 + GC_ROOT_SAVE(iter_mark, js); 5441 + ant_value_t cur_keys = object_enum(js, cur, OBJ_ENUM_KEYS); 5442 + GC_ROOT_PIN(js, cur_keys); 5443 + 5444 + if (is_err(cur_keys)) { GC_ROOT_RESTORE(js, iter_mark); return cur_keys; } 5445 + if (vtype(cur_keys) != T_ARR) goto next; 5446 + 5447 + ant_offset_t len = js_arr_len(js, cur_keys); 5448 + for (ant_offset_t i = 0; i < len; i++) { 5449 + ant_value_t key = js_arr_get(js, cur_keys, i); 5450 + GC_ROOT_PIN(js, key); 5451 + ant_value_t r = for_in_keys_add(js, out, seen, key); 5452 + if (is_err(r)) { GC_ROOT_RESTORE(js, iter_mark); return r; } 5453 + } 5454 + 5455 + next: 5456 + GC_ROOT_RESTORE(js, iter_mark); 5457 + ant_value_t proto = js_get_proto(js, cur); 5458 + if (!is_object_type(proto)) break; 5459 + cur = proto; 5460 + } 5461 + return out; 5462 + } 5463 + 5435 5464 ant_value_t js_for_in_keys(ant_t *js, ant_value_t obj) { 5436 5465 GC_ROOT_SAVE(root_mark, js); 5437 5466 uint8_t t = vtype(obj); ··· 5464 5493 } 5465 5494 5466 5495 if (t != T_OBJ && t != T_ARR && t != T_FUNC) goto done; 5467 - ant_value_t own_keys = object_enum(js, obj, OBJ_ENUM_KEYS); 5468 - 5469 - GC_ROOT_PIN(js, own_keys); 5470 - if (is_err(own_keys)) { 5471 - result = own_keys; 5472 - goto done; 5473 - } 5474 - 5475 - if (vtype(own_keys) != T_ARR) goto done; 5476 - ant_offset_t own_len = js_arr_len(js, own_keys); 5477 - 5478 - for (ant_offset_t i = 0; i < own_len; i++) { 5479 - ant_value_t key = js_arr_get(js, own_keys, i); 5480 - GC_ROOT_PIN(js, key); 5481 - result = for_in_keys_add(js, out, seen, key); 5482 - if (is_err(result)) goto done; 5483 - result = out; 5484 - } 5496 + result = for_in_keys_collect_chain(js, out, seen, obj); 5485 5497 5486 5498 done: 5487 5499 GC_ROOT_RESTORE(js, root_mark); ··· 5684 5696 return result; 5685 5697 } 5686 5698 5699 + // TODO: decompose this huge function into small pieces 5687 5700 static ant_value_t builtin_object_defineProperty(ant_t *js, ant_value_t *args, int nargs) { 5688 5701 if (nargs < 3) return js_mkerr(js, "Object.defineProperty requires 3 arguments"); 5689 5702 ··· 6003 6016 return obj; 6004 6017 } 6005 6018 6019 + static inline bool is_enumerable_prop( 6020 + ant_t *js, ant_value_t source, ant_object_t *source_ptr, 6021 + ant_value_t prop_key, uint32_t slot 6022 + ) { 6023 + if (vtype(prop_key) == T_STR) { 6024 + size_t klen = 0; 6025 + 6026 + const char *kstr = js_getstr(js, prop_key, &klen); 6027 + if (is_internal_prop(kstr, klen)) return false; 6028 + 6029 + if (!source_ptr || source_ptr->is_exotic) { 6030 + descriptor_entry_t *desc = lookup_descriptor(js_as_obj(source), kstr, klen); 6031 + return !desc || desc->enumerable; 6032 + }} 6033 + 6034 + return (ant_shape_get_attrs(source_ptr->shape, slot) & ANT_PROP_ATTR_ENUMERABLE) != 0; 6035 + } 6036 + 6006 6037 static ant_value_t builtin_object_assign(ant_t *js, ant_value_t *args, int nargs) { 6007 6038 if (nargs == 0) return js_mkerr(js, "Object.assign requires at least 1 argument"); 6008 6039 ··· 6027 6058 if (st != T_OBJ && st != T_ARR && st != T_FUNC) continue; 6028 6059 6029 6060 ant_iter_t iter = js_prop_iter_begin(js, source); 6030 - const char *key = NULL; 6031 - size_t key_len = 0; 6061 + ant_object_t *source_ptr = js_obj_ptr(js_as_obj(source)); 6062 + 6063 + ant_value_t prop_key = js_mkundef(); 6032 6064 ant_value_t val = js_mkundef(); 6033 - 6034 - while (js_prop_iter_next(&iter, &key, &key_len, &val)) { 6035 - if (is_internal_prop(key, key_len)) continue; 6036 - 6037 - ant_object_t *source_ptr = js_obj_ptr(js_as_obj(source)); 6038 - bool should_copy = true; 6039 - if (source_ptr && !source_ptr->is_exotic) { 6040 - const char *interned = intern_string(key, key_len); 6041 - if (interned && source_ptr->shape) { 6042 - int32_t slot = ant_shape_lookup_interned(source_ptr->shape, interned); 6043 - if (slot >= 0) { 6044 - should_copy = (ant_shape_get_attrs(source_ptr->shape, (uint32_t)slot) & ANT_PROP_ATTR_ENUMERABLE) != 0; 6045 - } 6046 - } 6047 - } else { 6048 - descriptor_entry_t *desc = lookup_descriptor(js_as_obj(source), key, key_len); 6049 - if (desc) should_copy = desc->enumerable; 6050 - } 6051 - 6052 - if (should_copy) { 6053 - ant_value_t key_str = js_mkstr(js, key, key_len); 6054 - js_setprop(js, as_obj, key_str, val); 6055 - } 6056 - } 6065 + 6066 + while (js_prop_iter_next_val(&iter, &prop_key, &val)) if ( 6067 + is_enumerable_prop(js, source, source_ptr, prop_key, (uint32_t)(iter.off - 1)) 6068 + ) js_setprop(js, as_obj, prop_key, val); 6069 + 6057 6070 js_prop_iter_end(&iter); 6058 6071 } 6059 6072 ··· 7128 7141 return accumulator; 7129 7142 } 7130 7143 7131 - static void flat_helper(ant_t *js, ant_value_t arr, ant_value_t result, ant_offset_t *result_idx, int depth) { 7144 + static inline void flat_helper(ant_t *js, ant_value_t arr, ant_value_t result, ant_offset_t *result_idx, int depth) { 7132 7145 ant_offset_t len = get_array_length(js, arr); 7133 7146 if (len == 0) return; 7134 7147 ··· 7136 7149 if (!arr_has(js, arr, i)) continue; 7137 7150 ant_value_t val = arr_get(js, arr, i); 7138 7151 7139 - if (depth > 0 && (vtype(val) == T_ARR || vtype(val) == T_OBJ)) { 7140 - flat_helper(js, val, result, result_idx, depth - 1); 7141 - } else { arr_set(js, result, *result_idx, val); (*result_idx)++; } 7152 + if (depth > 0 && vtype(val) == T_ARR) flat_helper(js, val, result, result_idx, depth - 1); 7153 + else { arr_set(js, result, *result_idx, val); (*result_idx)++; } 7142 7154 } 7143 7155 } 7144 7156 ··· 7352 7364 ant_value_t call_args[3] = { elem, tov((double)i), arr }; 7353 7365 ant_value_t mapped = sv_vm_call(js->vm, js, callback, this_arg, call_args, 3, NULL, false); 7354 7366 if (is_err(mapped)) return mapped; 7355 - 7356 - if (vtype(mapped) == T_ARR || vtype(mapped) == T_OBJ) { 7357 - ant_offset_t m_len = get_array_length(js, mapped); 7358 - for (ant_offset_t j = 0; j < m_len; j++) { 7359 - ant_value_t m_elem = arr_get(js, mapped, j); 7360 - arr_set(js, result, result_idx, m_elem); 7361 - result_idx++; 7362 - } 7363 - } else { 7364 - arr_set(js, result, result_idx, mapped); 7365 - result_idx++; 7366 - } 7367 + if (vtype(mapped) == T_ARR) flat_helper(js, mapped, result, &result_idx, 0); 7368 + else arr_set(js, result, result_idx++, mapped); 7367 7369 } 7368 7370 7369 7371 return mkval(T_ARR, vdata(result)); ··· 12858 12860 } else mkprop(js, obj, sym, val, 0); 12859 12861 } 12860 12862 12861 - ant_value_t js_get_sym(ant_t *js, ant_value_t obj, ant_value_t sym) { 12863 + ant_value_t js_get_sym_with_receiver(ant_t *js, ant_value_t obj, ant_value_t sym, ant_value_t receiver) { 12862 12864 if (vtype(sym) != T_SYMBOL) return js_mkundef(); 12863 12865 ant_offset_t sym_off = (ant_offset_t)vdata(sym); 12864 - 12865 - ant_value_t receiver = obj; 12866 + 12866 12867 if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); 12867 12868 uint8_t ot = vtype(obj); 12869 + 12868 12870 if (!is_object_type(obj)) { 12869 12871 if (ot == T_STR || ot == T_NUM || ot == T_BOOL || ot == T_BIGINT || ot == T_SYMBOL) { 12870 12872 ant_value_t proto = get_prototype_for_type(js, ot); 12871 12873 if (!is_object_type(proto)) return js_mkundef(); 12872 12874 obj = js_as_obj(proto); 12873 - } else { 12874 - return js_mkundef(); 12875 - } 12876 - } else { 12877 - obj = js_as_obj(obj); 12878 - } 12879 - 12880 - if (is_proxy(obj)) return proxy_get_val(js, obj, sym); 12881 - 12875 + } else return js_mkundef(); 12876 + } else obj = js_as_obj(obj); 12877 + 12878 + if (is_proxy(obj)) 12879 + return proxy_get_val(js, obj, sym); 12880 + 12882 12881 ant_value_t cur = obj; 12883 12882 proto_overflow_guard_t guard; 12884 12883 proto_overflow_guard_init(&guard); 12884 + 12885 12885 while (is_object_type(cur)) { 12886 12886 ant_value_t cur_obj = js_as_obj(cur); 12887 12887 prop_meta_t meta; ··· 12905 12905 ant_offset_t off = lkp_sym_proto(js, obj, sym_off); 12906 12906 if (off == 0) return js_mkundef(); 12907 12907 return propref_load(js, off); 12908 + } 12909 + 12910 + ant_value_t js_get_sym(ant_t *js, ant_value_t obj, ant_value_t sym) { 12911 + return js_get_sym_with_receiver(js, obj, sym, obj); 12908 12912 } 12909 12913 12910 12914 static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t *out) { ··· 13291 13295 return js_mkerr_typed(js, JS_ERR_TYPE, "%s is not a function", typestr(vtype(func))); 13292 13296 } 13293 13297 13294 - ant_iter_t js_prop_iter_begin(ant_t *js, ant_value_t obj) { 13295 - typedef struct { 13296 - ant_t *js; 13297 - ant_object_t *obj; 13298 - uint32_t index; 13299 - } prop_iter_ctx_t; 13298 + typedef struct { 13299 + ant_t *js; 13300 + ant_object_t *obj; 13301 + uint32_t index; 13302 + } prop_iter_ctx_t; 13300 13303 13304 + ant_iter_t js_prop_iter_begin(ant_t *js, ant_value_t obj) { 13301 13305 ant_iter_t iter = {.ctx = NULL, .off = 0}; 13302 13306 uint8_t t = vtype(obj); 13303 13307 if (t != T_OBJ && t != T_ARR && t != T_FUNC) return iter; 13304 13308 13305 13309 prop_iter_ctx_t *ctx = calloc(1, sizeof(*ctx)); 13306 13310 if (!ctx) return iter; 13311 + 13307 13312 ctx->js = js; 13308 13313 ctx->obj = js_obj_ptr(js_as_obj(obj)); 13309 13314 ctx->index = 0; 13315 + 13310 13316 if (!ctx->obj || !ctx->obj->shape) { 13311 13317 free(ctx); 13312 13318 return iter; 13313 13319 } 13320 + 13314 13321 iter.ctx = ctx; 13315 13322 return iter; 13316 13323 } 13317 13324 13318 13325 bool js_prop_iter_next(ant_iter_t *iter, const char **key, size_t *key_len, ant_value_t *value) { 13319 - typedef struct { 13320 - ant_t *js; 13321 - ant_object_t *obj; 13322 - uint32_t index; 13323 - } prop_iter_ctx_t; 13324 - 13325 13326 if (!iter || !iter->ctx) return false; 13326 13327 prop_iter_ctx_t *ctx = (prop_iter_ctx_t *)iter->ctx; 13328 + 13327 13329 ant_object_t *obj = ctx->obj; 13328 13330 if (!obj || !obj->shape) return false; 13329 13331 ··· 13339 13341 *key = prop->key.interned; 13340 13342 if (key_len) *key_len = strlen(prop->key.interned); 13341 13343 } 13344 + 13342 13345 if (value) *value = ant_object_prop_get_unchecked(obj, i); 13343 13346 iter->off = i + 1; 13347 + 13348 + return true; 13349 + } 13350 + 13351 + return false; 13352 + } 13353 + 13354 + bool js_prop_iter_next_val(ant_iter_t *iter, ant_value_t *key_out, ant_value_t *value) { 13355 + if (!iter || !iter->ctx) return false; 13356 + prop_iter_ctx_t *ctx = (prop_iter_ctx_t *)iter->ctx; 13357 + 13358 + ant_object_t *obj = ctx->obj; 13359 + if (!obj || !obj->shape) return false; 13360 + uint32_t count = ant_shape_count(obj->shape); 13361 + 13362 + while (ctx->index < count) { 13363 + uint32_t i = ctx->index++; 13364 + const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, i); 13365 + 13366 + if (!prop) continue; 13367 + if (i >= obj->prop_count) continue; 13368 + 13369 + if (key_out) { 13370 + if (prop->type == ANT_SHAPE_KEY_SYMBOL) *key_out = mkval(T_SYMBOL, prop->key.sym_off); 13371 + else *key_out = js_mkstr(ctx->js, prop->key.interned, strlen(prop->key.interned)); 13372 + } 13373 + 13374 + if (value) *value = ant_object_prop_get_unchecked(obj, i); 13375 + iter->off = i + 1; 13376 + 13344 13377 return true; 13345 13378 } 13346 13379
+36 -23
src/gc/objects.c
··· 122 122 } 123 123 124 124 #define GC_MARK_STACK_INIT 4096 125 - void gc_mark_value(ant_t *js, ant_value_t v); 126 125 127 - static inline void gc_mark_promise_handler(ant_t *js, const promise_handler_t *h) { 128 - if (!h) return; 129 - gc_mark_value(js, h->onFulfilled); 130 - gc_mark_value(js, h->onRejected); 131 - gc_mark_value(js, h->nextPromise); 132 - } 126 + static ant_object_t **gc_mark_stack = NULL; 127 + static void gc_mark_promise_handlers(ant_t *js, ant_promise_state_t *pd); 133 128 134 - static inline void gc_mark_promise_handlers(ant_t *js, ant_promise_state_t *pd) { 135 - if (!pd) return; 136 - 137 - if (pd->handler_count == 1) { 138 - gc_mark_promise_handler(js, &pd->inline_handler); 139 - return; 140 - } 141 - 142 - if (pd->handler_count <= 1 || !pd->handlers) return; 143 - 144 - promise_handler_t *h = NULL; 145 - while ((h = (promise_handler_t *)utarray_next(pd->handlers, h))) 146 - gc_mark_promise_handler(js, h); 147 - } 148 - 149 - static ant_object_t **gc_mark_stack = NULL; 150 129 static size_t gc_mark_sp = 0; 151 130 static size_t gc_mark_cap = 0; 152 131 ··· 259 238 gc_mark_value(js, e->key_val); 260 239 gc_mark_value(js, e->value); 261 240 }} 241 + } else if (obj->type_tag == T_WEAKMAP) { 242 + weakmap_entry_t **head = (weakmap_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); 243 + if (head) { 244 + weakmap_entry_t *e, *tmp; 245 + HASH_ITER(hh, *head, e, tmp) { 246 + gc_mark_value(js, e->key_obj); 247 + gc_mark_value(js, e->value); 248 + }} 262 249 } else if (obj->type_tag == T_SET) { 263 250 set_entry_t **head = (set_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); 264 251 if (head) { ··· 433 420 gc_mark_value(js, c->new_target); 434 421 } 435 422 423 + static inline void gc_mark_promise_handler(ant_t *js, const promise_handler_t *h) { 424 + if (!h) return; 425 + 426 + gc_mark_value(js, h->onFulfilled); 427 + gc_mark_value(js, h->onRejected); 428 + gc_mark_value(js, h->nextPromise); 429 + 430 + if (h->await_coro) gc_mark_coroutine(js, h->await_coro); 431 + } 432 + 433 + static inline void gc_mark_promise_handlers(ant_t *js, ant_promise_state_t *pd) { 434 + if (!pd) return; 435 + 436 + if (pd->handler_count == 1) { 437 + gc_mark_promise_handler(js, &pd->inline_handler); 438 + return; 439 + } 440 + 441 + if (pd->handler_count <= 1 || !pd->handlers) return; 442 + promise_handler_t *h = NULL; 443 + 444 + while ((h = (promise_handler_t *)utarray_next(pd->handlers, h))) 445 + gc_mark_promise_handler(js, h); 446 + } 447 + 436 448 static void gc_mark_roots(ant_t *js) { 437 449 gc_scan_vm_stack(js, js->vm); 438 450 ··· 486 498 gc_mark_compression_streams(js, gc_mark_value); 487 499 gc_mark_zlib(js, gc_mark_value); 488 500 gc_mark_wasm(js, gc_mark_value); 501 + gc_mark_napi(js, gc_mark_value); 489 502 490 503 for ( 491 504 ant_object_t *obj = g_pending_promises;
+6
src/modules/collections.c
··· 565 565 HASH_ADD(hh, *wm_ptr, key_obj, sizeof(ant_value_t), entry); 566 566 } 567 567 568 + ant_object_t *wm_obj = js_obj_ptr(this_val); 569 + if (wm_obj) { 570 + gc_write_barrier(js, wm_obj, key_obj); 571 + gc_write_barrier(js, wm_obj, args[1]); 572 + } 573 + 568 574 return this_val; 569 575 } 570 576
+15 -16
src/modules/date.c
··· 80 80 || t == T_BIGINT; 81 81 } 82 82 83 - static bool is_date_instance(ant_t *js, ant_value_t value) { 83 + bool is_date_instance(ant_value_t value) { 84 84 if (!is_object_type(value)) return false; 85 - ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); 86 - 87 - if (!is_object_type(date_proto)) return false; 88 - if (value == date_proto) return true; 89 - 90 - return proto_chain_contains(js, value, date_proto); 85 + ant_value_t brand = js_get_slot(js_as_obj(value), SLOT_BRAND); 86 + return vtype(brand) == T_NUM && (int)js_getnum(brand) == BRAND_DATE; 91 87 } 92 88 93 89 static bool date_this_time_value(ant_t *js, ant_value_t this_val, double *out, ant_value_t *err) { 94 - if (!is_date_instance(js, this_val)) { 90 + if (!is_date_instance(this_val)) { 95 91 if (err) *err = js_mkerr_typed(js, JS_ERR_TYPE, "not a Date object"); 96 92 return false; 97 93 } ··· 102 98 } 103 99 104 100 static ant_value_t date_set_this_time_value(ant_t *js, ant_value_t this_val, double v) { 105 - if (!is_date_instance(js, this_val)) { 101 + if (!is_date_instance(this_val)) { 106 102 return js_mkerr_typed(js, JS_ERR_TYPE, "not a Date object"); 107 103 } 108 104 ··· 763 759 764 760 int fields[9]; 765 761 date_fields_t fields1 = {0}; 762 + 766 763 bool is_local = false; 767 - bool ok = parse_isostring((const uint8_t *)buf, fields, &is_local) 768 - || parse_otherstring((const uint8_t *)buf, fields, &is_local); 764 + bool ok = 765 + parse_isostring((const uint8_t *)buf, fields, &is_local) || 766 + parse_otherstring((const uint8_t *)buf, fields, &is_local); 769 767 770 768 free(buf); 771 769 ··· 795 793 fields1.second = (double)fields[5]; 796 794 fields1.millisecond = (double)fields[6]; 797 795 *out_ms = date_make_fields(&fields1, is_local ? 1 : 0) - (double)fields[8] * 60000.0; 796 + 798 797 return true; 799 798 } 800 799 ··· 818 817 val = (double)date_now(); 819 818 } else if (nargs == 1) { 820 819 ant_value_t input = args[0]; 821 - 822 - if (is_object_type(input) && is_date_instance(js, input)) { 820 + 821 + if (is_object_type(input) && is_date_instance(input)) { 823 822 ant_value_t tv = js_get_slot(js_as_obj(input), SLOT_DATA); 824 823 val = (vtype(tv) == T_NUM) ? tod(tv) : JS_NAN; 825 824 val = date_timeclip(val); ··· 828 827 if (is_err(prim)) return prim; 829 828 if (vtype(prim) == T_STR) { 830 829 if (!date_parse_string_to_ms(js, prim, &val)) return js_mkerr_typed(js, JS_ERR_INTERNAL, "Date parse failed"); 831 - } else { 832 - val = js_to_number(js, prim); 833 - } 830 + } else val = js_to_number(js, prim); 834 831 val = date_timeclip(val); 835 832 } 836 833 } else { ··· 852 849 } 853 850 854 851 js_set_slot(js_as_obj(js->this_val), SLOT_DATA, tov(val)); 852 + js_set_slot(js_as_obj(js->this_val), SLOT_BRAND, js_mknum(BRAND_DATE)); 853 + 855 854 return js->this_val; 856 855 } 857 856
+170 -85
src/modules/events.c
··· 56 56 }; 57 57 58 58 typedef struct { 59 - UT_array *listeners; 60 - char *event_type; 61 - UT_hash_handle hh; 59 + unsigned char buf[48]; 60 + unsigned char *ptr; 61 + size_t len; 62 + } evt_key_t; 63 + 64 + typedef struct { 65 + UT_array *listeners; 66 + ant_value_t js_key; 67 + unsigned char *hash_key; 68 + size_t hash_key_len; 69 + UT_hash_handle hh; 62 70 } EventType; 63 71 64 72 typedef struct emitter_reg { ··· 69 77 static EventType *global_events = NULL; 70 78 static emitter_reg_t *emitter_registry = NULL; 71 79 72 - static EventType *make_event_type(const char *event_type) { 73 - size_t etlen = strlen(event_type); 74 - EventType *evt = ant_calloc(sizeof(EventType) + etlen + 1); 80 + static EventType *make_event_type(ant_value_t js_key, const evt_key_t *ek) { 81 + EventType *evt = ant_calloc(sizeof(EventType) + ek->len); 75 82 if (!evt) return NULL; 76 - evt->event_type = (char *)(evt + 1); 77 - memcpy(evt->event_type, event_type, etlen + 1); 83 + evt->js_key = js_key; 84 + evt->hash_key = (unsigned char *)(evt + 1); 85 + evt->hash_key_len = ek->len; 86 + memcpy(evt->hash_key, ek->ptr, ek->len); 78 87 utarray_new(evt->listeners, &event_listener_icd); 79 88 return evt; 80 89 } 81 90 91 + static EventType *evt_find(EventType *table, const evt_key_t *ek) { 92 + EventType *evt = NULL; 93 + HASH_FIND(hh, table, ek->ptr, ek->len, evt); 94 + return evt; 95 + } 96 + 97 + static EventType *evt_find_or_create(EventType **table, ant_value_t js_key, const evt_key_t *ek) { 98 + EventType *evt = evt_find(*table, ek); 99 + if (!evt) { 100 + evt = make_event_type(js_key, ek); 101 + if (!evt) return NULL; 102 + HASH_ADD_KEYPTR(hh, *table, evt->hash_key, evt->hash_key_len, evt); 103 + } 104 + return evt; 105 + } 106 + 107 + static void evt_key_reset(evt_key_t *k) { 108 + k->ptr = k->buf; 109 + k->len = 0; 110 + } 111 + 112 + static void evt_key_free(evt_key_t *k) { 113 + if (k->ptr != k->buf) free(k->ptr); 114 + evt_key_reset(k); 115 + } 116 + 117 + static bool evt_key_init(ant_t *js, ant_value_t arg, evt_key_t *out) { 118 + evt_key_reset(out); 119 + uint8_t tag = (uint8_t)vtype(arg); 120 + 121 + if (tag == T_STR) { 122 + size_t slen = 0; 123 + const char *s = js_getstr(js, arg, &slen); 124 + out->len = 1 + slen; 125 + 126 + if (out->len > sizeof(out->buf)) { 127 + out->ptr = malloc(out->len); 128 + if (!out->ptr) { evt_key_reset(out); return false; } 129 + } 130 + 131 + out->ptr[0] = tag; 132 + if (slen) memcpy(out->ptr + 1, s, slen); 133 + 134 + return true; 135 + } 136 + 137 + if (tag == T_SYMBOL) { 138 + out->len = 1 + sizeof(ant_value_t); 139 + out->ptr[0] = tag; 140 + memcpy(out->ptr + 1, &arg, sizeof(ant_value_t)); 141 + return true; 142 + } 143 + 144 + return false; 145 + } 146 + 82 147 static EventType **get_or_create_emitter_events(ant_t *js, ant_value_t this_obj) { 83 148 ant_value_t slot = js_get_slot(this_obj, SLOT_DATA); 84 149 if (vtype(slot) == T_UNDEF) { ··· 98 163 return (EventType **)(uintptr_t)js_getnum(slot); 99 164 } 100 165 101 - static EventType *find_or_create_global_event_type(const char *event_type) { 102 - EventType *evt = NULL; 103 - HASH_FIND_STR(global_events, event_type, evt); 104 - if (!evt) { 105 - evt = make_event_type(event_type); 106 - if (!evt) return NULL; 107 - HASH_ADD_KEYPTR(hh, global_events, evt->event_type, strlen(evt->event_type), evt); 108 - } 166 + static EventType *find_or_create_global_event_type(ant_t *js, ant_value_t js_key) { 167 + evt_key_t ek; 168 + if (!evt_key_init(js, js_key, &ek)) return NULL; 169 + EventType *evt = evt_find_or_create(&global_events, js_key, &ek); 170 + evt_key_free(&ek); 109 171 return evt; 110 172 } 111 173 112 - static EventType *find_global_event_type(const char *event_type) { 113 - EventType *evt = NULL; 114 - HASH_FIND_STR(global_events, event_type, evt); 174 + static EventType *find_global_event_type(ant_t *js, ant_value_t js_key) { 175 + evt_key_t ek; 176 + if (!evt_key_init(js, js_key, &ek)) return NULL; 177 + EventType *evt = evt_find(global_events, &ek); 178 + evt_key_free(&ek); 115 179 return evt; 116 180 } 117 181 118 - static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 182 + static EventType *find_or_create_emitter_event_type(ant_t *js, ant_value_t this_obj, ant_value_t js_key) { 119 183 EventType **events = get_or_create_emitter_events(js, this_obj); 120 184 if (!events) return NULL; 121 - EventType *evt = NULL; 122 - HASH_FIND_STR(*events, event_type, evt); 123 - if (!evt) { 124 - evt = make_event_type(event_type); 125 - if (!evt) return NULL; 126 - HASH_ADD_KEYPTR(hh, *events, evt->event_type, strlen(evt->event_type), evt); 127 - } 185 + evt_key_t ek; 186 + if (!evt_key_init(js, js_key, &ek)) return NULL; 187 + EventType *evt = evt_find_or_create(events, js_key, &ek); 188 + evt_key_free(&ek); 128 189 return evt; 129 190 } 130 191 131 - static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, const char *event_type) { 192 + static EventType *find_emitter_event_type(ant_t *js, ant_value_t this_obj, ant_value_t js_key) { 132 193 EventType **events = get_or_create_emitter_events(js, this_obj); 133 194 if (!events) return NULL; 134 - EventType *evt = NULL; 135 - HASH_FIND_STR(*events, event_type, evt); 195 + evt_key_t ek; 196 + if (!evt_key_init(js, js_key, &ek)) return NULL; 197 + EventType *evt = evt_find(*events, &ek); 198 + evt_key_free(&ek); 136 199 return evt; 200 + } 201 + 202 + static inline ant_value_t evt_key_from_arg(ant_value_t arg) { 203 + uint8_t t = vtype(arg); 204 + return (t == T_STR || t == T_SYMBOL) ? arg : 0; 137 205 } 138 206 139 207 static void js_init_event_obj(ant_t *js, ant_value_t obj, ant_value_t type_val, bool bubbles, bool cancelable) { ··· 483 551 484 552 static ant_value_t js_add_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 485 553 if (nargs < 1) return js_mkundef(); 486 - const char *event_type = js_getstr(js, args[0], NULL); 487 - if (!event_type) return js_mkundef(); 554 + ant_value_t key = evt_key_from_arg(args[0]); 555 + if (!key) return js_mkundef(); 488 556 return add_listener_to(js, args, nargs, 489 - find_or_create_emitter_event_type(js, js_getthis(js), event_type)); 557 + find_or_create_emitter_event_type(js, js_getthis(js), key)); 490 558 } 491 559 492 560 static ant_value_t js_add_event_listener(ant_t *js, ant_value_t *args, int nargs) { 493 561 if (nargs < 1) return js_mkundef(); 494 - const char *event_type = js_getstr(js, args[0], NULL); 495 - if (!event_type) return js_mkundef(); 496 - return add_listener_to(js, args, nargs, find_or_create_global_event_type(event_type)); 562 + ant_value_t key = evt_key_from_arg(args[0]); 563 + if (!key) return js_mkundef(); 564 + return add_listener_to(js, args, nargs, find_or_create_global_event_type(js, key)); 497 565 } 498 566 499 567 static ant_value_t js_remove_event_listener_method(ant_t *js, ant_value_t *args, int nargs) { 500 568 if (nargs < 1) return js_mkundef(); 501 - const char *event_type = js_getstr(js, args[0], NULL); 502 - if (!event_type) return js_mkundef(); 569 + ant_value_t key = evt_key_from_arg(args[0]); 570 + if (!key) return js_mkundef(); 503 571 return remove_listener_from(js, args, nargs, 504 - find_emitter_event_type(js, js_getthis(js), event_type)); 572 + find_emitter_event_type(js, js_getthis(js), key)); 505 573 } 506 574 507 575 static ant_value_t js_remove_event_listener(ant_t *js, ant_value_t *args, int nargs) { 508 576 if (nargs < 1) return js_mkundef(); 509 - const char *event_type = js_getstr(js, args[0], NULL); 510 - if (!event_type) return js_mkundef(); 511 - return remove_listener_from(js, args, nargs, find_global_event_type(event_type)); 577 + ant_value_t key = evt_key_from_arg(args[0]); 578 + if (!key) return js_mkundef(); 579 + return remove_listener_from(js, args, nargs, find_global_event_type(js, key)); 512 580 } 513 581 514 582 static ant_value_t js_dispatch_event_method(ant_t *js, ant_value_t *args, int nargs) { 515 583 if (nargs < 1 || !is_object_type(args[0])) return js_false; 516 584 ant_value_t this_obj = js_getthis(js); 517 - ant_value_t type_val = js_get(js, args[0], "type"); 518 - const char *event_type = js_getstr(js, type_val, NULL); 519 - if (!event_type) return js_false; 585 + ant_value_t key = js_get(js, args[0], "type"); 586 + if (!evt_key_from_arg(key)) return js_false; 520 587 return dispatch_event_to(js, args[0], 521 - find_emitter_event_type(js, this_obj, event_type), this_obj); 588 + find_emitter_event_type(js, this_obj, key), this_obj); 522 589 } 523 590 524 591 static ant_value_t js_dispatch_event(ant_t *js, ant_value_t *args, int nargs) { 525 592 if (nargs < 1 || !is_object_type(args[0])) return js_false; 526 - ant_value_t type_val = js_get(js, args[0], "type"); 527 - const char *event_type = js_getstr(js, type_val, NULL); 528 - if (!event_type) return js_false; 593 + ant_value_t key = js_get(js, args[0], "type"); 594 + if (!evt_key_from_arg(key)) return js_false; 529 595 return dispatch_event_to(js, args[0], 530 - find_global_event_type(event_type), js_glob(js)); 596 + find_global_event_type(js, key), js_glob(js)); 531 597 } 532 598 533 599 void js_dispatch_global_event(ant_t *js, ant_value_t event_obj) { 534 - ant_value_t type_val = js_get(js, event_obj, "type"); 535 - const char *event_type = js_getstr(js, type_val, NULL); 536 - if (!event_type) return; 537 - EventType *evt = find_global_event_type(event_type); 600 + ant_value_t key = js_get(js, event_obj, "type"); 601 + if (!evt_key_from_arg(key)) return; 602 + EventType *evt = find_global_event_type(js, key); 538 603 if (!evt || utarray_len(evt->listeners) == 0) return; 539 604 dispatch_event_to(js, event_obj, evt, js_glob(js)); 540 605 } 541 606 542 607 static bool eventemitter_add_listener_impl( 543 608 ant_t *js, 544 - ant_value_t target, const char *event_type, 609 + ant_value_t target, ant_value_t key, 545 610 ant_value_t listener, bool once, bool prepend 546 611 ) { 547 612 EventType *evt = NULL; 548 613 EventListenerEntry entry = {0}; 549 614 uint8_t t = 0; 550 615 551 - if (!is_object_type(target) || !event_type) return false; 616 + if (!is_object_type(target) || !key) return false; 552 617 t = vtype(listener); 553 618 if (t != T_FUNC && t != T_CFUNC) return false; 554 619 555 - evt = find_or_create_emitter_event_type(js, target, event_type); 620 + evt = find_or_create_emitter_event_type(js, target, key); 556 621 if (!evt) return false; 557 622 558 623 entry.callback = listener; ··· 574 639 575 640 static ant_value_t js_eventemitter_off(ant_t *js, ant_value_t *args, int nargs) { 576 641 if (nargs < 2) return js_mkerr(js, "off requires 2 arguments (event, listener)"); 577 - const char *event_type = js_getstr(js, args[0], NULL); 642 + ant_value_t key = evt_key_from_arg(args[0]); 578 643 579 - if (!event_type) return js_mkerr(js, "event must be a string"); 644 + if (!key) return js_mkerr(js, "event must be a string or Symbol"); 580 645 ant_value_t this_obj = js_getthis(js); 581 646 582 647 remove_listener_from( 583 648 js, args, nargs, 584 - find_emitter_event_type(js, this_obj, event_type) 649 + find_emitter_event_type(js, this_obj, key) 585 650 ); 586 651 587 652 return this_obj; ··· 589 654 590 655 static bool eventemitter_emit_args_impl( 591 656 ant_t *js, 592 - ant_value_t target, const char *event_type, 657 + ant_value_t target, ant_value_t key, 593 658 ant_value_t *args, int nargs 594 659 ) { 595 660 EventType *evt = NULL; 596 661 597 - if (!is_object_type(target) || !event_type) return false; 598 - evt = find_emitter_event_type(js, target, event_type); 662 + if (!is_object_type(target) || !key) return false; 663 + evt = find_emitter_event_type(js, target, key); 599 664 if (!evt || utarray_len(evt->listeners) == 0) return false; 600 665 601 666 for (unsigned int i = 0; i < utarray_len(evt->listeners);) { ··· 610 675 if (vtype(cb) != T_FUNC && vtype(cb) != T_CFUNC) continue; 611 676 612 677 ant_value_t result = sv_vm_call(js->vm, js, cb, js_mkundef(), args, nargs, NULL, false); 613 - if (vtype(result) == T_ERR) fprintf(stderr, "Error in event listener for '%s': %s\n", event_type, js_str(js, result)); 678 + if (vtype(result) == T_ERR) { 679 + if (vtype(key) == T_STR) fprintf(stderr, "Error in event listener for '%s': %s\n", js_str(js, key), js_str(js, result)); 680 + else fprintf(stderr, "Error in event listener: %s\n", js_str(js, result)); 681 + } 614 682 } 615 683 616 684 return true; 617 685 } 618 686 619 687 static ant_value_t js_eventemitter_emit(ant_t *js, ant_value_t *args, int nargs) { 620 - const char *event_type = NULL; 621 - 622 688 if (nargs < 1) return js_mkerr(js, "emit requires at least 1 argument (event)"); 623 - event_type = js_getstr(js, args[0], NULL); 624 - if (!event_type) return js_mkerr(js, "event must be a string"); 689 + ant_value_t key = evt_key_from_arg(args[0]); 690 + if (!key) return js_mkerr(js, "event must be a string or Symbol"); 625 691 626 692 return js_bool(eventemitter_emit_args_impl( 627 - js, js_getthis(js), event_type, 693 + js, js_getthis(js), key, 628 694 nargs > 1 ? &args[1] : NULL, nargs - 1 629 695 )); 630 696 } 631 697 698 + bool eventemitter_emit_args_val( 699 + ant_t *js, 700 + ant_value_t target, ant_value_t key, 701 + ant_value_t *args, int nargs 702 + ) { 703 + return eventemitter_emit_args_impl(js, target, key, args, nargs); 704 + } 705 + 632 706 bool eventemitter_emit_args( 633 707 ant_t *js, 634 708 ant_value_t target, const char *event_type, 635 709 ant_value_t *args, int nargs 636 710 ) { 637 - return eventemitter_emit_args_impl(js, target, event_type, args, nargs); 711 + return eventemitter_emit_args_val(js, target, js_mkstr(js, event_type, strlen(event_type)), args, nargs); 712 + } 713 + 714 + bool eventemitter_add_listener_val( 715 + ant_t *js, 716 + ant_value_t target, ant_value_t key, 717 + ant_value_t listener, bool once 718 + ) { 719 + return eventemitter_add_listener_impl(js, target, key, listener, once, false); 638 720 } 639 721 640 722 bool eventemitter_add_listener( ··· 642 724 ant_value_t target, const char *event_type, 643 725 ant_value_t listener, bool once 644 726 ) { 645 - return eventemitter_add_listener_impl(js, target, event_type, listener, once, false); 727 + return eventemitter_add_listener_val(js, target, js_mkstr(js, event_type, strlen(event_type)), listener, once); 646 728 } 647 729 648 730 static ant_value_t js_eventemitter_add(ant_t *js, ant_value_t *args, int nargs, bool once, bool prepend) { 649 - const char *event_type = NULL; 650 - 651 731 if (nargs < 2) return js_mkerr(js, "requires 2 arguments (event, listener)"); 652 - event_type = js_getstr(js, args[0], NULL); 732 + ant_value_t key = evt_key_from_arg(args[0]); 653 733 654 - if (!event_type) return js_mkerr(js, "event must be a string"); 655 - if (!eventemitter_add_listener_impl(js, js_getthis(js), event_type, args[1], once, prepend)) 734 + if (!key) return js_mkerr(js, "event must be a string or Symbol"); 735 + if (!eventemitter_add_listener_impl(js, js_getthis(js), key, args[1], once, prepend)) 656 736 return js_mkerr(js, "listener must be a function"); 657 737 658 738 return js_getthis(js); ··· 677 757 static ant_value_t js_eventemitter_removeAllListeners(ant_t *js, ant_value_t *args, int nargs) { 678 758 ant_value_t this_obj = js_getthis(js); 679 759 if (nargs < 1) return this_obj; 680 - const char *event_type = js_getstr(js, args[0], NULL); 681 - if (!event_type) return this_obj; 682 - EventType *evt = find_emitter_event_type(js, this_obj, event_type); 760 + 761 + ant_value_t key = evt_key_from_arg(args[0]); 762 + if (!key) return this_obj; 763 + 764 + EventType *evt = find_emitter_event_type(js, this_obj, key); 683 765 if (evt) utarray_clear(evt->listeners); 766 + 684 767 return this_obj; 685 768 } 686 769 687 770 static ant_value_t js_eventemitter_listenerCount(ant_t *js, ant_value_t *args, int nargs) { 688 771 if (nargs < 1) return js_mknum(0); 689 - const char *event_type = js_getstr(js, args[0], NULL); 690 - if (!event_type) return js_mknum(0); 691 - EventType *evt = find_emitter_event_type(js, js_getthis(js), event_type); 772 + ant_value_t key = evt_key_from_arg(args[0]); 773 + 774 + if (!key) return js_mknum(0); 775 + EventType *evt = find_emitter_event_type(js, js_getthis(js), key); 776 + 692 777 if (!evt) return js_mknum(0); 693 778 return js_mknum((double)utarray_len(evt->listeners)); 694 779 } ··· 700 785 EventType **events = get_or_create_emitter_events(js, this_obj); 701 786 if (events && *events) { 702 787 EventType *evt, *tmp; 703 - HASH_ITER(hh, *events, evt, tmp) if (utarray_len(evt->listeners) > 0) js_arr_push( 704 - js, result, js_mkstr(js, evt->event_type, strlen(evt->event_type)) 705 - ); 788 + HASH_ITER(hh, *events, evt, tmp) if (utarray_len(evt->listeners) > 0) 789 + js_arr_push(js, result, evt->js_key); 706 790 } 707 791 708 792 return result; ··· 805 889 static void mark_event_type_listeners(ant_t *js, gc_mark_fn mark, EventType *events) { 806 890 EventType *evt, *tmp; 807 891 HASH_ITER(hh, events, evt, tmp) { 892 + if (vtype(evt->js_key) == T_STR) mark(js, evt->js_key); 808 893 for (unsigned int i = 0; i < utarray_len(evt->listeners); i++) { 809 894 EventListenerEntry *e = (EventListenerEntry *)utarray_eltptr(evt->listeners, i); 810 895 mark(js, e->callback);
+441 -36
src/modules/fs.c
··· 27 27 #include "silver/engine.h" 28 28 29 29 #include "modules/fs.h" 30 + #include "modules/date.h" 30 31 #include "modules/buffer.h" 31 32 #include "modules/events.h" 32 33 #include "modules/symbol.h" ··· 61 62 FS_OP_ACCESS, 62 63 FS_OP_REALPATH, 63 64 FS_OP_WRITE_FD, 65 + FS_OP_READ_FD, 64 66 FS_OP_OPEN, 65 - FS_OP_CLOSE 67 + FS_OP_CLOSE, 68 + FS_OP_MKDTEMP 66 69 } fs_op_type_t; 67 70 68 71 typedef struct fs_request_s { 69 72 uv_fs_t uv_req; 70 73 ant_t *js; 71 74 ant_value_t promise; 72 - 75 + ant_value_t target_buffer; 76 + ant_value_t callback_fn; 77 + 73 78 char *path; 74 79 char *data; 75 80 char *error_msg; 76 81 size_t data_len; 77 - 82 + size_t buf_offset; 83 + 78 84 fs_op_type_t op_type; 79 85 fs_encoding_t encoding; 80 86 uv_file fd; 81 - 87 + 82 88 int completed; 83 89 int failed; 84 90 int recursive; 85 91 int error_code; 92 + int with_file_types; 86 93 } fs_request_t; 87 94 88 95 typedef enum { ··· 127 134 ant_value_t listener; 128 135 } fs_watchfile_options_t; 129 136 137 + static ant_value_t g_dirent_proto = 0; 130 138 static ant_value_t g_fswatcher_proto = 0; 131 139 static ant_value_t g_fswatcher_ctor = 0; 132 140 ··· 179 187 return result; 180 188 } 181 189 182 - static ant_value_t fs_stats_object_new( 183 - ant_t *js, 184 - mode_t mode, 185 - double size, 186 - double uid, 187 - double gid 188 - ) { 190 + typedef struct { 191 + mode_t mode; 192 + double size, uid, gid; 193 + double atime_ms, mtime_ms, ctime_ms, birthtime_ms; 194 + } fs_stat_fields_t; 195 + 196 + static ant_value_t fs_make_date(ant_t *js, double ms) { 197 + ant_value_t obj = js_mkobj(js); 198 + ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); 199 + if (is_object_type(date_proto)) js_set_proto_init(obj, date_proto); 200 + js_set_slot(obj, SLOT_DATA, tov(ms)); 201 + js_set_slot(obj, SLOT_BRAND, js_mknum(BRAND_DATE)); 202 + return obj; 203 + } 204 + 205 + static ant_value_t fs_stats_object_new(ant_t *js, const fs_stat_fields_t *f) { 189 206 ant_value_t stat_obj = js_mkobj(js); 190 207 ant_value_t proto = js_get_ctor_proto(js, "Stats", 5); 191 208 192 209 if (is_object_type(proto) || is_special_object(proto)) 193 210 js_set_proto_init(stat_obj, proto); 194 211 195 - js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)mode)); 196 - js_set(js, stat_obj, "size", js_mknum(size)); 197 - js_set(js, stat_obj, "mode", js_mknum((double)mode)); 198 - js_set(js, stat_obj, "uid", js_mknum(uid)); 199 - js_set(js, stat_obj, "gid", js_mknum(gid)); 212 + js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)f->mode)); 213 + js_set(js, stat_obj, "size", js_mknum(f->size)); 214 + js_set(js, stat_obj, "mode", js_mknum((double)f->mode)); 215 + js_set(js, stat_obj, "uid", js_mknum(f->uid)); 216 + js_set(js, stat_obj, "gid", js_mknum(f->gid)); 217 + 218 + js_set(js, stat_obj, "atimeMs", js_mknum(f->atime_ms)); 219 + js_set(js, stat_obj, "mtimeMs", js_mknum(f->mtime_ms)); 220 + js_set(js, stat_obj, "ctimeMs", js_mknum(f->ctime_ms)); 221 + js_set(js, stat_obj, "birthtimeMs", js_mknum(f->birthtime_ms)); 222 + 223 + js_set(js, stat_obj, "atime", fs_make_date(js, f->atime_ms)); 224 + js_set(js, stat_obj, "mtime", fs_make_date(js, f->mtime_ms)); 225 + js_set(js, stat_obj, "ctime", fs_make_date(js, f->ctime_ms)); 226 + js_set(js, stat_obj, "birthtime", fs_make_date(js, f->birthtime_ms)); 200 227 201 228 return stat_obj; 202 229 } 203 230 231 + static double uv_ts_to_ms(uv_timespec_t ts) { 232 + return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6; 233 + } 234 + 235 + #ifdef __APPLE__ 236 + #define POSIX_TS_MS(st, field) \ 237 + ((double)(st)->st_##field##spec.tv_sec * 1000.0 + (double)(st)->st_##field##spec.tv_nsec / 1e6) 238 + #define POSIX_BIRTH_MS(st) POSIX_TS_MS(st, birthtime) 239 + #else 240 + #define POSIX_TS_MS(st, field) \ 241 + ((double)(st)->st_##field.tv_sec * 1000.0 + (double)(st)->st_##field.tv_nsec / 1e6) 242 + #define POSIX_BIRTH_MS(st) 0.0 243 + #endif 244 + 245 + static const fs_stat_fields_t fs_stat_fields_zero = {0}; 246 + 204 247 static ant_value_t fs_stats_object_from_uv(ant_t *js, const uv_stat_t *st) { 205 - if (!st) return fs_stats_object_new(js, 0, 0.0, 0.0, 0.0); 206 - return fs_stats_object_new( 207 - js, (mode_t)st->st_mode, 208 - (double)st->st_size, (double)st->st_uid, (double)st->st_gid 209 - ); 248 + if (!st) return fs_stats_object_new(js, &fs_stat_fields_zero); 249 + return fs_stats_object_new(js, &(fs_stat_fields_t){ 250 + .mode = (mode_t)st->st_mode, 251 + .size = (double)st->st_size, 252 + .uid = (double)st->st_uid, 253 + .gid = (double)st->st_gid, 254 + .atime_ms = uv_ts_to_ms(st->st_atim), 255 + .mtime_ms = uv_ts_to_ms(st->st_mtim), 256 + .ctime_ms = uv_ts_to_ms(st->st_ctim), 257 + .birthtime_ms = uv_ts_to_ms(st->st_birthtim), 258 + }); 210 259 } 211 260 212 261 static ant_value_t fs_stats_object_from_posix(ant_t *js, const struct stat *st) { 213 - if (!st) return fs_stats_object_new(js, 0, 0.0, 0.0, 0.0); 214 - return fs_stats_object_new( 215 - js, st->st_mode, 216 - (double)st->st_size, (double)st->st_uid, (double)st->st_gid 217 - ); 262 + if (!st) return fs_stats_object_new(js, &fs_stat_fields_zero); 263 + return fs_stats_object_new(js, &(fs_stat_fields_t){ 264 + .mode = st->st_mode, 265 + .size = (double)st->st_size, 266 + .uid = (double)st->st_uid, 267 + .gid = (double)st->st_gid, 268 + .atime_ms = POSIX_TS_MS(st, atime), 269 + .mtime_ms = POSIX_TS_MS(st, mtime), 270 + .ctime_ms = POSIX_TS_MS(st, ctime), 271 + .birthtime_ms = POSIX_BIRTH_MS(st), 272 + }); 218 273 } 219 274 220 275 static bool fs_stat_path_sync(const char *path, uv_stat_t *out) { ··· 883 938 result = js_mkstr(req->js, req->data, req->data_len); 884 939 else if (req->op_type == FS_OP_REALPATH && req->data) 885 940 result = js_mkstr(req->js, req->data, req->data_len); 941 + else if (req->op_type == FS_OP_MKDTEMP && req->data) 942 + result = js_mkstr(req->js, req->data, req->data_len); 886 943 js_resolve_promise(req->js, req->promise, result); 887 944 } 888 945 ··· 1087 1144 complete_request(req); 1088 1145 } 1089 1146 1147 + static ant_value_t create_dirent_object(ant_t *js, const char *name, size_t name_len, uv_dirent_type_t type) { 1148 + ant_value_t obj = js_newobj(js); 1149 + js_set_proto(obj, g_dirent_proto); 1150 + js_set(js, obj, "name", js_mkstr(js, name, name_len)); 1151 + js_set_slot(obj, SLOT_DATA, tov((double)type)); 1152 + return obj; 1153 + } 1154 + 1155 + static void on_mkdtemp_complete(uv_fs_t *uv_req) { 1156 + fs_request_t *req = (fs_request_t *)uv_req->data; 1157 + 1158 + if (uv_req->result < 0) { 1159 + fs_request_fail(req, (int)uv_req->result); 1160 + req->completed = 1; 1161 + complete_request(req); 1162 + uv_fs_req_cleanup(uv_req); 1163 + return; 1164 + } 1165 + 1166 + req->data = strdup(uv_req->path); 1167 + if (!req->data) { 1168 + req->failed = 1; 1169 + req->error_msg = strdup("Out of memory"); 1170 + req->completed = 1; 1171 + complete_request(req); 1172 + uv_fs_req_cleanup(uv_req); 1173 + return; 1174 + } 1175 + 1176 + req->data_len = strlen(req->data); 1177 + req->completed = 1; 1178 + uv_fs_req_cleanup(uv_req); 1179 + complete_request(req); 1180 + } 1181 + 1090 1182 static void on_exists_complete(uv_fs_t *uv_req) { 1091 1183 fs_request_t *req = (fs_request_t *)uv_req->data; 1092 1184 ant_value_t result = js_bool(uv_req->result >= 0); ··· 1127 1219 uv_dirent_t dirent; 1128 1220 1129 1221 while (uv_fs_scandir_next(uv_req, &dirent) != UV_EOF) { 1222 + if (req->with_file_types) { 1223 + ant_value_t entry = create_dirent_object(req->js, dirent.name, strlen(dirent.name), dirent.type); 1224 + js_arr_push(req->js, arr, entry); 1225 + } else { 1130 1226 ant_value_t name = js_mkstr(req->js, dirent.name, strlen(dirent.name)); 1131 1227 js_arr_push(req->js, arr, name); 1132 - } 1228 + }} 1133 1229 1134 1230 req->completed = 1; 1135 1231 js_resolve_promise(req->js, req->promise, arr); ··· 1428 1524 return js_mkundef(); 1429 1525 } 1430 1526 1527 + static double fs_time_arg_to_seconds(ant_t *js, ant_value_t v) { 1528 + if (is_date_instance(v)) { 1529 + ant_value_t t = js_get_slot(js_as_obj(v), SLOT_DATA); 1530 + return (vtype(t) == T_NUM) ? js_getnum(t) / 1000.0 : 0.0; 1531 + } 1532 + return js_to_number(js, v); 1533 + } 1534 + 1535 + static ant_value_t builtin_fs_utimesSync(ant_t *js, ant_value_t *args, int nargs) { 1536 + if (nargs < 3) return js_mkerr(js, "utimesSync() requires path, atime, and mtime"); 1537 + if (vtype(args[0]) != T_STR) return js_mkerr(js, "utimesSync() path must be a string"); 1538 + 1539 + const char *path = js_str(js, args[0]); 1540 + double atime = fs_time_arg_to_seconds(js, args[1]); 1541 + double mtime = fs_time_arg_to_seconds(js, args[2]); 1542 + 1543 + uv_fs_t req; 1544 + int rc = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); 1545 + uv_fs_req_cleanup(&req); 1546 + 1547 + if (rc < 0) return js_mkerr(js, "utimesSync: %s", uv_strerror(rc)); 1548 + return js_mkundef(); 1549 + } 1550 + 1551 + static ant_value_t builtin_fs_utimes(ant_t *js, ant_value_t *args, int nargs) { 1552 + return builtin_fs_utimesSync(js, args, nargs); 1553 + } 1554 + 1555 + static ant_value_t builtin_fs_futimesSync(ant_t *js, ant_value_t *args, int nargs) { 1556 + if (nargs < 3) return js_mkerr(js, "futimesSync() requires fd, atime, and mtime"); 1557 + int fd = (int)js_to_number(js, args[0]); 1558 + double atime = fs_time_arg_to_seconds(js, args[1]); 1559 + double mtime = fs_time_arg_to_seconds(js, args[2]); 1560 + 1561 + uv_fs_t req; 1562 + int rc = uv_fs_futime(NULL, &req, fd, atime, mtime, NULL); 1563 + uv_fs_req_cleanup(&req); 1564 + 1565 + if (rc < 0) return js_mkerr(js, "futimesSync: %s", uv_strerror(rc)); 1566 + return js_mkundef(); 1567 + } 1568 + 1569 + static ant_value_t builtin_fs_futimes(ant_t *js, ant_value_t *args, int nargs) { 1570 + return builtin_fs_futimesSync(js, args, nargs); 1571 + } 1572 + 1431 1573 static ant_value_t builtin_fs_appendFileSync(ant_t *js, ant_value_t *args, int nargs) { 1432 1574 if (nargs < 2) return js_mkerr(js, "appendFileSync() requires path and data arguments"); 1433 1575 ··· 1664 1806 return req->promise; 1665 1807 } 1666 1808 1809 + static ant_value_t builtin_fs_mkdtempSync(ant_t *js, ant_value_t *args, int nargs) { 1810 + if (nargs < 1 || vtype(args[0]) != T_STR) 1811 + return js_mkerr(js, "mkdtempSync() requires a prefix string"); 1812 + 1813 + size_t prefix_len; 1814 + const char *prefix = js_getstr(js, args[0], &prefix_len); 1815 + if (!prefix) return js_mkerr(js, "Failed to get prefix string"); 1816 + 1817 + size_t tpl_len = prefix_len + 6; 1818 + char *tpl = malloc(tpl_len + 1); 1819 + if (!tpl) return js_mkerr(js, "Out of memory"); 1820 + 1821 + memcpy(tpl, prefix, prefix_len); 1822 + memcpy(tpl + prefix_len, "XXXXXX", 6); 1823 + tpl[tpl_len] = '\0'; 1824 + 1825 + char *result = mkdtemp(tpl); 1826 + if (!result) { 1827 + free(tpl); 1828 + return js_mkerr(js, "mkdtempSync failed: %s", strerror(errno)); 1829 + } 1830 + 1831 + ant_value_t ret = js_mkstr(js, result, strlen(result)); 1832 + free(tpl); 1833 + return ret; 1834 + } 1835 + 1836 + static ant_value_t builtin_fs_mkdtemp(ant_t *js, ant_value_t *args, int nargs) { 1837 + if (nargs < 1 || vtype(args[0]) != T_STR) 1838 + return js_mkerr(js, "mkdtemp() requires a prefix string"); 1839 + 1840 + size_t prefix_len; 1841 + const char *prefix = js_getstr(js, args[0], &prefix_len); 1842 + if (!prefix) return js_mkerr(js, "Failed to get prefix string"); 1843 + 1844 + size_t tpl_len = prefix_len + 6; 1845 + char *tpl = malloc(tpl_len + 1); 1846 + if (!tpl) return js_mkerr(js, "Out of memory"); 1847 + 1848 + memcpy(tpl, prefix, prefix_len); 1849 + memcpy(tpl + prefix_len, "XXXXXX", 6); 1850 + tpl[tpl_len] = '\0'; 1851 + 1852 + fs_request_t *req = calloc(1, sizeof(fs_request_t)); 1853 + if (!req) { free(tpl); return js_mkerr(js, "Out of memory"); } 1854 + 1855 + req->js = js; 1856 + req->op_type = FS_OP_MKDTEMP; 1857 + req->promise = js_mkpromise(js); 1858 + req->path = tpl; 1859 + req->uv_req.data = req; 1860 + 1861 + utarray_push_back(pending_requests, &req); 1862 + int result = uv_fs_mkdtemp(uv_default_loop(), &req->uv_req, req->path, on_mkdtemp_complete); 1863 + 1864 + if (result < 0) { 1865 + fs_request_fail(req, result); 1866 + req->completed = 1; 1867 + complete_request(req); 1868 + } 1869 + 1870 + return req->promise; 1871 + } 1872 + 1667 1873 static ant_value_t builtin_fs_rmdirSync(ant_t *js, ant_value_t *args, int nargs) { 1668 1874 if (nargs < 1) return js_mkerr(js, "rmdirSync() requires a path argument"); 1669 1875 ··· 1729 1935 1730 1936 static ant_value_t builtin_fs_rm(ant_t *js, ant_value_t *args, int nargs) { 1731 1937 return fs_rm_impl(js, args, nargs, true); 1938 + } 1939 + 1940 + static ant_value_t dirent_isFile(ant_t *js, ant_value_t *args, int nargs) { 1941 + ant_value_t this = js_getthis(js); 1942 + ant_value_t type_val = js_get_slot(this, SLOT_DATA); 1943 + if (vtype(type_val) != T_NUM) return js_false; 1944 + return js_bool((int)js_getnum(type_val) == UV_DIRENT_FILE); 1945 + } 1946 + 1947 + static ant_value_t dirent_isDirectory(ant_t *js, ant_value_t *args, int nargs) { 1948 + ant_value_t this = js_getthis(js); 1949 + ant_value_t type_val = js_get_slot(this, SLOT_DATA); 1950 + if (vtype(type_val) != T_NUM) return js_false; 1951 + return js_bool((int)js_getnum(type_val) == UV_DIRENT_DIR); 1952 + } 1953 + 1954 + static ant_value_t dirent_isSymbolicLink(ant_t *js, ant_value_t *args, int nargs) { 1955 + ant_value_t this = js_getthis(js); 1956 + ant_value_t type_val = js_get_slot(this, SLOT_DATA); 1957 + if (vtype(type_val) != T_NUM) return js_false; 1958 + return js_bool((int)js_getnum(type_val) == UV_DIRENT_LINK); 1959 + } 1960 + 1961 + static ant_value_t dirent_isBlockDevice(ant_t *js, ant_value_t *args, int nargs) { 1962 + ant_value_t this = js_getthis(js); 1963 + ant_value_t type_val = js_get_slot(this, SLOT_DATA); 1964 + if (vtype(type_val) != T_NUM) return js_false; 1965 + return js_bool((int)js_getnum(type_val) == UV_DIRENT_BLOCK); 1966 + } 1967 + 1968 + static ant_value_t dirent_isCharacterDevice(ant_t *js, ant_value_t *args, int nargs) { 1969 + ant_value_t this = js_getthis(js); 1970 + ant_value_t type_val = js_get_slot(this, SLOT_DATA); 1971 + if (vtype(type_val) != T_NUM) return js_false; 1972 + return js_bool((int)js_getnum(type_val) == UV_DIRENT_CHAR); 1973 + } 1974 + 1975 + static ant_value_t dirent_isFIFO(ant_t *js, ant_value_t *args, int nargs) { 1976 + ant_value_t this = js_getthis(js); 1977 + ant_value_t type_val = js_get_slot(this, SLOT_DATA); 1978 + if (vtype(type_val) != T_NUM) return js_false; 1979 + return js_bool((int)js_getnum(type_val) == UV_DIRENT_FIFO); 1980 + } 1981 + 1982 + static ant_value_t dirent_isSocket(ant_t *js, ant_value_t *args, int nargs) { 1983 + ant_value_t this = js_getthis(js); 1984 + ant_value_t type_val = js_get_slot(this, SLOT_DATA); 1985 + if (vtype(type_val) != T_NUM) return js_false; 1986 + return js_bool((int)js_getnum(type_val) == UV_DIRENT_SOCKET); 1732 1987 } 1733 1988 1734 1989 static ant_value_t stat_isFile(ant_t *js, ant_value_t *args, int nargs) { ··· 2063 2318 if (nargs < 1) return js_mkerr(js, "readdirSync() requires a path argument"); 2064 2319 if (vtype(args[0]) != T_STR) return js_mkerr(js, "readdirSync() path must be a string"); 2065 2320 2321 + bool with_file_types = false; 2322 + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 2323 + ant_value_t wft = js_get(js, args[1], "withFileTypes"); 2324 + with_file_types = js_truthy(js, wft); 2325 + } 2326 + 2066 2327 size_t path_len; 2067 2328 char *path = js_getstr(js, args[0], &path_len); 2068 2329 if (!path) return js_mkerr(js, "Failed to get path string"); ··· 2084 2345 uv_dirent_t dirent; 2085 2346 2086 2347 while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { 2087 - ant_value_t name = js_mkstr(js, dirent.name, strlen(dirent.name)); 2088 - js_arr_push(js, arr, name); 2348 + if (with_file_types) { 2349 + ant_value_t entry = create_dirent_object(js, dirent.name, strlen(dirent.name), dirent.type); 2350 + js_arr_push(js, arr, entry); 2351 + } else { 2352 + ant_value_t name = js_mkstr(js, dirent.name, strlen(dirent.name)); 2353 + js_arr_push(js, arr, name); 2354 + } 2089 2355 } 2090 2356 2091 2357 uv_fs_req_cleanup(&req); ··· 2096 2362 if (nargs < 1) return js_mkerr(js, "readdir() requires a path argument"); 2097 2363 if (vtype(args[0]) != T_STR) return js_mkerr(js, "readdir() path must be a string"); 2098 2364 2365 + bool with_file_types = false; 2366 + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { 2367 + ant_value_t wft = js_get(js, args[1], "withFileTypes"); 2368 + with_file_types = js_truthy(js, wft); 2369 + } 2370 + 2099 2371 size_t path_len; 2100 2372 char *path = js_getstr(js, args[0], &path_len); 2101 2373 if (!path) return js_mkerr(js, "Failed to get path string"); ··· 2106 2378 2107 2379 req->js = js; 2108 2380 req->op_type = FS_OP_READDIR; 2381 + req->with_file_types = with_file_types; 2109 2382 req->promise = js_mkpromise(js); 2110 2383 req->path = strndup(path, path_len); 2111 2384 req->uv_req.data = req; ··· 2138 2411 free_fs_request(req); 2139 2412 } 2140 2413 2414 + static void on_read_fd_complete(uv_fs_t *uv_req) { 2415 + fs_request_t *req = (fs_request_t *)uv_req->data; 2416 + 2417 + if (uv_req->result < 0) { 2418 + if (is_callable(req->callback_fn)) { 2419 + ant_value_t err = js_mkerr(req->js, "read failed: %s", uv_strerror((int)uv_req->result)); 2420 + ant_value_t cb_args[1] = { err }; 2421 + fs_call_value(req->js, req->callback_fn, js_mkundef(), cb_args, 1); 2422 + remove_pending_request(req); 2423 + free_fs_request(req); 2424 + return; 2425 + } 2426 + fs_request_fail(req, (int)uv_req->result); 2427 + req->completed = 1; 2428 + complete_request(req); 2429 + return; 2430 + } 2431 + 2432 + ssize_t bytes_read = uv_req->result; 2433 + 2434 + if (req->data && bytes_read > 0) { 2435 + ant_value_t ta_val = js_get_slot(req->target_buffer, SLOT_BUFFER); 2436 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(ta_val); 2437 + if (ta && ta->buffer && ta->buffer->data) { 2438 + uint8_t *dest = ta->buffer->data + ta->byte_offset + req->buf_offset; 2439 + memcpy(dest, req->data, (size_t)bytes_read); 2440 + } 2441 + } 2442 + 2443 + if (is_callable(req->callback_fn)) { 2444 + ant_value_t cb_args[3] = { js_mknull(), js_mknum((double)bytes_read), req->target_buffer }; 2445 + fs_call_value(req->js, req->callback_fn, js_mkundef(), cb_args, 3); 2446 + remove_pending_request(req); 2447 + free_fs_request(req); 2448 + return; 2449 + } 2450 + 2451 + req->completed = 1; 2452 + js_resolve_promise(req->js, req->promise, js_mknum((double)bytes_read)); 2453 + remove_pending_request(req); 2454 + free_fs_request(req); 2455 + } 2456 + 2457 + static ant_value_t builtin_fs_read_fd(ant_t *js, ant_value_t *args, int nargs) { 2458 + if (nargs < 2) return js_mkerr(js, "read() requires fd and buffer arguments"); 2459 + if (vtype(args[0]) != T_NUM) return js_mkerr(js, "read() fd must be a number"); 2460 + 2461 + int fd = (int)js_getnum(args[0]); 2462 + 2463 + ant_value_t buf_arg = args[1]; 2464 + ant_value_t ta_data_val = js_get_slot(buf_arg, SLOT_BUFFER); 2465 + TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); 2466 + if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) 2467 + return js_mkerr(js, "read() buffer argument must be a Buffer or TypedArray"); 2468 + 2469 + size_t buf_len = ta_data->byte_length; 2470 + size_t offset = 0; 2471 + size_t length = buf_len; 2472 + int64_t position = -1; 2473 + 2474 + ant_value_t callback = js_mkundef(); 2475 + int data_nargs = nargs; 2476 + if (nargs >= 3 && is_callable(args[nargs - 1])) { 2477 + callback = args[nargs - 1]; 2478 + data_nargs = nargs - 1; 2479 + } 2480 + 2481 + if (data_nargs >= 3 && vtype(args[2]) == T_NUM) offset = (size_t)js_getnum(args[2]); 2482 + if (data_nargs >= 4 && vtype(args[3]) == T_NUM) length = (size_t)js_getnum(args[3]); 2483 + if (data_nargs >= 5 && vtype(args[4]) == T_NUM) position = (int64_t)js_getnum(args[4]); 2484 + 2485 + if (offset > buf_len) return js_mkerr(js, "read() offset out of bounds"); 2486 + if (offset + length > buf_len) return js_mkerr(js, "read() length extends beyond buffer"); 2487 + 2488 + fs_request_t *req = calloc(1, sizeof(fs_request_t)); 2489 + if (!req) return js_mkerr(js, "Out of memory"); 2490 + 2491 + req->js = js; 2492 + req->op_type = FS_OP_READ_FD; 2493 + req->promise = js_mkpromise(js); 2494 + req->fd = fd; 2495 + req->target_buffer = buf_arg; 2496 + req->buf_offset = offset; 2497 + req->data_len = length; 2498 + req->callback_fn = callback; 2499 + req->data = malloc(length > 0 ? length : 1); 2500 + if (!req->data) { 2501 + free(req); 2502 + return js_mkerr(js, "Out of memory"); 2503 + } 2504 + req->uv_req.data = req; 2505 + 2506 + utarray_push_back(pending_requests, &req); 2507 + 2508 + uv_buf_t buf = uv_buf_init(req->data, (unsigned int)length); 2509 + int result = uv_fs_read(uv_default_loop(), &req->uv_req, req->fd, &buf, 1, position, on_read_fd_complete); 2510 + 2511 + if (result < 0) { 2512 + fs_request_fail(req, result); 2513 + req->completed = 1; 2514 + complete_request(req); 2515 + } 2516 + 2517 + return req->promise; 2518 + } 2519 + 2141 2520 static ant_value_t builtin_fs_readSync(ant_t *js, ant_value_t *args, int nargs) { 2142 2521 if (nargs < 2) return js_mkerr(js, "readSync() requires fd and buffer arguments"); 2143 2522 if (vtype(args[0]) != T_NUM) return js_mkerr(js, "readSync() fd must be a number"); ··· 2723 3102 js_set_descriptor(js, stats_ctor, "name", 4, 0); 2724 3103 2725 3104 js_set(js, glob, "Stats", js_obj_to_func(stats_ctor)); 3105 + 3106 + g_dirent_proto = js_mkobj(js); 3107 + js_set(js, g_dirent_proto, "isFile", js_mkfun(dirent_isFile)); 3108 + js_set(js, g_dirent_proto, "isDirectory", js_mkfun(dirent_isDirectory)); 3109 + js_set(js, g_dirent_proto, "isSymbolicLink", js_mkfun(dirent_isSymbolicLink)); 3110 + js_set(js, g_dirent_proto, "isBlockDevice", js_mkfun(dirent_isBlockDevice)); 3111 + js_set(js, g_dirent_proto, "isCharacterDevice", js_mkfun(dirent_isCharacterDevice)); 3112 + js_set(js, g_dirent_proto, "isFIFO", js_mkfun(dirent_isFIFO)); 3113 + js_set(js, g_dirent_proto, "isSocket", js_mkfun(dirent_isSocket)); 3114 + js_set_sym(js, g_dirent_proto, get_toStringTag_sym(), js_mkstr(js, "Dirent", 6)); 3115 + gc_register_root(&g_dirent_proto); 2726 3116 } 2727 3117 2728 3118 static ant_value_t fs_callback_success_handler(ant_t *js, ant_value_t *args, int nargs) { ··· 2795 3185 bool exists_style 2796 3186 ) { 2797 3187 GC_ROOT_SAVE(root_mark, js); 2798 - ant_value_t success_ctx = js_mkobj(js); 2799 - ant_value_t error_ctx = js_mkobj(js); 2800 3188 ant_value_t success_fn = js_mkundef(); 2801 3189 ant_value_t error_fn = js_mkundef(); 2802 3190 2803 3191 GC_ROOT_PIN(js, promise); 2804 3192 GC_ROOT_PIN(js, callback); 3193 + 3194 + ant_value_t success_ctx = js_mkobj(js); 2805 3195 GC_ROOT_PIN(js, success_ctx); 3196 + ant_value_t error_ctx = js_mkobj(js); 2806 3197 GC_ROOT_PIN(js, error_ctx); 2807 3198 2808 3199 js_set_slot(success_ctx, SLOT_DATA, callback); ··· 2813 3204 } 2814 3205 2815 3206 success_fn = js_heavy_mkfun(js, fs_callback_success_handler, success_ctx); 2816 - error_fn = js_heavy_mkfun(js, fs_callback_error_handler, error_ctx); 2817 3207 GC_ROOT_PIN(js, success_fn); 3208 + error_fn = js_heavy_mkfun(js, fs_callback_error_handler, error_ctx); 2818 3209 GC_ROOT_PIN(js, error_fn); 2819 3210 2820 3211 js_promise_then(js, promise, success_fn, error_fn); ··· 2871 3262 return js_mkundef(); 2872 3263 } 2873 3264 3265 + ant_value_t attach_result = fs_callback_attach_promise(js, result, callback, exists_style); 2874 3266 GC_ROOT_RESTORE(js, root_mark); 2875 - return fs_callback_attach_promise(js, result, callback, exists_style); 3267 + return attach_result; 2876 3268 } 2877 3269 2878 3270 static ant_value_t fs_make_callback_wrapper(ant_t *js, ant_value_t original, bool exists_style) { ··· 2893 3285 js_set(js, lib, "rm", js_mkfun(builtin_fs_rm)); 2894 3286 js_set(js, lib, "unlink", js_mkfun(builtin_fs_unlink)); 2895 3287 js_set(js, lib, "mkdir", js_mkfun(builtin_fs_mkdir)); 3288 + js_set(js, lib, "mkdtemp", js_mkfun(builtin_fs_mkdtemp)); 2896 3289 js_set(js, lib, "rmdir", js_mkfun(builtin_fs_rmdir)); 2897 3290 js_set(js, lib, "stat", js_mkfun(builtin_fs_stat)); 2898 3291 js_set(js, lib, "lstat", js_mkfun(builtin_fs_lstat)); 3292 + js_set(js, lib, "utimes", js_mkfun(builtin_fs_utimes)); 3293 + js_set(js, lib, "futimes", js_mkfun(builtin_fs_futimes)); 2899 3294 js_set(js, lib, "exists", js_mkfun(builtin_fs_exists)); 2900 3295 js_set(js, lib, "access", js_mkfun(builtin_fs_access)); 2901 3296 js_set(js, lib, "readdir", js_mkfun(builtin_fs_readdir)); ··· 2912 3307 js_set(js, lib, "rm", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rm), false)); 2913 3308 js_set(js, lib, "unlink", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_unlink), false)); 2914 3309 js_set(js, lib, "mkdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_mkdir), false)); 3310 + js_set(js, lib, "mkdtemp", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_mkdtemp), false)); 2915 3311 js_set(js, lib, "rmdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_rmdir), false)); 2916 3312 js_set(js, lib, "stat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_stat), false)); 2917 3313 js_set(js, lib, "lstat", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_lstat), false)); 3314 + js_set(js, lib, "utimes", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_utimes), false)); 3315 + js_set(js, lib, "futimes", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_futimes), false)); 2918 3316 js_set(js, lib, "exists", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_exists), true)); 2919 3317 js_set(js, lib, "access", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_access), false)); 2920 3318 js_set(js, lib, "readdir", fs_make_callback_wrapper(js, js_mkfun(builtin_fs_readdir), false)); ··· 2949 3347 2950 3348 ant_value_t fs_library(ant_t *js) { 2951 3349 ant_value_t lib = js_mkobj(js); 2952 - ant_value_t realpath_sync = js_mkfun(builtin_fs_realpathSync); 3350 + ant_value_t realpath_sync = js_heavy_mkfun(js, builtin_fs_realpathSync, js_mkundef()); 2953 3351 2954 3352 fs_set_callback_compatible_methods(js, lib); 2955 3353 fs_init_watch_constructors(js); 2956 3354 3355 + js_set(js, lib, "read", js_mkfun(builtin_fs_read_fd)); 2957 3356 js_set(js, lib, "readFileSync", js_mkfun(builtin_fs_readFileSync)); 2958 3357 js_set(js, lib, "readSync", js_mkfun(builtin_fs_readSync)); 2959 3358 js_set(js, lib, "stream", js_mkfun(builtin_fs_readBytes)); ··· 2968 3367 js_set(js, lib, "rmSync", js_mkfun(builtin_fs_rmSync)); 2969 3368 js_set(js, lib, "unlinkSync", js_mkfun(builtin_fs_unlinkSync)); 2970 3369 js_set(js, lib, "mkdirSync", js_mkfun(builtin_fs_mkdirSync)); 3370 + js_set(js, lib, "mkdtempSync", js_mkfun(builtin_fs_mkdtempSync)); 2971 3371 js_set(js, lib, "rmdirSync", js_mkfun(builtin_fs_rmdirSync)); 2972 3372 js_set(js, lib, "statSync", js_mkfun(builtin_fs_statSync)); 2973 3373 js_set(js, lib, "lstatSync", js_mkfun(builtin_fs_lstatSync)); 3374 + js_set(js, lib, "utimesSync", js_mkfun(builtin_fs_utimesSync)); 3375 + js_set(js, lib, "futimesSync", js_mkfun(builtin_fs_futimesSync)); 2974 3376 js_set(js, lib, "existsSync", js_mkfun(builtin_fs_existsSync)); 2975 3377 js_set(js, lib, "accessSync", js_mkfun(builtin_fs_accessSync)); 2976 3378 js_set(js, lib, "readdirSync", js_mkfun(builtin_fs_readdirSync)); ··· 3018 3420 unsigned int len = utarray_len(pending_requests); 3019 3421 for (unsigned int i = 0; i < len; i++) { 3020 3422 fs_request_t **reqp = (fs_request_t **)utarray_eltptr(pending_requests, i); 3021 - if (reqp && *reqp) { mark(js, (*reqp)->promise); } 3022 - } 3423 + if (reqp && *reqp) { 3424 + mark(js, (*reqp)->promise); 3425 + if (is_object_type((*reqp)->target_buffer)) mark(js, (*reqp)->target_buffer); 3426 + if (is_callable((*reqp)->callback_fn)) mark(js, (*reqp)->callback_fn); 3427 + }} 3023 3428 3024 3429 for (watcher = active_watchers; watcher; watcher = watcher->next_active) { 3025 3430 if (vtype(watcher->obj) == T_OBJ) mark(js, watcher->obj);
+51
src/modules/globals.c
··· 1 1 #include <string.h> 2 2 #include <stdio.h> 3 3 #include <stdlib.h> 4 + #include <time.h> 4 5 5 6 #include "ant.h" 6 7 #include "errors.h" ··· 100 101 sv_vm_call(js->vm, js, handler, global, call_args, 1, NULL, false); 101 102 } 102 103 104 + // stub: minimal Intl 105 + static ant_value_t intl_dtf_format(ant_t *js, ant_value_t *args, int nargs) { 106 + time_t t; 107 + 108 + if (nargs >= 1 && vtype(args[0]) == T_NUM) { 109 + t = (time_t)(js_getnum(args[0]) / 1000.0); 110 + } else t = time(NULL); 111 + 112 + struct tm local; 113 + localtime_r(&t, &local); 114 + 115 + char buf[64]; 116 + int hour12 = local.tm_hour % 12; 117 + if (hour12 == 0) hour12 = 12; 118 + 119 + const char *ampm = local.tm_hour < 12 ? "AM" : "PM"; 120 + snprintf(buf, sizeof(buf), "%d:%02d:%02d %s", hour12, local.tm_min, local.tm_sec, ampm); 121 + 122 + return js_mkstr(js, buf, strlen(buf)); 123 + } 124 + 125 + static ant_value_t intl_dtf_resolvedOptions(ant_t *js, ant_value_t *args, int nargs) { 126 + ant_value_t obj = js_mkobj(js); 127 + js_set(js, obj, "locale", js_mkstr(js, "en-US", 5)); 128 + js_set(js, obj, "timeZone", js_mkstr(js, "UTC", 3)); 129 + return obj; 130 + } 131 + 132 + static ant_value_t intl_dtf_formatToParts(ant_t *js, ant_value_t *args, int nargs) { 133 + return js_mkarr(js); 134 + } 135 + 136 + static ant_value_t intl_dtf_constructor(ant_t *js, ant_value_t *args, int nargs) { 137 + ant_value_t this = js_getthis(js); 138 + 139 + ant_value_t format_fn = js_heavy_mkfun(js, intl_dtf_format, js_mkundef()); 140 + js_set(js, this, "format", format_fn); 141 + js_set(js, this, "resolvedOptions", js_mkfun(intl_dtf_resolvedOptions)); 142 + js_set(js, this, "formatToParts", js_mkfun(intl_dtf_formatToParts)); 143 + 144 + return this; 145 + } 146 + 103 147 void init_globals_module(void) { 104 148 ant_t *js = rt->js; 105 149 ant_value_t global = js_glob(js); 106 150 107 151 js_set(js, global, "reportError", js_mkfun(js_report_error)); 108 152 js_set_descriptor(js, global, "reportError", 11, JS_DESC_W | JS_DESC_C); 153 + 154 + ant_value_t intl = js_mkobj(js); 155 + ant_value_t dtf_ctor = js_heavy_mkfun(js, intl_dtf_constructor, js_mkundef()); 156 + 157 + js_mark_constructor(dtf_ctor, true); 158 + js_set(js, intl, "DateTimeFormat", dtf_ctor); 159 + js_set(js, global, "Intl", intl); 109 160 }
+37 -14
src/modules/module.c
··· 44 44 return ns; 45 45 } 46 46 47 - // require.resolve(specifier) 47 + static ant_value_t resolve_strip_file_url(ant_t *js, ant_value_t resolved) { 48 + if (is_err(resolved) || vtype(resolved) != T_STR) return resolved; 49 + 50 + ant_offset_t len = 0; 51 + ant_offset_t off = vstr(js, resolved, &len); 52 + 53 + const char *s = (const char *)(uintptr_t)(off); 54 + static const char *prefix = "file://"; 55 + 56 + if ((size_t)len >= strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0) { 57 + const char *path_part = s + strlen(prefix); 58 + size_t plen = (size_t)len - strlen(prefix); 59 + return js_mkstr(js, path_part, plen); 60 + } 61 + 62 + return resolved; 63 + } 64 + 65 + // require.resolve(specifier, options?) 48 66 static ant_value_t builtin_createRequire_resolve(ant_t *js, ant_value_t *args, int nargs) { 49 67 if (nargs < 1 || vtype(args[0]) != T_STR) 50 68 return js_mkerr(js, "require.resolve() expects a string specifier"); ··· 59 77 base_path = (const char *)(uintptr_t)(doff); 60 78 } 61 79 62 - ant_value_t resolved = js_esm_resolve_specifier(js, args[0], base_path); 63 - if (is_err(resolved)) return resolved; 64 - if (vtype(resolved) != T_STR) return resolved; 80 + ant_value_t paths_val = (nargs >= 2 && is_object_type(args[1])) 81 + ? js_get(js, args[1], "paths") : js_mkundef(); 65 82 66 - ant_offset_t len = 0; 67 - ant_offset_t off = vstr(js, resolved, &len); 68 - 69 - const char *s = (const char *)(uintptr_t)(off); 70 - static const char *prefix = "file://"; 83 + if (vtype(paths_val) != T_ARR) { 84 + ant_value_t resolved = js_esm_resolve_specifier(js, args[0], base_path); 85 + return resolve_strip_file_url(js, resolved); 86 + } 71 87 72 - if ((size_t)len >= strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0) { 73 - const char *path_part = s + strlen(prefix); 74 - size_t plen = (size_t)len - strlen(prefix); 75 - return js_mkstr(js, path_part, plen); 88 + ant_offset_t path_count = js_arr_len(js, paths_val); 89 + for (ant_offset_t i = 0; i < path_count; i++) { 90 + ant_value_t p = js_arr_get(js, paths_val, i); 91 + if (vtype(p) != T_STR) continue; 92 + 93 + char *dir = js_getstr(js, p, NULL); 94 + if (!dir) continue; 95 + 96 + ant_value_t resolved = js_esm_resolve_specifier(js, args[0], dir); 97 + if (!is_err(resolved) && vtype(resolved) == T_STR) 98 + return resolve_strip_file_url(js, resolved); 76 99 } 77 100 78 - return resolved; 101 + return js_mkerr(js, "Cannot resolve module"); 79 102 } 80 103 81 104 // createRequire(filename)
+6 -7
src/modules/structured-clone.c
··· 9 9 #include "internal.h" 10 10 #include "descriptors.h" 11 11 12 + #include "modules/date.h" 12 13 #include "modules/buffer.h" 13 14 #include "modules/collections.h" 14 15 #include "modules/domexception.h" ··· 276 277 return clone; 277 278 } 278 279 279 - ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); 280 - if ( 281 - vtype(date_proto) != T_UNDEF 282 - && is_object_type(date_proto) 283 - && js_is_prototype_of(js, date_proto, val) 284 - ) { 280 + if (is_date_instance(val)) { 285 281 ant_value_t clone = js_mkobj(js); 286 - js_set_proto_init(clone, date_proto); 282 + ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); 283 + if (is_object_type(date_proto)) js_set_proto_init(clone, date_proto); 284 + 287 285 js_set_slot(clone, SLOT_DATA, js_get_slot(val, SLOT_DATA)); 286 + js_set_slot(clone, SLOT_BRAND, js_mknum(BRAND_DATE)); 288 287 sc_add(seen, val, clone); 289 288 290 289 return clone;
+134 -30
src/modules/zlib.c
··· 119 119 } 120 120 121 121 typedef struct { ant_t *js; ant_value_t obj; } brotli_emit_ctx_t; 122 + typedef struct { ant_t *js; ant_value_t arr; bool error; } brotli_collect_ctx_t; 122 123 123 124 static int brotli_emit_cb(void *ctx, const uint8_t *chunk, size_t len) { 124 125 brotli_emit_ctx_t *ec = (brotli_emit_ctx_t *)ctx; 125 126 zlib_emit_data(ec->js, ec->obj, chunk, len); 127 + return 0; 128 + } 129 + 130 + static int brotli_collect_cb(void *ctx, const uint8_t *chunk, size_t len) { 131 + brotli_collect_ctx_t *ec = (brotli_collect_ctx_t *)ctx; 132 + ant_value_t buf = zlib_make_buffer(ec->js, chunk, len); 133 + if (is_err(buf)) { ec->error = true; return -1; } 134 + js_arr_push(ec->js, ec->arr, buf); 126 135 return 0; 127 136 } 128 137 ··· 179 188 return js_mkundef(); 180 189 } 181 190 191 + static ant_value_t zlib_process_chunk_sync( 192 + ant_t *js, zlib_stream_t *st, 193 + const uint8_t *input, size_t input_len, 194 + int flush_flag 195 + ) { 196 + ant_value_t arr = js_mkarr(js); 197 + 198 + if (st->brotli) { 199 + brotli_collect_ctx_t ctx = { js, arr, false }; 200 + int rc = 0; 201 + if (input && input_len > 0) 202 + rc = brotli_stream_process(st->brotli, input, input_len, brotli_collect_cb, &ctx); 203 + if (rc >= 0 && flush_flag == Z_FINISH) 204 + rc = brotli_stream_finish(st->brotli, brotli_collect_cb, &ctx); 205 + if (rc < 0 || ctx.error) return js_mkerr(js, "brotli operation failed"); 206 + return arr; 207 + } 208 + 209 + bool compress = zlib_kind_is_compress(st->kind); 210 + uint8_t out[ZLIB_CHUNK]; 211 + 212 + st->strm.next_in = input ? (Bytef *)input : NULL; 213 + st->strm.avail_in = input ? (uInt)input_len : 0; 214 + 215 + int ret; 216 + do { 217 + st->strm.next_out = out; 218 + st->strm.avail_out = ZLIB_CHUNK; 219 + 220 + if (compress) { 221 + ret = deflate(&st->strm, flush_flag); 222 + if (ret == Z_STREAM_ERROR) return js_mkerr(js, "zlib deflate error"); 223 + } else { 224 + ret = inflate(&st->strm, flush_flag); 225 + if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) 226 + return js_mkerr(js, "zlib inflate error"); 227 + } 228 + 229 + size_t have = ZLIB_CHUNK - st->strm.avail_out; 230 + if (have > 0) { 231 + ant_value_t buf = zlib_make_buffer(js, out, have); 232 + if (is_err(buf)) return buf; 233 + js_arr_push(js, arr, buf); 234 + } 235 + if (ret == Z_STREAM_END) break; 236 + } while (st->strm.avail_out == 0); 237 + 238 + return arr; 239 + } 240 + 241 + static ant_value_t js_zlib_process_chunk(ant_t *js, ant_value_t *args, int nargs) { 242 + zlib_stream_t *st = zlib_stream_ptr(js_getthis(js)); 243 + if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid zlib stream"); 244 + if (st->destroyed || st->ended) return js_mkarr(js); 245 + 246 + const uint8_t *input = NULL; 247 + size_t input_len = 0; 248 + if (nargs >= 1 && is_object_type(args[0])) 249 + buffer_source_get_bytes(js, args[0], &input, &input_len); 250 + 251 + int flush_flag = Z_NO_FLUSH; 252 + if (nargs >= 2 && vtype(args[1]) == T_NUM) 253 + flush_flag = (int)js_getnum(args[1]); 254 + 255 + return zlib_process_chunk_sync(js, st, input, input_len, flush_flag); 256 + } 257 + 182 258 static ant_value_t js_zlib_write(ant_t *js, ant_value_t *args, int nargs) { 183 259 zlib_stream_t *st = zlib_stream_ptr(js_getthis(js)); 184 260 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid zlib stream"); ··· 353 429 354 430 js_set(js, obj, "readable", js_true); 355 431 js_set(js, obj, "writable", js_true); 432 + js_set(js, obj, "_processChunk", js_mkfun(js_zlib_process_chunk)); 433 + 434 + ant_value_t handle_obj = js_mkobj(js); 435 + js_set(js, handle_obj, "close", js_mkfun(js_zlib_destroy)); 436 + js_set(js, obj, "_handle", handle_obj); 356 437 357 438 st->obj = obj; 358 439 zlib_add_active(st); ··· 407 488 size_t newcap = b->cap ? b->cap * 2 : 4096; 408 489 while (newcap < b->len + n) newcap *= 2; 409 490 uint8_t *p = realloc(b->data, newcap); 491 + 410 492 if (!p) { b->error = 1; return -1; } 411 493 b->data = p; 412 494 b->cap = newcap; 413 495 } 496 + 414 497 memcpy(b->data + b->len, chunk, n); 415 498 b->len += n; 499 + 416 500 return 0; 417 501 } 418 502 ··· 652 736 js_mkfun(js_zlib_get_bytes_written), JS_DESC_C); 653 737 } 654 738 739 + static ant_value_t zlib_mkctor(ant_t *js, ant_value_t (*fn)(ant_t *, ant_value_t *, int)) { 740 + ant_value_t ctor = js_heavy_mkfun(js, fn, js_mkundef()); 741 + js_mark_constructor(ctor, true); 742 + return ctor; 743 + } 744 + 655 745 ant_value_t zlib_library(ant_t *js) { 656 746 zlib_init_proto(js); 657 747 658 748 ant_value_t lib = js_mkobj(js); 659 749 ant_value_t consts = make_constants(js); 660 750 661 - js_set(js, lib, "constants", consts); 751 + js_set(js, lib, "gzip", js_mkfun(js_gzip)); 752 + js_set(js, lib, "gzipSync", js_mkfun(js_gzipSync)); 753 + js_set(js, lib, "Gzip", zlib_mkctor(js, js_create_gzip)); 754 + js_set(js, lib, "createGzip", js_mkfun(js_create_gzip)); 662 755 663 - js_set(js, lib, "createGzip", js_mkfun(js_create_gzip)); 664 - js_set(js, lib, "createGunzip", js_mkfun(js_create_gunzip)); 665 - js_set(js, lib, "createDeflate", js_mkfun(js_create_deflate)); 666 - js_set(js, lib, "createInflate", js_mkfun(js_create_inflate)); 667 - js_set(js, lib, "createDeflateRaw", js_mkfun(js_create_deflate_raw)); 668 - js_set(js, lib, "createInflateRaw", js_mkfun(js_create_inflate_raw)); 669 - js_set(js, lib, "createUnzip", js_mkfun(js_create_unzip)); 670 - js_set(js, lib, "createBrotliCompress", js_mkfun(js_create_brotli_compress)); 671 - js_set(js, lib, "createBrotliDecompress", js_mkfun(js_create_brotli_decompress)); 756 + js_set(js, lib, "gunzip", js_mkfun(js_gunzip)); 757 + js_set(js, lib, "gunzipSync", js_mkfun(js_gunzipSync)); 758 + js_set(js, lib, "Gunzip", zlib_mkctor(js, js_create_gunzip)); 759 + js_set(js, lib, "createGunzip", js_mkfun(js_create_gunzip)); 672 760 673 - js_set(js, lib, "gzip", js_mkfun(js_gzip)); 674 - js_set(js, lib, "gunzip", js_mkfun(js_gunzip)); 675 - js_set(js, lib, "deflate", js_mkfun(js_deflate)); 676 - js_set(js, lib, "inflate", js_mkfun(js_inflate)); 677 - js_set(js, lib, "deflateRaw", js_mkfun(js_deflateRaw)); 678 - js_set(js, lib, "inflateRaw", js_mkfun(js_inflateRaw)); 679 - js_set(js, lib, "unzip", js_mkfun(js_unzip)); 680 - js_set(js, lib, "brotliCompress", js_mkfun(js_brotliCompress)); 681 - js_set(js, lib, "brotliDecompress", js_mkfun(js_brotliDecompress)); 761 + js_set(js, lib, "deflate", js_mkfun(js_deflate)); 762 + js_set(js, lib, "deflateSync", js_mkfun(js_deflateSync)); 763 + js_set(js, lib, "Deflate", zlib_mkctor(js, js_create_deflate)); 764 + js_set(js, lib, "createDeflate", js_mkfun(js_create_deflate)); 682 765 683 - js_set(js, lib, "gzipSync", js_mkfun(js_gzipSync)); 684 - js_set(js, lib, "gunzipSync", js_mkfun(js_gunzipSync)); 685 - js_set(js, lib, "deflateSync", js_mkfun(js_deflateSync)); 686 - js_set(js, lib, "inflateSync", js_mkfun(js_inflateSync)); 687 - js_set(js, lib, "deflateRawSync", js_mkfun(js_deflateRawSync)); 688 - js_set(js, lib, "inflateRawSync", js_mkfun(js_inflateRawSync)); 689 - js_set(js, lib, "unzipSync", js_mkfun(js_unzipSync)); 690 - js_set(js, lib, "brotliCompressSync", js_mkfun(js_brotliCompressSync)); 691 - js_set(js, lib, "brotliDecompressSync", js_mkfun(js_brotliDecompressSync)); 766 + js_set(js, lib, "inflate", js_mkfun(js_inflate)); 767 + js_set(js, lib, "inflateSync", js_mkfun(js_inflateSync)); 768 + js_set(js, lib, "Inflate", zlib_mkctor(js, js_create_inflate)); 769 + js_set(js, lib, "createInflate", js_mkfun(js_create_inflate)); 692 770 693 - js_set(js, lib, "crc32", js_mkfun(js_zlib_crc32)); 694 - js_set(js, lib, "default", lib); 771 + js_set(js, lib, "deflateRaw", js_mkfun(js_deflateRaw)); 772 + js_set(js, lib, "deflateRawSync", js_mkfun(js_deflateRawSync)); 773 + js_set(js, lib, "DeflateRaw", zlib_mkctor(js, js_create_deflate_raw)); 774 + js_set(js, lib, "createDeflateRaw", js_mkfun(js_create_deflate_raw)); 695 775 776 + js_set(js, lib, "inflateRaw", js_mkfun(js_inflateRaw)); 777 + js_set(js, lib, "inflateRawSync", js_mkfun(js_inflateRawSync)); 778 + js_set(js, lib, "InflateRaw", zlib_mkctor(js, js_create_inflate_raw)); 779 + js_set(js, lib, "createInflateRaw", js_mkfun(js_create_inflate_raw)); 780 + 781 + js_set(js, lib, "unzip", js_mkfun(js_unzip)); 782 + js_set(js, lib, "unzipSync", js_mkfun(js_unzipSync)); 783 + js_set(js, lib, "Unzip", zlib_mkctor(js, js_create_unzip)); 784 + js_set(js, lib, "createUnzip", js_mkfun(js_create_unzip)); 785 + 786 + js_set(js, lib, "brotliCompress", js_mkfun(js_brotliCompress)); 787 + js_set(js, lib, "brotliCompressSync", js_mkfun(js_brotliCompressSync)); 788 + js_set(js, lib, "BrotliCompress", zlib_mkctor(js, js_create_brotli_compress)); 789 + js_set(js, lib, "createBrotliCompress", js_mkfun(js_create_brotli_compress)); 790 + 791 + js_set(js, lib, "brotliDecompress", js_mkfun(js_brotliDecompress)); 792 + js_set(js, lib, "brotliDecompressSync", js_mkfun(js_brotliDecompressSync)); 793 + js_set(js, lib, "BrotliDecompress", zlib_mkctor(js, js_create_brotli_decompress)); 794 + js_set(js, lib, "createBrotliDecompress", js_mkfun(js_create_brotli_decompress)); 795 + 796 + js_set(js, lib, "default", lib); 797 + js_set(js, lib, "constants", consts); 798 + js_set(js, lib, "crc32", js_mkfun(js_zlib_crc32)); 696 799 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "zlib", 4)); 800 + 697 801 return lib; 698 802 } 699 803
+13 -5
src/silver/compiler.c
··· 1180 1180 if (!prop) continue; 1181 1181 if (prop->type == N_PROPERTY) 1182 1182 hoist_lexical_pattern(c, prop->right, is_const); 1183 + else if (prop->type == N_REST || prop->type == N_SPREAD) 1184 + hoist_lexical_pattern(c, prop->right, is_const); 1183 1185 } 1184 1186 break; 1185 1187 default: ··· 2263 2265 static void compile_new(sv_compiler_t *c, sv_ast_t *node) { 2264 2266 compile_expr(c, node->left); 2265 2267 emit_op(c, OP_DUP); 2266 - int argc = node->args.count; 2267 - for (int i = 0; i < argc; i++) 2268 - compile_expr(c, node->args.items[i]); 2269 - emit_op(c, OP_NEW); 2270 - emit_u16(c, (uint16_t)argc); 2268 + if (call_has_spread_arg(node)) { 2269 + compile_call_args_array(c, node); 2270 + emit_op(c, OP_NEW_APPLY); 2271 + emit_u16(c, 1); 2272 + } else { 2273 + int argc = node->args.count; 2274 + for (int i = 0; i < argc; i++) 2275 + compile_expr(c, node->args.items[i]); 2276 + emit_op(c, OP_NEW); 2277 + emit_u16(c, (uint16_t)argc); 2278 + } 2271 2279 } 2272 2280 2273 2281 static bool sv_node_has_optional_base(sv_ast_t *n) {
+4 -3
src/silver/engine.c
··· 1079 1079 DISPATCH(); 1080 1080 } 1081 1081 1082 - L_NEW: { VM_CHECK(sv_op_new(vm, js, ip)); NEXT(3); } 1083 - L_APPLY: { VM_CHECK(sv_op_apply(vm, js, ip)); NEXT(3); } 1084 - L_EVAL: { VM_CHECK(sv_op_eval(vm, js, frame, ip)); NEXT(5); } 1082 + L_NEW: { VM_CHECK(sv_op_new(vm, js, ip)); NEXT(3); } 1083 + L_APPLY: { VM_CHECK(sv_op_apply(vm, js, ip)); NEXT(3); } 1084 + L_NEW_APPLY: { VM_CHECK(sv_op_new_apply(vm, js, ip)); NEXT(3); } 1085 + L_EVAL: { VM_CHECK(sv_op_eval(vm, js, frame, ip)); NEXT(5); } 1085 1086 1086 1087 // TODO: make the methods below DRY 1087 1088 L_RETURN: {
+16 -7
src/silver/ops/async.h
··· 165 165 coro->active_parent = js->active_async_coro; 166 166 js->active_async_coro = coro; 167 167 168 - ant_value_t result = sv_execute_entry(async_vm, func, this_val, NULL, 0); 169 - js->active_async_coro = coro->active_parent; 170 - coro->active_parent = NULL; 168 + ant_value_t result = sv_execute_entry( 169 + async_vm, func, 170 + this_val, NULL, 0 171 + ); 171 172 172 173 if (async_vm->suspended) { 174 + js->active_async_coro = coro->active_parent; 175 + coro->active_parent = NULL; 173 176 enqueue_coroutine(coro); 174 177 GC_ROOT_RESTORE(js, root_mark); 175 178 return promise; ··· 182 185 js->thrown_value = js_mkundef(); 183 186 js_reject_promise(js, promise, reject_value); 184 187 } else js_resolve_promise(js, promise, result); 188 + 189 + js->active_async_coro = coro->active_parent; 190 + coro->active_parent = NULL; 185 191 186 192 free_coroutine(coro); 187 193 GC_ROOT_RESTORE(js, root_mark); ··· 365 371 js->active_async_coro = coro; 366 372 367 373 ant_value_t result = sv_execute_closure_entry( 368 - async_vm, closure, callee_func, super_val, this_val, args, argc, NULL 374 + async_vm, closure, callee_func, 375 + super_val, this_val, args, argc, NULL 369 376 ); 370 377 371 - js->active_async_coro = coro->active_parent; 372 - coro->active_parent = NULL; 373 - 374 378 if (async_vm->suspended) { 379 + js->active_async_coro = coro->active_parent; 380 + coro->active_parent = NULL; 375 381 enqueue_coroutine(coro); 376 382 GC_ROOT_RESTORE(js, root_mark); 377 383 return promise; ··· 385 391 js_reject_promise(js, promise, reject_value); 386 392 } else js_resolve_promise(js, promise, result); 387 393 394 + js->active_async_coro = coro->active_parent; 395 + coro->active_parent = NULL; 396 + 388 397 free_coroutine(coro); 389 398 GC_ROOT_RESTORE(js, root_mark); 390 399
+54
src/silver/ops/calls.h
··· 119 119 return result; 120 120 } 121 121 122 + static inline ant_value_t sv_op_new_apply(sv_vm_t *vm, ant_t *js, uint8_t *ip) { 123 + uint16_t argc = sv_get_u16(ip + 1); 124 + ant_value_t *args = &vm->stack[vm->sp - argc]; 125 + ant_value_t new_target = vm->stack[vm->sp - argc - 1]; 126 + ant_value_t func = vm->stack[vm->sp - argc - 2]; 127 + ant_value_t record_func = func; 128 + js->new_target = new_target; 129 + 130 + sv_call_args_t call; 131 + sv_call_args_reset(&call, args, (int)argc); 132 + ant_value_t norm = sv_apply_normalize_args(js, &call); 133 + if (is_err(norm)) { vm->sp -= argc + 2; return norm; } 134 + 135 + if (vtype(func) == T_OBJ && is_proxy(func)) { 136 + ant_value_t result = js_proxy_construct(js, func, call.args, call.argc, new_target); 137 + sv_call_args_release(&call); 138 + vm->sp -= argc + 2; 139 + if (is_err(result)) return result; 140 + vm->stack[vm->sp++] = result; 141 + return result; 142 + } 143 + if (!js_is_constructor(js, func)) { 144 + sv_call_args_release(&call); 145 + vm->sp -= argc + 2; 146 + return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor"); 147 + } 148 + 149 + ant_value_t proto = js_mkundef(); 150 + if (vtype(func) == T_FUNC) { 151 + ant_value_t proto_source = func; 152 + ant_value_t func_obj = js_func_obj(func); 153 + ant_value_t target_func = js_get_slot(func_obj, SLOT_TARGET_FUNC); 154 + if (vtype(target_func) == T_FUNC) { 155 + proto_source = target_func; 156 + record_func = target_func; 157 + } 158 + proto = js_getprop_fallback(js, proto_source, "prototype"); 159 + } 160 + 161 + ant_value_t obj = js_mkobj_with_inobj_limit(js, sv_tfb_ctor_inobj_limit(record_func)); 162 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 163 + ant_value_t ctor_this = obj; 164 + ant_value_t result = sv_vm_call(vm, js, func, obj, call.args, call.argc, &ctor_this, true); 165 + sv_call_args_release(&call); 166 + vm->sp -= argc + 2; 167 + if (is_err(result)) return result; 168 + ant_value_t final_obj = 169 + is_object_type(result) ? result 170 + : (is_object_type(ctor_this) ? ctor_this : obj); 171 + sv_tfb_record_ctor_prop_count(record_func, final_obj); 172 + vm->stack[vm->sp++] = final_obj; 173 + return result; 174 + } 175 + 122 176 static inline ant_value_t sv_op_eval(sv_vm_t *vm, ant_t *js, sv_frame_t *frame, uint8_t *ip) { 123 177 ant_value_t code = vm->stack[--vm->sp]; 124 178 if (vtype(code) != T_STR) {
+3 -1
src/silver/ops/objects.h
··· 48 48 ant_value_t desc_obj = js_as_obj(obj); 49 49 bool is_getter = (flags & 1) != 0; 50 50 bool is_setter = (flags & 2) != 0; 51 - if (vtype(key) == T_SYMBOL && !is_getter && !is_setter) { 51 + if (vtype(key) == T_SYMBOL) { 52 + if (is_getter) { js_set_sym_getter_desc(js, desc_obj, key, fn, JS_DESC_E | JS_DESC_C); return; } 53 + if (is_setter) { js_set_sym_setter_desc(js, desc_obj, key, fn, JS_DESC_E | JS_DESC_C); return; } 52 54 js_set_sym(js, obj, key, fn); 53 55 return; 54 56 }
+12 -1
src/silver/ops/super.h
··· 12 12 ant_value_t prop = vm->stack[--vm->sp]; 13 13 ant_value_t obj = vm->stack[--vm->sp]; 14 14 ant_value_t receiver = vm->stack[--vm->sp]; 15 - ant_value_t proto = js_get_proto(js, obj); 15 + ant_value_t proto; 16 + 17 + if (vtype(obj) == T_FUNC && vtype(receiver) != T_FUNC) { 18 + proto = js_getprop_fallback(js, obj, "prototype"); 19 + } else proto = js_get_proto(js, obj); 20 + 21 + if (vtype(prop) == T_SYMBOL) { 22 + vm->stack[vm->sp++] = js_get_sym_with_receiver(js, proto, prop, receiver); 23 + return; 24 + } 25 + 16 26 ant_value_t key_str = coerce_to_str(js, prop); 17 27 ant_offset_t klen; 18 28 ant_offset_t koff = vstr(js, key_str, &klen); 29 + 19 30 const char *kptr = (const char *)(uintptr_t)(koff); 20 31 vm->stack[vm->sp++] = js_getprop_super(js, proto, receiver, kptr); 21 32 }
+2 -2
src/silver/swarm.c
··· 1675 1675 case OP_CALL: case OP_CALL_METHOD: 1676 1676 case OP_TAIL_CALL: case OP_TAIL_CALL_METHOD: 1677 1677 case OP_ARRAY: case OP_NEW: 1678 - case OP_APPLY: 1678 + case OP_APPLY: case OP_NEW_APPLY: 1679 1679 f.needs_args_buf = true; 1680 1680 if (op == OP_TAIL_CALL || op == OP_TAIL_CALL_METHOD) 1681 1681 f.needs_tco_args = true; ··· 1741 1741 case OP_CALL: case OP_CALL_METHOD: 1742 1742 case OP_CALL_IS_PROTO: 1743 1743 case OP_TAIL_CALL: case OP_TAIL_CALL_METHOD: 1744 - case OP_APPLY: 1744 + case OP_APPLY: case OP_NEW_APPLY: 1745 1745 case OP_GET_GLOBAL: case OP_GET_GLOBAL_UNDEF: 1746 1746 case OP_PUT_GLOBAL: 1747 1747 case OP_GET_FIELD: case OP_GET_FIELD2: case OP_PUT_FIELD:
+16 -10
src/sugar.c
··· 72 72 static size_t calculate_coro_stack_size(void) { 73 73 static size_t cached_size = 0; 74 74 if (coro_stack_size_initialized) return cached_size; 75 + 75 76 coro_stack_size_initialized = true; 76 77 const char *env_stack = getenv("ANT_CORO_STACK_SIZE"); 78 + 77 79 if (env_stack) { 78 - size_t size = (size_t)atoi(env_stack) * 1024; 79 - if (size >= 32 * 1024 && size <= 8 * 1024 * 1024) { 80 - cached_size = size; return cached_size; 81 - } 82 - } 80 + size_t size = (size_t)atoi(env_stack) * 1024; 81 + if (size >= 32 * 1024 && size <= 8 * 1024 * 1024) { 82 + cached_size = size; return cached_size; 83 + }} 84 + 83 85 return cached_size; 84 86 } 85 87 ··· 105 107 js->active_async_coro = coro; 106 108 ant_value_t result = sv_resume_suspended(coro->sv_vm); 107 109 108 - js->active_async_coro = coro->active_parent; 109 - coro->active_parent = NULL; 110 - 111 110 coro->is_settled = false; 112 111 coro->awaited_promise = js_mkundef(); 113 - if (coro->sv_vm->suspended) return; 114 - remove_coroutine(coro); 112 + 113 + if (coro->sv_vm->suspended) { 114 + js->active_async_coro = coro->active_parent; 115 + coro->active_parent = NULL; 116 + return; 117 + } remove_coroutine(coro); 115 118 116 119 if (is_err(result)) { 117 120 ant_value_t reject_value = js->thrown_value; ··· 122 125 } else js_resolve_promise(js, coro->async_promise, result); 123 126 124 127 js_maybe_drain_microtasks_after_async_settle(js); 128 + js->active_async_coro = coro->active_parent; 129 + 130 + coro->active_parent = NULL; 125 131 free_coroutine(coro); 126 132 127 133 return;
+3
src/types/modules/fs.d.ts
··· 50 50 function readFile(path: string): Promise<Uint8Array>; 51 51 function readFileSync(path: string, encoding: Encoding | { encoding: Encoding }): string; 52 52 function readFileSync(path: string): Uint8Array; 53 + function read(fd: number, buffer: ArrayBufferView, offset?: number, length?: number, position?: number | null, callback?: (err: Error | null, bytesRead: number, buffer: ArrayBufferView) => void): Promise<number>; 53 54 function readSync(fd: number, buffer: ArrayBufferView, offset?: number, length?: number, position?: number | null): number; 54 55 function stream(path: string): Promise<string>; 55 56 function open(path: string, flags?: string, mode?: number): Promise<number>; ··· 71 72 function unlinkSync(path: string): void; 72 73 function mkdir(path: string, options?: { recursive?: boolean; mode?: number }): Promise<void>; 73 74 function mkdirSync(path: string, options?: number | { recursive?: boolean; mode?: number }): void; 75 + function mkdtemp(prefix: string): Promise<string>; 76 + function mkdtempSync(prefix: string): string; 74 77 function rmdir(path: string): Promise<void>; 75 78 function rmdirSync(path: string): void; 76 79 function stat(path: string): Promise<Stats>;