diff --git a/examples/results.txt b/examples/results.txt index 89602ce..c6cf437 100644 --- a/examples/results.txt +++ b/examples/results.txt @@ -1307,7 +1307,7 @@ compat-table/es2019/misc.Function-toString.arrows.js: failed compat-table/es2019/misc.Function-toString.class-explicit-constructor.js: failed compat-table/es2019/misc.Function-toString.class-implicit-constructor.js: failed compat-table/es2019/misc.Function-toString.computed-names.js: failed -compat-table/es2019/misc.Function-toString.native-code.js: failed +compat-table/es2019/misc.Function-toString.native-code.js: OK compat-table/es2019/misc.Function-toString.unicode-escapes.js: failed compat-table/es2019/misc.JSON-stringify-well-formed.js: OK compat-table/es2019/misc.JSON-superset.line-separator.js: OK diff --git a/include/ant.h b/include/ant.h index 6151cb5..7bd1823 100644 --- a/include/ant.h +++ b/include/ant.h @@ -102,20 +102,34 @@ ant_value_t js_get_sym_with_receiver(ant_t *, ant_value_t obj, ant_value_t sym, ant_value_t js_mkobj(ant_t *); ant_value_t js_mkobj_with_inobj_limit(ant_t *, uint8_t inobj_limit); + ant_value_t js_newobj(ant_t *); ant_value_t js_mkarr(ant_t *); ant_value_t js_mkstr(ant_t *, const void *, size_t); ant_value_t js_mkstr_permanent(ant_t *, const void *, size_t); ant_value_t js_mkbigint(ant_t *, const char *digits, size_t len, bool negative); + ant_value_t js_mksym(ant_t *, const char *desc); ant_value_t js_mksym_well_known(ant_t *, const char *desc); -ant_value_t js_mkfun(ant_value_t (*fn)(ant_t *, ant_value_t *, int)); -ant_value_t js_heavy_mkfun(ant_t *js, ant_value_t (*fn)(ant_t *, ant_value_t *, int), ant_value_t data); +ant_value_t js_mkfun_meta(const ant_cfunc_meta_t *meta); +ant_value_t js_mkfun_dyn(ant_cfunc_t fn); +ant_value_t js_heavy_mkfun(ant_t *js, ant_value_t (*fn)(ant_t *, ant_value_t *, int), ant_value_t data); ant_value_t js_mkprop_fast(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); ant_offset_t js_mkprop_fast_off(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); +#define js_mkfun(fn) ({ \ + static const ant_cfunc_meta_t _ant_cfunc_meta = { (fn), NULL, 0, 0 }; \ + js_mkfun_meta(&_ant_cfunc_meta); \ +}) + +#define js_mkfun_arity(fn, arity) ({ \ + static const ant_cfunc_meta_t _ant_cfunc_meta = { (fn), NULL, (arity), 0 }; \ + js_mkfun_meta(&_ant_cfunc_meta); \ +}) + void js_set(ant_t *, ant_value_t, const char *, ant_value_t); +void js_set_exact(ant_t *, ant_value_t, const char *, ant_value_t); void js_set_sym(ant_t *, ant_value_t obj, ant_value_t sym, ant_value_t val); void js_set_symbol(ant_t *, ant_value_t obj, const char *key, ant_value_t val); void js_saveval(ant_t *js, ant_offset_t off, ant_value_t v); diff --git a/include/internal.h b/include/internal.h index ff70950..192d4e7 100644 --- a/include/internal.h +++ b/include/internal.h @@ -253,6 +253,14 @@ struct ant_isolate_t { uint8_t cap; } cfunc_promote_cache; + struct { + const ant_cfunc_meta_t **base_meta; + const char **name_ptr; + ant_value_t *named; + uint16_t len; + uint16_t cap; + } cfunc_name_cache; + bool owns_mem; bool fatal_error; bool thrown_exists; @@ -311,13 +319,6 @@ typedef enum { PROP_META_SYMBOL = 1, } prop_meta_key_t; -typedef ant_value_t - (*js_cfunc_fn_t)(ant_t *, ant_value_t *, int); - -static inline js_cfunc_fn_t js_as_cfunc(ant_value_t fn_val) { - return (js_cfunc_fn_t)(uintptr_t)vdata(fn_val); -} - static inline bool is_err(ant_value_t v) { return vtype(v) == T_ERR; } @@ -339,6 +340,25 @@ static inline bool is_callable(ant_value_t v) { return t == T_FUNC || t == T_CFUNC; } +static inline const ant_cfunc_meta_t *js_as_cfunc_meta(ant_value_t fn_val) { + return (const ant_cfunc_meta_t *)(uintptr_t)vdata(fn_val); +} + +static inline ant_cfunc_t js_as_cfunc(ant_value_t fn_val) { + const ant_cfunc_meta_t *meta = js_as_cfunc_meta(fn_val); + return meta ? meta->fn : NULL; +} + +static inline uint32_t js_cfunc_length(ant_value_t fn_val) { + const ant_cfunc_meta_t *meta = js_as_cfunc_meta(fn_val); + return meta ? meta->length : 0; +} + +static inline bool js_cfunc_same_entrypoint(ant_value_t fn_val, ant_cfunc_t fn) { + const ant_cfunc_meta_t *meta = js_as_cfunc_meta(fn_val); + return meta && meta->fn == fn; +} + bool is_internal_prop(const char *key, ant_offset_t klen); size_t uint_to_str(char *buf, size_t bufsize, uint64_t val); int extract_array_args(ant_t *js, ant_value_t arr, ant_value_t **out_args); @@ -353,6 +373,7 @@ bool js_obj_ensure_unique_shape(ant_object_t *obj); ant_value_t js_propref_load(ant_t *js, ant_offset_t handle); ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uint8_t attrs); ant_value_t mkprop_interned(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs); +ant_value_t mkprop_interned_exact(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs); ant_value_t setprop_cstr(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); ant_value_t setprop_interned(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); ant_value_t js_define_property(ant_t *js, ant_value_t obj, ant_value_t prop, ant_value_t descriptor, bool reflect_mode); @@ -546,6 +567,16 @@ static inline ant_value_t defmethod(ant_t *js, ant_value_t obj, const char *name ); } +static inline ant_value_t defalias(ant_t *js, ant_value_t obj, const char *name, size_t len, ant_value_t fn) { + const char *interned = intern_string(name, len); + if (!interned) return js_mkerr(js, "oom"); + + return mkprop_interned_exact( + js, obj, interned, fn, + ANT_PROP_ATTR_WRITABLE | ANT_PROP_ATTR_CONFIGURABLE + ); +} + static inline ant_flat_string_t *str_flat_from_bytes(const char *str) { return (ant_flat_string_t *)((char *)str - offsetof(ant_flat_string_t, bytes)); } @@ -594,19 +625,19 @@ static inline void js_set_module_default(ant_t *js, ant_value_t lib, ant_value_t } ant_value_t js_cfunc_promote(ant_t *js, ant_value_t cfunc); +ant_value_t js_cfunc_expose_named(ant_t *js, ant_value_t cfunc, const char *name, size_t name_len); static inline ant_value_t js_cfunc_lookup_promoted(ant_t *js, ant_value_t cfunc) { uintptr_t ptr = vdata(cfunc); - for (uint8_t i = 0; i < js->cfunc_promote_cache.len; i++) { - if (js->cfunc_promote_cache.cfunc_ptr[i] == ptr) - return js->cfunc_promote_cache.promoted[i]; - } + for (uint8_t i = 0; i < js->cfunc_promote_cache.len; i++) if ( + js->cfunc_promote_cache.cfunc_ptr[i] == ptr + ) return js->cfunc_promote_cache.promoted[i]; return cfunc; } static inline ant_value_t js_make_ctor(ant_t *js, ant_cfunc_t fn, ant_value_t proto, const char *name, size_t nlen) { ant_value_t obj = js_mkobj(js); - js_set_slot(obj, SLOT_CFUNC, js_mkfun(fn)); + js_set_slot(obj, SLOT_CFUNC, js_mkfun_dyn(fn)); js_mkprop_fast(js, obj, "prototype", 9, proto); js_mkprop_fast(js, obj, "name", 4, js_mkstr(js, name, nlen)); js_set_descriptor(js, obj, "name", 4, 0); diff --git a/include/types.h b/include/types.h index 5a8167f..d7c6cbb 100644 --- a/include/types.h +++ b/include/types.h @@ -26,7 +26,16 @@ typedef size_t ant_handle_t; typedef uint64_t ant_offset_t; typedef uint64_t ant_value_t; -typedef ant_value_t (*ant_cfunc_t) (ant_t *, ant_value_t *, int); +typedef ant_value_t + (*ant_cfunc_t) + (ant_t *, ant_value_t *, int); + +typedef struct ant_cfunc_meta { + ant_cfunc_t fn; + const char *name; + uint32_t length; + uint8_t flags; +} ant_cfunc_meta_t; #define ant_bind_t ant_value_t func, ant_value_t this_val #define ant_params_t ant_t *js, ant_value_t *args, int nargs diff --git a/src/ant.c b/src/ant.c index 225f5ca..372d5d7 100644 --- a/src/ant.c +++ b/src/ant.c @@ -574,8 +574,11 @@ static bool streq(const char *buf, size_t len, const char *p, size_t n); static bool parse_func_params(ant_t *js, uint8_t *flags, int *out_count); static bool try_dynamic_setter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value); static uintptr_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *setter_out, bool *has_setter_out); -static ant_value_t call_proto_accessor(ant_t *js, ant_value_t prim, ant_value_t accessor, bool has_accessor, ant_value_t *arg, int arg_count, bool is_setter); + static ant_value_t get_prototype_for_type(ant_t *js, uint8_t type); +static ant_value_t js_cfunc_name_value(ant_t *js, ant_value_t cfunc); +static ant_value_t js_cfunc_length_value(ant_value_t cfunc); +static ant_value_t js_expose_cfunc_for_key(ant_t *js, ant_value_t value, const char *key, size_t key_len); static inline ant_value_t lkp_val(ant_t *js, ant_value_t obj, const char *buf, size_t len); static inline ant_value_t lkp_sym_proto_val(ant_t *js, ant_value_t obj, ant_offset_t sym_off); @@ -937,11 +940,44 @@ static ant_value_t get_obj_ctor(ant_t *js, ant_value_t obj) { } static const char *get_func_name(ant_t *js, ant_value_t func, ant_offset_t *out_len) { + if (vtype(func) == T_CFUNC) { + const ant_cfunc_meta_t *meta = js_as_cfunc_meta(func); + if (!meta || !meta->name) return NULL; + if (out_len) *out_len = (ant_offset_t)strlen(meta->name); + return meta->name; + } + if (vtype(func) != T_FUNC) return NULL; - ant_value_t name = lkp_val(js, js_func_obj(func), "name", 4); - if (vtype(name) != T_STR) return NULL; - ant_offset_t str_off = vstr(js, name, out_len); - return (const char *)(uintptr_t)(str_off); + ant_value_t func_obj = js_func_obj(func); + ant_value_t name = lkp_val(js, func_obj, "name", 4); + + if (vtype(name) == T_STR) { + ant_offset_t str_off = vstr(js, name, out_len); + return (const char *)(uintptr_t)(str_off); + } + + ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC); + if (vtype(cfunc_slot) == T_CFUNC) return get_func_name(js, cfunc_slot, out_len); + + return NULL; +} + +static bool js_cfunc_try_get_own(ant_t *js, ant_value_t cfunc, const char *key, size_t key_len, ant_value_t *out) { + if (key_len == 4 && memcmp(key, "name", 4) == 0) { + ant_value_t name = js_cfunc_name_value(js, cfunc); + if (vtype(name) != T_UNDEF) { + *out = name; + return true; + } + return false; + } + + if (key_len == 6 && memcmp(key, "length", 6) == 0) { + *out = js_cfunc_length_value(cfunc); + return true; + } + + return false; } static const char *get_class_name(ant_t *js, ant_value_t obj, ant_offset_t *out_len, const char *skip) { @@ -2227,7 +2263,7 @@ static size_t strfunc(ant_t *js, ant_value_t value, char *buf, size_t len) { ant_value_t async_slot = get_slot(func_obj, SLOT_ASYNC); bool is_async = (async_slot == js_true); - bool has_code = (vtype(code_slot) == T_CFUNC); + bool has_code = (vtype(code_slot) == T_NTARG); const struct func_format *fmt = &formats[is_async]; @@ -2283,6 +2319,21 @@ static size_t strfunc(ant_t *js, ant_value_t value, char *buf, size_t len) { return cpy(buf, len, fmt->anon, fmt->anon_len); } +static size_t strcfunc(ant_t *js, ant_value_t value, char *buf, size_t len) { + ant_offset_t name_len = 0; + const char *name = get_func_name(js, value, &name_len); + const struct func_format *fmt = &formats[0]; + + if (name && name_len > 0) { + size_t n = cpy(buf, len, fmt->prefix, fmt->prefix_len); + n += cpy(buf + n, REMAIN(n, len), name, name_len); + n += cpy(buf + n, REMAIN(n, len), "]", 1); + return n; + } + + return cpy(buf, len, fmt->anon, fmt->anon_len); +} + static size_t tostr(ant_t *js, ant_value_t value, char *buf, size_t len) { switch (vtype(value)) { case T_UNDEF: return ANT_COPY(buf, len, "undefined"); @@ -2300,8 +2351,7 @@ static size_t tostr(ant_t *js, ant_value_t value, char *buf, size_t len) { case T_BIGINT: return strbigint(js, value, buf, len); case T_PROMISE: return strpromise(js, value, buf, len); case T_FUNC: return strfunc(js, value, buf, len); - - case T_CFUNC: return ANT_COPY(buf, len, "[native code]"); + case T_CFUNC: return strcfunc(js, value, buf, len); case T_FFI: return ANT_COPY(buf, len, "[native code (ffi)]"); case T_ERR: { @@ -2424,7 +2474,7 @@ ant_value_t js_tostring_val(ant_t *js, ant_value_t value) { static const void *jump_table[] = { [T_OBJ] = &&L_OBJ, [T_STR] = &&L_STR, [T_UNDEF] = &&L_UNDEF, [T_NULL] = &&L_NULL, [T_NUM] = &&L_NUM, - [T_BOOL] = &&L_BOOL, [T_FUNC] = &&L_OBJ, [T_CFUNC] = &&L_DEFAULT, + [T_BOOL] = &&L_BOOL, [T_FUNC] = &&L_OBJ, [T_CFUNC] = &&L_OBJ, [T_ERR] = &&L_DEFAULT, [T_ARR] = &&L_OBJ, [T_PROMISE] = &&L_DEFAULT, [T_TYPEDARRAY] = &&L_DEFAULT, [T_BIGINT] = &&L_BIGINT, [T_SYMBOL] = &&L_DEFAULT, @@ -3006,6 +3056,8 @@ ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uin const char *p = (const char *)(uintptr_t)(koff); const char *interned = intern_string(p, klen); if (!interned) return js_mkerr(js, "oom"); + v = js_expose_cfunc_for_key(js, v, interned, (size_t)klen); + if (is_err(v)) return v; int32_t found = ant_shape_lookup_interned(ptr->shape, interned); if (found >= 0) { @@ -3013,9 +3065,7 @@ ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uin if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); ant_shape_set_attrs_interned(ptr->shape, interned, attrs); } else { - if (!ant_shape_add_interned_tr(&ptr->shape, interned, attrs, &slot)) { - return js_mkerr(js, "oom"); - } + if (!ant_shape_add_interned_tr(&ptr->shape, interned, attrs, &slot)) return js_mkerr(js, "oom"); added = true; } } @@ -3027,30 +3077,37 @@ ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uin if (slot >= ptr->prop_count && !js_obj_ensure_prop_capacity(ptr, slot + 1)) { return js_mkerr(js, "oom"); } + ant_object_prop_set_unchecked(ptr, slot, v); gc_write_barrier(js, ptr, v); return v; } -ant_value_t mkprop_interned(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs) { +static ant_value_t mkprop_interned_impl( + ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs, bool expose_cfunc +) { obj = js_as_obj(obj); ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape || !interned_key) return js_mkerr(js, "invalid object"); + if (expose_cfunc) { + v = js_expose_cfunc_for_key(js, v, interned_key, strlen(interned_key)); + if (is_err(v)) return v; + } if (!attrs) attrs = ANT_PROP_ATTR_DEFAULT; uint32_t slot = 0; bool added = false; int32_t found = ant_shape_lookup_interned(ptr->shape, interned_key); + if (found >= 0) { slot = (uint32_t)found; if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); ant_shape_set_attrs_interned(ptr->shape, interned_key, attrs); } else { - if (!ant_shape_add_interned_tr(&ptr->shape, interned_key, attrs, &slot)) { - return js_mkerr(js, "oom"); - } + if (!ant_shape_add_interned_tr(&ptr->shape, interned_key, attrs, &slot)) return js_mkerr(js, "oom"); added = true; } @@ -3060,11 +3117,21 @@ ant_value_t mkprop_interned(ant_t *js, ant_value_t obj, const char *interned_key if (slot >= ptr->prop_count && !js_obj_ensure_prop_capacity(ptr, slot + 1)) { return js_mkerr(js, "oom"); } + ant_object_prop_set_unchecked(ptr, slot, v); gc_write_barrier(js, ptr, v); + return v; } +ant_value_t mkprop_interned(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs) { + return mkprop_interned_impl(js, obj, interned_key, v, attrs, true); +} + +ant_value_t mkprop_interned_exact(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs) { + return mkprop_interned_impl(js, obj, interned_key, v, attrs, false); +} + ant_value_t js_mkprop_fast(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) { const char *interned = intern_string(key, len); if (!interned) return js_mkerr(js, "oom"); @@ -3082,7 +3149,6 @@ ant_offset_t js_mkprop_fast_off(ant_t *js, ant_value_t obj, const char *key, siz void js_saveval(ant_t *js, ant_offset_t off, ant_value_t v) { bool ok = propref_store(js, off, v); assert(ok && "js_saveval expects a valid property handle"); - (void)ok; } static void set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t val) { @@ -3137,7 +3203,7 @@ static ant_value_t get_slot(ant_value_t obj, internal_slot_t slot) { } static void set_func_code_ptr(ant_t *js, ant_value_t func_obj, const char *code, size_t len) { - set_slot(func_obj, SLOT_CODE, mkval(T_CFUNC, (size_t)code)); + set_slot(func_obj, SLOT_CODE, mkval(T_NTARG, (size_t)code)); set_slot(func_obj, SLOT_CODE_LEN, tov((double)len)); } @@ -3151,13 +3217,13 @@ static const char *get_func_code(ant_t *js, ant_value_t func_obj, ant_offset_t * ant_value_t code_val = get_slot(func_obj, SLOT_CODE); ant_value_t len_val = get_slot(func_obj, SLOT_CODE_LEN); - if (vtype(code_val) != T_CFUNC) { + if (vtype(code_val) != T_NTARG) { if (len) *len = 0; return NULL; } if (len) *len = (ant_offset_t)tod(len_val); - return (const char *)vdata(code_val); + return (const char *)(uintptr_t)vdata(code_val); } static inline bool js_is_trim_space(char ch) { @@ -3742,6 +3808,29 @@ bool js_try_get_own_data_prop(ant_t *js, ant_value_t obj, const char *key, size_ return false; } +static ant_value_t call_proto_accessor( + ant_t *js, ant_value_t prim, ant_value_t accessor, bool has_accessor, + ant_value_t *arg, int arg_count, bool is_setter +) { + if (!has_accessor || (vtype(accessor) != T_FUNC && vtype(accessor) != T_CFUNC)) + return js_mkundef(); + + js_error_site_t saved_errsite = js->errsite; + ant_value_t result = sv_vm_call(sv_vm_get_active(js), js, accessor, prim, arg, arg_count, NULL, false); + + bool had_throw = js->thrown_exists; + ant_value_t thrown = js->thrown_value; + js->errsite = saved_errsite; + + if (had_throw) { + js->thrown_exists = true; + js->thrown_value = thrown; + } + + if (is_setter) return is_err(result) ? result : (arg ? *arg : js_mkundef()); + return result; +} + // TODO: decompose into smaller helpers ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) { uint8_t ot = vtype(obj); @@ -3830,6 +3919,9 @@ ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen); const char *key = (char *)(uintptr_t)(koff); + v = js_expose_cfunc_for_key(js, v, key, (size_t)klen); + if (is_err(v)) return v; + if (array_obj_ptr(obj) && klen > 0 && key[0] >= '0' && key[0] <= '9') { ant_arguments_state_t *args_state = js_arguments_state(obj); if (args_state && !args_state->in_setter && js_arguments_setter(js, obj, key, (size_t)klen, v)) return v; @@ -3983,6 +4075,8 @@ ant_value_t setprop_cstr(ant_t *js, ant_value_t obj, const char *key, size_t len ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t v) { obj = js_as_obj(obj); + v = js_expose_cfunc_for_key(js, v, key, klen); + if (is_err(v)) return v; if (is_proxy(obj)) { ant_value_t result = proxy_set(js, obj, key, klen, v); if (is_err(result)) return result; @@ -4367,29 +4461,6 @@ static uintptr_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, si return 0; } -static ant_value_t call_proto_accessor( - ant_t *js, ant_value_t prim, ant_value_t accessor, bool has_accessor, - ant_value_t *arg, int arg_count, bool is_setter -) { - if (!has_accessor || (vtype(accessor) != T_FUNC && vtype(accessor) != T_CFUNC)) - return js_mkundef(); - - js_error_site_t saved_errsite = js->errsite; - ant_value_t result = sv_vm_call(sv_vm_get_active(js), js, accessor, prim, arg, arg_count, NULL, false); - - bool had_throw = js->thrown_exists; - ant_value_t thrown = js->thrown_value; - js->errsite = saved_errsite; - - if (had_throw) { - js->thrown_exists = true; - js->thrown_value = thrown; - } - - if (is_setter) return is_err(result) ? result : (arg ? *arg : js_mkundef()); - return result; -} - ant_value_t js_get_proto(ant_t *js, ant_value_t obj) { uint8_t t = vtype(obj); @@ -4451,17 +4522,17 @@ static inline ant_value_t get_ctor_proto(ant_t *js, const char *name, size_t len static ant_value_t get_prototype_for_type(ant_t *js, uint8_t type) { switch (type) { - case T_OBJ: return js->sym.object_proto; - case T_ARR: return js->sym.array_proto; - case T_STR: return get_ctor_proto(js, "String", 6); - case T_NUM: return get_ctor_proto(js, "Number", 6); - case T_BOOL: return get_ctor_proto(js, "Boolean", 7); - case T_FUNC: return get_ctor_proto(js, "Function", 8); - case T_PROMISE: return get_ctor_proto(js, "Promise", 7); + case T_OBJ: return js->sym.object_proto; + case T_ARR: return js->sym.array_proto; + case T_STR: return get_ctor_proto(js, "String", 6); + case T_NUM: return get_ctor_proto(js, "Number", 6); + case T_BOOL: return get_ctor_proto(js, "Boolean", 7); + case T_FUNC: return get_ctor_proto(js, "Function", 8); + case T_PROMISE: return get_ctor_proto(js, "Promise", 7); case T_GENERATOR: return js->sym.generator_proto; - case T_BIGINT: return get_ctor_proto(js, "BigInt", 6); - case T_SYMBOL: return get_ctor_proto(js, "Symbol", 6); - default: return js_mknull(); + case T_BIGINT: return get_ctor_proto(js, "BigInt", 6); + case T_SYMBOL: return get_ctor_proto(js, "Symbol", 6); + default: return js_mknull(); }} ant_offset_t lkp_proto(ant_t *js, ant_value_t obj, const char *key, size_t len) { @@ -4764,7 +4835,7 @@ static bool js_try_call_method(ant_t *js, ant_value_t obj, const char *method, a js->this_val = obj; ant_value_t result; - if (ft == T_CFUNC) result = ((ant_value_t (*)(ant_t *, ant_value_t *, int))vdata(fn))(js, args, nargs); + if (ft == T_CFUNC) result = js_as_cfunc(fn)(js, args, nargs); else result = sv_vm_call(js->vm, js, fn, obj, args, nargs, NULL, false); bool had_throw = js->thrown_exists; @@ -5419,11 +5490,28 @@ static ant_value_t builtin_function_toString(ant_t *js, ant_value_t *args, int n return js_mkerr_typed(js, JS_ERR_TYPE, "Function.prototype.toString requires that 'this' be a Function"); } - if (t == T_CFUNC) return ANT_STRING("function() { [native code] }"); + // TODO: make dry + if (t == T_CFUNC) { + ant_offset_t name_len = 0; + const char *name = get_func_name(js, func, &name_len); + if (name && name_len > 0) { + size_t total = 9 + name_len + 21 + 1; + char *buf = ant_calloc(total); + size_t n = 0; + n += cpy(buf + n, total - n, "function ", 9); + n += cpy(buf + n, total - n, name, name_len); + n += cpy(buf + n, total - n, "() { [native code] }", 20); + ant_value_t result = js_mkstr(js, buf, n); + free(buf); + return result; + } + return ANT_STRING("function() { [native code] }"); + } ant_value_t func_obj = js_func_obj(func); ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC); + // TODO: make dry if (vtype(cfunc_slot) == T_CFUNC) { ant_offset_t name_len = 0; const char *name = get_func_name(js, func, &name_len); @@ -5444,7 +5532,7 @@ static ant_value_t builtin_function_toString(ant_t *js, ant_value_t *args, int n ant_value_t code_val = get_slot(func_obj, SLOT_CODE); ant_value_t len_val = get_slot(func_obj, SLOT_CODE_LEN); - if (vtype(code_val) == T_CFUNC && vtype(len_val) == T_NUM) { + if (vtype(code_val) == T_NTARG && vtype(len_val) == T_NUM) { const char *code = (const char *)(uintptr_t)vdata(code_val); size_t code_len = (size_t)tod(len_val); @@ -5578,7 +5666,7 @@ static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs if (is_err(bound_func)) return bound_func; ant_value_t code_val = get_slot(func_obj, SLOT_CODE); - if (vtype(code_val) == T_STR || vtype(code_val) == T_CFUNC) { + if (vtype(code_val) == T_STR || vtype(code_val) == T_NTARG) { set_slot(bound_func, SLOT_CODE, code_val); set_slot(bound_func, SLOT_CODE_LEN, get_slot(func_obj, SLOT_CODE_LEN)); } @@ -7032,11 +7120,9 @@ static ant_value_t strobj_call_custom_inspect(ant_t *js, ant_value_t obj) { if (vtype(inspect_fn) == T_CFUNC) { ant_value_t saved_this = js->this_val; js->this_val = obj; - result = ((ant_value_t (*)(ant_t *, ant_value_t *, int))vdata(inspect_fn))(js, &depth_arg, 1); + result = js_as_cfunc(inspect_fn)(js, &depth_arg, 1); js->this_val = saved_this; - } else { - result = sv_vm_call(js->vm, js, inspect_fn, obj, &depth_arg, 1, NULL, false); - } + } else result = sv_vm_call(js->vm, js, inspect_fn, obj, &depth_arg, 1, NULL, false); if (is_err(result) || js->thrown_exists || vtype(result) != T_STR) { js_restore_exception(js, &saved); @@ -7332,6 +7418,35 @@ static ant_value_t builtin_object_getOwnPropertyDescriptor(ant_t *js, ant_value_ ant_value_t key = args[1]; uint8_t t = vtype(obj); + if (t == T_CFUNC) { + bool is_sym = (vtype(key) == T_SYMBOL); + if (is_sym) return js_mkundef(); + + const char *key_str = NULL; + ant_offset_t key_len = 0; + if (vtype(key) == T_STR) { + ant_offset_t key_off = vstr(js, key, &key_len); + key_str = (const char *)(uintptr_t)key_off; + } else { + char buf[64]; + size_t n = tostr(js, key, buf, sizeof(buf)); + key = js_mkstr(js, buf, n); + ant_offset_t key_off = vstr(js, key, &key_len); + key_str = (const char *)(uintptr_t)key_off; + } + + ant_value_t value = js_mkundef(); + if (!js_cfunc_try_get_own(js, obj, key_str, (size_t)key_len, &value)) return js_mkundef(); + + ant_value_t result = js_mkobj(js); + js_setprop(js, result, js_mkstr(js, "value", 5), value); + js_setprop(js, result, js_mkstr(js, "writable", 8), js_false); + js_setprop(js, result, js_mkstr(js, "enumerable", 10), js_false); + js_setprop(js, result, js_mkstr(js, "configurable", 12), js_true); + + return result; + } + if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_mkundef(); const char *key_str; @@ -7429,7 +7544,10 @@ static ant_value_t builtin_object_getOwnPropertyDescriptor(ant_t *js, ant_value_ prop_val = tov((double)get_array_length(js, as_obj)); has_value_out = true; } - if (has_value_out) js_setprop(js, result, js_mkstr(js, "value", 5), prop_val); + if (has_value_out) { + if (vtype(prop_val) == T_CFUNC) prop_val = js_cfunc_promote(js, prop_val); + js_setprop(js, result, js_mkstr(js, "value", 5), prop_val); + } js_setprop(js, result, js_mkstr(js, "writable", 8), js_bool(writable)); js_setprop(js, result, js_mkstr(js, "enumerable", 10), js_bool(enumerable)); js_setprop(js, result, js_mkstr(js, "configurable", 12), js_bool(configurable)); @@ -7455,6 +7573,16 @@ static inline bool own_prop_names_is_dense_shadow( static ant_value_t builtin_object_getOwnPropertyNames(ant_t *js, ant_value_t *args, int nargs) { if (nargs == 0) return mkarr(js); ant_value_t obj = args[0]; + + if (vtype(obj) == T_CFUNC) { + ant_value_t arr = mkarr(js); + ant_offset_t idx = 0; + arr_set(js, arr, idx++, js->length_str); + if (vtype(js_cfunc_name_value(js, obj)) == T_STR) { + arr_set(js, arr, idx++, ANT_STRING("name")); + } + return mkval(T_ARR, vdata(arr)); + } if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js); if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); @@ -11236,7 +11364,7 @@ static ant_value_t make_data_cfunc( GC_ROOT_PIN(js, obj); set_slot(obj, SLOT_DATA, data); - set_slot(obj, SLOT_CFUNC, js_mkfun(fn)); + set_slot(obj, SLOT_CFUNC, js_mkfun_dyn(fn)); ant_value_t func = js_obj_to_func(obj); GC_ROOT_RESTORE(js, root_mark); @@ -11415,7 +11543,7 @@ void js_process_promise_handlers(ant_t *js, ant_value_t promise) { ant_value_t res = js_mkundef(); if (vtype(handler) == T_CFUNC) { - ant_value_t (*fn)(ant_t *, ant_value_t *, int) = (ant_value_t(*)(ant_t *, ant_value_t *, int))vdata(handler); + ant_value_t (*fn)(ant_t *, ant_value_t *, int) = js_as_cfunc(handler); res = fn(js, &val, 1); } else { ant_value_t call_args[] = { val }; @@ -12629,7 +12757,7 @@ static ant_value_t handle_proxy_instanceof(ant_t *js, ant_value_t l, ant_value_t } static ant_value_t handle_cfunc_instanceof(ant_value_t l, ant_value_t r, uint8_t ltype) { - ant_value_t (*fn)(ant_t *, ant_value_t *, int) = (ant_value_t(*)(ant_t *, ant_value_t *, int)) vdata(r); + ant_value_t (*fn)(ant_t *, ant_value_t *, int) = js_as_cfunc(r); if (fn == builtin_Object) return mkval(T_BOOL, ltype == T_OBJ ? 1 : 0); if (fn == builtin_Function) return mkval(T_BOOL, (ltype == T_FUNC || ltype == T_CFUNC) ? 1 : 0); @@ -14064,8 +14192,8 @@ ant_t *js_create(void *buf, size_t len) { defmethod(js, number_ctor_obj, "isFinite", 8, js_mkfun(builtin_Number_isFinite)); defmethod(js, number_ctor_obj, "isInteger", 9, js_mkfun(builtin_Number_isInteger)); defmethod(js, number_ctor_obj, "isSafeInteger", 13, js_mkfun(builtin_Number_isSafeInteger)); - defmethod(js, number_ctor_obj, "parseInt", 8, js_mkfun(builtin_parseInt)); - defmethod(js, number_ctor_obj, "parseFloat", 10, js_mkfun(builtin_parseFloat)); + ant_value_t number_parse_int = defmethod(js, number_ctor_obj, "parseInt", 8, js_mkfun(builtin_parseInt)); + ant_value_t number_parse_float = defmethod(js, number_ctor_obj, "parseFloat", 10, js_mkfun(builtin_parseFloat)); js_setprop(js, number_ctor_obj, js_mkstr(js, "MAX_VALUE", 9), tov(1.7976931348623157e+308)); js_setprop(js, number_ctor_obj, js_mkstr(js, "MIN_VALUE", 9), tov(5e-324)); @@ -14127,8 +14255,8 @@ ant_t *js_create(void *buf, size_t len) { js_setprop(js, p_ctor_obj, ANT_STRING("name"), ANT_STRING("Promise")); js_setprop(js, glob, js_mkstr(js, "Promise", 7), js_obj_to_func(p_ctor_obj)); - defmethod(js, glob, "parseInt", 8, js_mkfun(builtin_parseInt)); - defmethod(js, glob, "parseFloat", 10, js_mkfun(builtin_parseFloat)); + defalias(js, glob, "parseInt", 8, number_parse_int); + defalias(js, glob, "parseFloat", 10, number_parse_float); defmethod(js, glob, "eval", 4, js_mkfun(builtin_eval)); defmethod(js, glob, "isNaN", 5, js_mkfun(builtin_global_isNaN)); defmethod(js, glob, "isFinite", 8, js_mkfun(builtin_global_isFinite)); @@ -14235,6 +14363,19 @@ void js_destroy(ant_t *js) { js->cfunc_promote_cache.promoted = NULL; js->cfunc_promote_cache.len = js->cfunc_promote_cache.cap = 0; + for (uint16_t i = 0; i < js->cfunc_name_cache.len; i++) free( + (void *)(uintptr_t)vdata(js->cfunc_name_cache.named[i]) + ); + + free(js->cfunc_name_cache.base_meta); + free(js->cfunc_name_cache.name_ptr); + free(js->cfunc_name_cache.named); + + js->cfunc_name_cache.base_meta = NULL; + js->cfunc_name_cache.name_ptr = NULL; + js->cfunc_name_cache.named = NULL; + js->cfunc_name_cache.len = js->cfunc_name_cache.cap = 0; + js_pool_destroy(&js->pool.rope); js_pool_destroy(&js->pool.symbol); js_pool_destroy(&js->pool.permanent); @@ -14256,14 +14397,115 @@ inline ant_value_t js_mknull(void) { return mkval(T_NULL, 0); } inline ant_value_t js_mknum(double value) { return tov(value); } inline ant_value_t js_mkobj(ant_t *js) { return mkobj(js, 0); } inline ant_value_t js_glob(ant_t *js) { return js->global; } -inline ant_value_t js_mkfun(ant_value_t (*fn)(ant_t *, ant_value_t *, int)) { return mkval(T_CFUNC, (size_t) (void *) fn); } + +ant_value_t js_mkfun_meta(const ant_cfunc_meta_t *meta) { + return mkval(T_CFUNC, (uintptr_t)meta); +} + +ant_value_t js_mkfun_dyn(ant_cfunc_t fn) { + typedef struct dyn_cfunc_meta_entry { + ant_cfunc_t fn; + ant_cfunc_meta_t meta; + struct dyn_cfunc_meta_entry *next; + } dyn_cfunc_meta_entry_t; + + static dyn_cfunc_meta_entry_t *head = NULL; + for (dyn_cfunc_meta_entry_t *it = head; it; it = it->next) { + if (it->fn == fn) return js_mkfun_meta(&it->meta); + } + + dyn_cfunc_meta_entry_t *entry = ant_calloc(sizeof(dyn_cfunc_meta_entry_t)); + if (!entry) return mkval(T_ERR, 0); + + entry->fn = fn; + entry->meta.fn = fn; + entry->next = head; + head = entry; + + return js_mkfun_meta(&entry->meta); +} inline ant_value_t js_getthis(ant_t *js) { return js->this_val; } inline void js_setthis(ant_t *js, ant_value_t val) { js->this_val = val; } inline ant_value_t js_getcurrentfunc(ant_t *js) { return js->current_func; } +static ant_value_t js_cfunc_name_value(ant_t *js, ant_value_t cfunc) { + const ant_cfunc_meta_t *meta = js_as_cfunc_meta(cfunc); + if (!meta || !meta->name) return js_mkundef(); + return js_mkstr(js, meta->name, strlen(meta->name)); +} + +static ant_value_t js_cfunc_length_value(ant_value_t cfunc) { + return tov((double)js_cfunc_length(cfunc)); +} + +ant_value_t js_cfunc_expose_named(ant_t *js, ant_value_t cfunc, const char *name, size_t name_len) { + if (vtype(cfunc) != T_CFUNC || !name || is_internal_prop(name, (ant_offset_t)name_len)) return cfunc; + + const ant_cfunc_meta_t *base = js_as_cfunc_meta(cfunc); + if (!base) return cfunc; + + if ( + base->name && + strlen(base->name) == name_len && + memcmp(base->name, name, name_len) == 0 + ) return cfunc; + + const char *interned = intern_string(name, name_len); + if (!interned) return js_mkerr(js, "oom"); + + for (uint16_t i = 0; i < js->cfunc_name_cache.len; i++) if ( + js->cfunc_name_cache.base_meta[i] == base && + js->cfunc_name_cache.name_ptr[i] == interned + ) return js->cfunc_name_cache.named[i]; + + ant_cfunc_meta_t *named_meta = ant_calloc(sizeof(ant_cfunc_meta_t)); + if (!named_meta) return js_mkerr(js, "oom"); + + named_meta->fn = base->fn; + named_meta->name = interned; + named_meta->length = base->length; + named_meta->flags = base->flags; + + if (js->cfunc_name_cache.len >= js->cfunc_name_cache.cap) { + uint16_t new_cap = js->cfunc_name_cache.cap ? js->cfunc_name_cache.cap * 2 : 8; + + const ant_cfunc_meta_t **new_base = realloc( + js->cfunc_name_cache.base_meta, + new_cap * sizeof(ant_cfunc_meta_t *) + ); + + const char **new_names = realloc(js->cfunc_name_cache.name_ptr, new_cap * sizeof(const char *)); + ant_value_t *new_named = realloc(js->cfunc_name_cache.named, new_cap * sizeof(ant_value_t)); + + if (!new_base || !new_names || !new_named) { + free(named_meta); + return js_mkerr(js, "oom"); + } + + js->cfunc_name_cache.base_meta = new_base; + js->cfunc_name_cache.name_ptr = new_names; + js->cfunc_name_cache.named = new_named; + js->cfunc_name_cache.cap = new_cap; + } + + ant_value_t named = js_mkfun_meta(named_meta); + uint16_t idx = js->cfunc_name_cache.len++; + js->cfunc_name_cache.base_meta[idx] = base; + js->cfunc_name_cache.name_ptr[idx] = interned; + js->cfunc_name_cache.named[idx] = named; + + return named; +} + +static ant_value_t js_expose_cfunc_for_key(ant_t *js, ant_value_t value, const char *key, size_t key_len) { + if (vtype(value) != T_CFUNC) return value; + return js_cfunc_expose_named(js, value, key, key_len); +} + ant_value_t js_cfunc_promote(ant_t *js, ant_value_t cfunc) { uintptr_t ptr = vdata(cfunc); + const ant_cfunc_meta_t *meta = js_as_cfunc_meta(cfunc); for (uint8_t i = 0; i < js->cfunc_promote_cache.len; i++) { if (js->cfunc_promote_cache.cfunc_ptr[i] == ptr) @@ -14279,6 +14521,16 @@ ant_value_t js_cfunc_promote(ant_t *js, ant_value_t cfunc) { if (obj_ptr) obj_ptr->proto = proto; } + ant_value_t length_result = js_setprop(js, fn_obj, ANT_STRING("length"), js_cfunc_length_value(cfunc)); + if (is_err(length_result)) return length_result; + js_set_descriptor(js, fn_obj, "length", 6, JS_DESC_C); + + if (meta && meta->name) { + ant_value_t name_result = js_setprop(js, fn_obj, ANT_STRING("name"), js_cfunc_name_value(js, cfunc)); + if (is_err(name_result)) return name_result; + js_set_descriptor(js, fn_obj, "name", 4, JS_DESC_C); + } + ant_value_t promoted = js_obj_to_func(fn_obj); if (js->cfunc_promote_cache.len >= js->cfunc_promote_cache.cap) { @@ -14303,7 +14555,7 @@ ant_value_t js_cfunc_promote(ant_t *js, ant_value_t cfunc) { } ant_value_t js_heavy_mkfun(ant_t *js, ant_value_t (*fn)(ant_t *, ant_value_t *, int), ant_value_t data) { - ant_value_t cfunc = js_mkfun(fn); + ant_value_t cfunc = js_mkfun_dyn(fn); ant_value_t fn_obj = mkobj(js, 0); set_slot(fn_obj, SLOT_CFUNC, cfunc); @@ -14314,6 +14566,8 @@ ant_value_t js_heavy_mkfun(ant_t *js, ant_value_t (*fn)(ant_t *, ant_value_t *, void js_set(ant_t *js, ant_value_t obj, const char *key, ant_value_t val) { size_t key_len = strlen(key); + val = js_expose_cfunc_for_key(js, val, key, key_len); + if (is_err(val)) return; if (vtype(obj) == T_OBJ) { ant_offset_t existing = lkp(js, obj, key, key_len); @@ -14343,6 +14597,37 @@ void js_set(ant_t *js, ant_value_t obj, const char *key, ant_value_t val) { } } +void js_set_exact(ant_t *js, ant_value_t obj, const char *key, ant_value_t val) { + size_t key_len = strlen(key); + const char *interned = intern_string(key, key_len); + + if (!interned) { + js_mkerr(js, "oom"); + return; + } + + if (vtype(obj) == T_OBJ) { + ant_offset_t existing = lkp(js, obj, key, key_len); + if (existing > 0) { + if (is_const_prop(js, existing)) { + js_mkerr(js, "assignment to constant"); + return; + } + js_saveval(js, existing, val); + } else mkprop_interned_exact(js, obj, interned, val, 0); + } else if (vtype(obj) == T_FUNC) { + ant_value_t func_obj = js_func_obj(obj); + ant_offset_t existing = lkp(js, func_obj, key, key_len); + if (existing > 0) { + if (is_const_prop(js, existing)) { + js_mkerr(js, "assignment to constant"); + return; + } + js_saveval(js, existing, val); + } else mkprop_interned_exact(js, func_obj, interned, val, 0); + } +} + void js_set_sym(ant_t *js, ant_value_t obj, ant_value_t sym, ant_value_t val) { if (vtype(sym) != T_SYMBOL) return; ant_offset_t sym_off = (ant_offset_t)vdata(sym); @@ -14423,6 +14708,16 @@ ant_value_t js_get_symbol(ant_t *js, ant_value_t obj, const char *key) { static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t *out) { size_t key_len = strlen(key); + + if (vtype(obj) == T_CFUNC) { + if (js_cfunc_try_get_own(js, obj, key, key_len, out)) return true; + ant_offset_t off = lkp_proto(js, obj, key, key_len); + if (off != 0) { + *out = propref_load(js, off); + return true; + } + return false; + } if (vtype(obj) == T_FUNC) { if (sv_vm_is_strict(js->vm) && @@ -14441,7 +14736,7 @@ static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t if (vtype(import_meta) == T_UNDEF) import_meta = js_get_current_import_meta(js); if (key_len == 4 && memcmp(key, "meta", 4) == 0 && vtype(import_meta) != T_UNDEF) { ant_value_t cfunc = js_get_slot(func_obj, SLOT_CFUNC); - if (vtype(cfunc) == T_CFUNC && js_as_cfunc(cfunc) == js_builtin_import) { + if (vtype(cfunc) == T_CFUNC && js_cfunc_same_entrypoint(cfunc, js_builtin_import)) { *out = import_meta; return true; } @@ -14596,11 +14891,6 @@ ant_value_t js_getprop_proto(ant_t *js, ant_value_t obj, const char *key) { } ant_value_t js_getprop_fallback(ant_t *js, ant_value_t obj, const char *name) { - if (vtype(obj) == T_CFUNC && js->cfunc_promote_cache.len > 0) { - ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); - if (vtype(promoted) != T_CFUNC) return js_getprop_fallback(js, promoted, name); - } - ant_value_t val; if (js_try_get(js, obj, name, &val)) return val; diff --git a/src/modules/buffer.c b/src/modules/buffer.c index 8d33617..a8e4ba4 100644 --- a/src/modules/buffer.c +++ b/src/modules/buffer.c @@ -2601,11 +2601,10 @@ void init_buffer_module() { js_set(js, g_typedarray_iter_proto, "next", js_mkfun(ta_iter_next)); js_iter_register_advance(g_typedarray_iter_proto, advance_typedarray); - ant_value_t ta_values_fn = js_mkfun(ta_values); - js_set(js, typedarray_proto, "values", ta_values_fn); + js_set(js, typedarray_proto, "values", js_mkfun(ta_values)); js_set(js, typedarray_proto, "keys", js_mkfun(ta_keys)); js_set(js, typedarray_proto, "entries", js_mkfun(ta_entries)); - js_set_sym(js, typedarray_proto, get_iterator_sym(), ta_values_fn); + js_set_sym(js, typedarray_proto, get_iterator_sym(), js_get(js, typedarray_proto, "values")); #define SETUP_TYPEDARRAY(name) \ do { \ @@ -2709,8 +2708,8 @@ void init_buffer_module() { js_set(js, buffer_proto, "readUInt32BE", js_mkfun(js_buffer_readUInt32BE)); js_set_sym(js, buffer_proto, get_toStringTag_sym(), js_mkstr(js, "Buffer", 6)); - js_set_sym(js, buffer_proto, get_iterator_sym(), ta_values_fn); - js_set(js, buffer_proto, "values", ta_values_fn); + js_set(js, buffer_proto, "values", js_get(js, typedarray_proto, "values")); + js_set_sym(js, buffer_proto, get_iterator_sym(), js_get(js, buffer_proto, "values")); js_set(js, buffer_ctor_obj, "from", js_mkfun(js_buffer_from)); js_set(js, buffer_ctor_obj, "alloc", js_mkfun(js_buffer_alloc)); diff --git a/src/modules/collections.c b/src/modules/collections.c index 745039a..532e630 100644 --- a/src/modules/collections.c +++ b/src/modules/collections.c @@ -513,11 +513,6 @@ static ant_value_t map_entries(ant_t *js, ant_value_t *args, int nargs) { return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_ENTRIES); } -static ant_value_t map_iterator(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; - return create_map_iterator(js, js->this_val, ITER_TYPE_MAP_ENTRIES); -} - bool advance_set(ant_t *js, js_iter_t *it, ant_value_t *out) { set_iterator_state_t *state = get_set_iter_state(js, it->iterator); if (!state || !state->current) return false; @@ -638,11 +633,6 @@ static ant_value_t set_entries(ant_t *js, ant_value_t *args, int nargs) { return create_set_iterator(js, js->this_val, ITER_TYPE_SET_ENTRIES); } -static ant_value_t set_iterator(ant_t *js, ant_value_t *args, int nargs) { - (void)args; (void)nargs; - return create_set_iterator(js, js->this_val, ITER_TYPE_SET_VALUES); -} - static ant_value_t set_forEach(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js->this_val; set_entry_t **set_ptr = get_set_from_obj(js, this_val); @@ -1106,7 +1096,7 @@ void init_collections_module(void) { js_set(js, map_proto, "keys", js_mkfun(map_keys)); js_set(js, map_proto, "values", js_mkfun(map_values)); js_set(js, map_proto, "forEach", js_mkfun(map_forEach)); - js_set_sym(js, map_proto, iter_sym, js_mkfun(map_iterator)); + js_set_sym(js, map_proto, iter_sym, js_get(js, map_proto, "entries")); js_set_sym(js, map_proto, tag_sym, js_mkstr(js, "Map", 3)); ant_value_t map_ctor = js_mkobj(js); @@ -1126,10 +1116,10 @@ void init_collections_module(void) { js_set(js, set_proto, "clear", js_mkfun(set_clear)); js_set_getter_desc(js, set_proto, "size", 4, js_mkfun(set_size), JS_DESC_C); js_set(js, set_proto, "values", js_mkfun(set_values)); - js_set(js, set_proto, "keys", js_mkfun(set_values)); + js_set_exact(js, set_proto, "keys", js_get(js, set_proto, "values")); js_set(js, set_proto, "entries", js_mkfun(set_entries)); js_set(js, set_proto, "forEach", js_mkfun(set_forEach)); - js_set_sym(js, set_proto, iter_sym, js_mkfun(set_iterator)); + js_set_sym(js, set_proto, iter_sym, js_get(js, set_proto, "values")); js_set_sym(js, set_proto, tag_sym, js_mkstr(js, "Set", 3)); ant_value_t set_ctor = js_mkobj(js); diff --git a/src/modules/date.c b/src/modules/date.c index 8de8a4a..f1c699f 100644 --- a/src/modules/date.c +++ b/src/modules/date.c @@ -1280,7 +1280,7 @@ static void date_define_methods( ) { for (size_t i = 0; i < count; i++) js_setprop(js,target, js_mkstr(js, methods[i].name, methods[i].len), - js_mkfun(methods[i].fn) + js_mkfun_dyn(methods[i].fn) ); } diff --git a/src/modules/events.c b/src/modules/events.c index ffa8a2c..a32466b 100644 --- a/src/modules/events.c +++ b/src/modules/events.c @@ -1253,12 +1253,12 @@ ant_value_t eventemitter_prototype(ant_t *js) { if (is_object_type(function_proto)) js_set_proto_init(eventemitter_ctor, function_proto); js_set(js, eventemitter_proto, "on", js_mkfun(js_eventemitter_on)); - js_set(js, eventemitter_proto, "addListener", js_mkfun(js_eventemitter_on)); + js_set_exact(js, eventemitter_proto, "addListener", js_get(js, eventemitter_proto, "on")); js_set(js, eventemitter_proto, "once", js_mkfun(js_eventemitter_once)); js_set(js, eventemitter_proto, "prependListener", js_mkfun(js_eventemitter_prepend_listener)); js_set(js, eventemitter_proto, "prependOnceListener", js_mkfun(js_eventemitter_prepend_once_listener)); js_set(js, eventemitter_proto, "off", js_mkfun(js_eventemitter_off)); - js_set(js, eventemitter_proto, "removeListener", js_mkfun(js_eventemitter_off)); + js_set_exact(js, eventemitter_proto, "removeListener", js_get(js, eventemitter_proto, "off")); js_set(js, eventemitter_proto, "emit", js_mkfun(js_eventemitter_emit)); js_set(js, eventemitter_proto, "removeAllListeners", js_mkfun(js_eventemitter_removeAllListeners)); js_set(js, eventemitter_proto, "listenerCount", js_mkfun(js_eventemitter_listenerCount)); diff --git a/src/modules/formdata.c b/src/modules/formdata.c index 38b9610..f26f6b4 100644 --- a/src/modules/formdata.c +++ b/src/modules/formdata.c @@ -487,7 +487,7 @@ void init_formdata_module(void) { js_set(js, g_formdata_proto, "keys", js_mkfun(js_formdata_keys)); js_set(js, g_formdata_proto, "values", js_mkfun(js_formdata_values)); - js_set_sym(js, g_formdata_proto, get_iterator_sym(), js_mkfun(js_formdata_entries)); + js_set_sym(js, g_formdata_proto, get_iterator_sym(), js_get(js, g_formdata_proto, "entries")); js_set_sym(js, g_formdata_proto, get_toStringTag_sym(), js_mkstr(js, "FormData", 8)); ant_value_t ctor_obj = js_mkobj(js); diff --git a/src/modules/fs.c b/src/modules/fs.c index 14339b1..266a77d 100644 --- a/src/modules/fs.c +++ b/src/modules/fs.c @@ -191,8 +191,7 @@ static ant_value_t fs_call_value( ant_value_t result = js_mkundef(); js->this_val = this_val; - if (vtype(fn) == T_CFUNC) - result = ((ant_value_t (*)(ant_t *, ant_value_t *, int))vdata(fn))(js, args, nargs); + if (vtype(fn) == T_CFUNC) result = js_as_cfunc(fn)(js, args, nargs); else result = sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, false); js->this_val = saved_this; diff --git a/src/modules/headers.c b/src/modules/headers.c index c6a6087..e5d2776 100644 --- a/src/modules/headers.c +++ b/src/modules/headers.c @@ -725,10 +725,6 @@ static ant_value_t js_headers_entries(ant_t *js, ant_value_t *args, int nargs) { return make_headers_iter(js, js->this_val, ITER_ENTRIES); } -static ant_value_t js_headers_symbol_iterator(ant_t *js, ant_value_t *args, int nargs) { - return make_headers_iter(js, js->this_val, ITER_ENTRIES); -} - static ant_value_t headers_inspect_finish(ant_t *js, ant_value_t this_obj, ant_value_t body_obj) { ant_value_t tag_val = js_get_sym(js, this_obj, get_toStringTag_sym()); const char *tag = vtype(tag_val) == T_STR ? js_getstr(js, tag_val, NULL) : "Headers"; @@ -1095,7 +1091,7 @@ void init_headers_module(void) { js_set(js, g_headers_proto, "entries", js_mkfun(js_headers_entries)); js_set(js, g_headers_proto, "getSetCookie", js_mkfun(js_headers_get_set_cookie)); - js_set_sym(js, g_headers_proto, get_iterator_sym(), js_mkfun(js_headers_symbol_iterator)); + js_set_sym(js, g_headers_proto, get_iterator_sym(), js_get(js, g_headers_proto, "entries")); js_set_sym(js, g_headers_proto, get_inspect_sym(), js_mkfun(headers_inspect)); js_set_sym(js, g_headers_proto, get_toStringTag_sym(), js_mkstr(js, "Headers", 7)); diff --git a/src/modules/io.c b/src/modules/io.c index a66bd3f..28c61bb 100644 --- a/src/modules/io.c +++ b/src/modules/io.c @@ -397,7 +397,7 @@ static ant_value_t console_call_value( ant_value_t result = js_mkundef(); js->this_val = this_val; - if (vtype(fn) == T_CFUNC) result = ((ant_cfunc_t)vdata(fn))(js, args, nargs); + if (vtype(fn) == T_CFUNC) result = js_as_cfunc(fn)(js, args, nargs); else result = sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, false); js->this_val = saved_this; diff --git a/src/modules/net.c b/src/modules/net.c index 81603d4..624396e 100644 --- a/src/modules/net.c +++ b/src/modules/net.c @@ -151,8 +151,7 @@ static ant_value_t net_call_value( ant_value_t result = js_mkundef(); js->this_val = this_val; - if (vtype(fn) == T_CFUNC) - result = ((ant_value_t (*)(ant_t *, ant_value_t *, int))vdata(fn))(js, args, nargs); + if (vtype(fn) == T_CFUNC) result = js_as_cfunc(fn)(js, args, nargs); else result = sv_vm_call(js->vm, js, fn, this_val, args, nargs, NULL, false); js->this_val = saved_this; return result; diff --git a/src/modules/process.c b/src/modules/process.c index 8fe0b15..b646833 100644 --- a/src/modules/process.c +++ b/src/modules/process.c @@ -1845,12 +1845,12 @@ static void process_set_methods(ant_t *js, ant_value_t obj, bool include_event_m if (include_event_methods) { js_set(js, obj, "on", js_mkfun(process_on)); - js_set(js, obj, "addListener", js_mkfun(process_on)); + js_set_exact(js, obj, "addListener", js_get(js, obj, "on")); js_set(js, obj, "once", js_mkfun(process_once)); js_set(js, obj, "prependListener", js_mkfun(process_prepend_listener)); js_set(js, obj, "prependOnceListener", js_mkfun(process_prepend_once_listener)); js_set(js, obj, "off", js_mkfun(process_off)); - js_set(js, obj, "removeListener", js_mkfun(process_off)); + js_set_exact(js, obj, "removeListener", js_get(js, obj, "off")); js_set(js, obj, "removeAllListeners", js_mkfun(process_remove_all_listeners)); js_set(js, obj, "emit", js_mkfun(process_emit)); js_set(js, obj, "listenerCount", js_mkfun(process_listener_count)); @@ -1967,8 +1967,8 @@ void init_process_module() { js_set(js, stdin_proto, "resume", js_mkfun(js_stdin_resume)); js_set(js, stdin_proto, "pause", js_mkfun(js_stdin_pause)); js_set(js, stdin_proto, "on", js_mkfun(js_stdin_on)); - js_set(js, stdin_proto, "removeListener", js_mkfun(js_stdin_remove_listener)); js_set(js, stdin_proto, "off", js_mkfun(js_stdin_remove_listener)); + js_set_exact(js, stdin_proto, "removeListener", js_get(js, stdin_proto, "off")); js_set(js, stdin_proto, "removeAllListeners", js_mkfun(js_stdin_remove_all_listeners)); js_set_sym(js, stdin_proto, get_toStringTag_sym(), js_mkstr(js, "ReadStream", 10)); @@ -1982,8 +1982,8 @@ void init_process_module() { js_set(js, stdout_proto, "write", js_mkfun(js_stdout_write)); js_set(js, stdout_proto, "on", js_mkfun(js_stdout_on)); js_set(js, stdout_proto, "once", js_mkfun(js_stdout_once)); - js_set(js, stdout_proto, "removeListener", js_mkfun(js_stdout_remove_listener)); js_set(js, stdout_proto, "off", js_mkfun(js_stdout_remove_listener)); + js_set_exact(js, stdout_proto, "removeListener", js_get(js, stdout_proto, "off")); js_set(js, stdout_proto, "removeAllListeners", js_mkfun(js_stdout_remove_all_listeners)); js_set(js, stdout_proto, "getWindowSize", js_mkfun(js_stdout_get_window_size)); js_set_sym(js, stdout_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11)); @@ -1999,8 +1999,8 @@ void init_process_module() { js_set(js, stderr_proto, "write", js_mkfun(js_stderr_write)); js_set(js, stderr_proto, "on", js_mkfun(js_stderr_on)); js_set(js, stderr_proto, "once", js_mkfun(js_stderr_once)); - js_set(js, stderr_proto, "removeListener", js_mkfun(js_stderr_remove_listener)); js_set(js, stderr_proto, "off", js_mkfun(js_stderr_remove_listener)); + js_set_exact(js, stderr_proto, "removeListener", js_get(js, stderr_proto, "off")); js_set(js, stderr_proto, "removeAllListeners", js_mkfun(js_stderr_remove_all_listeners)); js_set_sym(js, stderr_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11)); diff --git a/src/modules/regex.c b/src/modules/regex.c index c42d1bd..f0b8f06 100644 --- a/src/modules/regex.c +++ b/src/modules/regex.c @@ -1054,7 +1054,7 @@ bool regexp_exec_truthy_try_fast( ant_value_t *out_result ) { if (!out_result || vtype(call_func) != T_CFUNC) return false; - if (js_as_cfunc(call_func) != builtin_regexp_exec) return false; + if (!js_cfunc_same_entrypoint(call_func, builtin_regexp_exec)) return false; if (!is_object_type(regexp) || vtype(arg) != T_STR) return false; ant_value_t result = regexp_exec_internal(js, regexp, arg, true); @@ -1077,7 +1077,7 @@ static ant_value_t builtin_regexp_test(ant_t *js, ant_value_t *args, int nargs) if (is_err(exec_fn)) return exec_fn; ant_value_t result; - if (vtype(exec_fn) == T_CFUNC && js_as_cfunc(exec_fn) == builtin_regexp_exec) { + if (vtype(exec_fn) == T_CFUNC && js_cfunc_same_entrypoint(exec_fn, builtin_regexp_exec)) { result = regexp_exec_internal(js, regexp, str_arg, true); } else result = regexp_exec_with_exec_fn(js, regexp, str_arg, exec_fn); diff --git a/src/modules/server.c b/src/modules/server.c index 2ab30e8..815be4e 100644 --- a/src/modules/server.c +++ b/src/modules/server.c @@ -285,10 +285,10 @@ static ant_value_t server_call_fetch(server_runtime_t *server, ant_value_t reque ant_value_t result = js_mkundef(); js->this_val = server->export_obj; - if (vtype(server->fetch_fn) == T_CFUNC) - result = ((ant_value_t (*)(ant_t *, ant_value_t *, int))vdata(server->fetch_fn))(js, args, 2); + if (vtype(server->fetch_fn) == T_CFUNC) result = js_as_cfunc(server->fetch_fn)(js, args, 2); else result = sv_vm_call(js->vm, js, server->fetch_fn, server->export_obj, args, 2, NULL, false); js->this_val = saved_this; + return result; } diff --git a/src/modules/url.c b/src/modules/url.c index dc6a2eb..90d4aa8 100644 --- a/src/modules/url.c +++ b/src/modules/url.c @@ -1437,11 +1437,11 @@ void init_url_module(void) { js_set(js, g_usp_proto, "forEach", js_mkfun(usp_forEach)); js_set_getter_desc(js, g_usp_proto, "size", 4, js_mkfun(usp_size_get), JS_DESC_C); - ant_value_t entries_fn = js_mkfun(usp_entries_fn); - js_set(js, g_usp_proto, "entries", entries_fn); + js_set(js, g_usp_proto, "entries", js_mkfun(usp_entries_fn)); js_set(js, g_usp_proto, "keys", js_mkfun(usp_keys_fn)); js_set(js, g_usp_proto, "values", js_mkfun(usp_values_fn)); - js_set_sym(js, g_usp_proto, get_iterator_sym(), entries_fn); + + js_set_sym(js, g_usp_proto, get_iterator_sym(), js_get(js, g_usp_proto, "entries")); js_set_sym(js, g_usp_proto, get_toStringTag_sym(), js_mkstr(js, "URLSearchParams", 15)); ant_value_t usp_ctor = js_make_ctor(js, js_URLSearchParams, g_usp_proto, "URLSearchParams", 15); diff --git a/src/silver/compiler.c b/src/silver/compiler.c index 420c865..aa8507b 100644 --- a/src/silver/compiler.c +++ b/src/silver/compiler.c @@ -154,7 +154,7 @@ static void build_gc_const_tables(sv_func_t *func) { int child_count = 0; for (int i = 0; i < func->const_count; i++) { - if (vtype(func->constants[i]) == T_CFUNC) child_count++; + if (vtype(func->constants[i]) == T_NTARG) child_count++; } if (child_count > 0) { @@ -162,7 +162,7 @@ static void build_gc_const_tables(sv_func_t *func) { if (func->child_funcs) { int out = 0; for (int i = 0; i < func->const_count; i++) { - if (vtype(func->constants[i]) != T_CFUNC) continue; + if (vtype(func->constants[i]) != T_NTARG) continue; func->child_funcs[out++] = (sv_func_t *)(uintptr_t)vdata(func->constants[i]); } func->child_func_count = child_count; } @@ -1214,7 +1214,7 @@ static void hoist_lexical_decls(sv_compiler_t *c, sv_ast_list_t *stmts) { static void hoist_one_func(sv_compiler_t *c, sv_ast_t *node) { sv_func_t *fn = compile_function_body(c, node, c->mode); if (!fn) return; - int idx = add_constant(c, mkval(T_CFUNC, (uintptr_t)fn)); + int idx = add_constant(c, mkval(T_NTARG, (uintptr_t)fn)); emit_op(c, OP_CLOSURE); emit_u32(c, (uint32_t)idx); emit_set_function_name(c, node->str, node->len); @@ -2543,7 +2543,8 @@ void compile_func_expr(sv_compiler_t *c, sv_ast_t *node) { if (has_name) end_scope(c); return; } - int idx = add_constant(c, mkval(T_CFUNC, (uintptr_t)fn)); + + int idx = add_constant(c, mkval(T_NTARG, (uintptr_t)fn)); emit_op(c, OP_CLOSURE); emit_u32(c, (uint32_t)idx); @@ -4155,8 +4156,7 @@ void compile_class(sv_compiler_t *c, sv_ast_t *node) { fn->name = name; } sv_compile_ctx_cleanup(&comp); - - int idx = add_constant(c, mkval(T_CFUNC, (uintptr_t)fn)); + int idx = add_constant(c, mkval(T_NTARG, (uintptr_t)fn)); emit_op(c, OP_CLOSURE); emit_u32(c, (uint32_t)idx); } else emit_op(c, OP_UNDEF); @@ -4802,7 +4802,7 @@ void sv_disasm(ant_t *js, sv_func_t *func, const char *label) { fprintf(stderr, "\n"); for (int i = 0; i < func->const_count; i++) { - if (vtype(func->constants[i]) == T_CFUNC) { + if (vtype(func->constants[i]) == T_NTARG) { sv_func_t *child = (sv_func_t *)(uintptr_t)vdata(func->constants[i]); char child_label[256]; snprintf(child_label, sizeof(child_label), "%s/closure[%d]", label, i); diff --git a/src/silver/engine.c b/src/silver/engine.c index 22faf0b..275cfeb 100644 --- a/src/silver/engine.c +++ b/src/silver/engine.c @@ -1416,10 +1416,8 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant if ( vtype(call_func) == T_CFUNC && - js_as_cfunc(call_func) == builtin_object_isPrototypeOf - ) { - call_result = sv_isproto_ic_eval(js, call_this, call_arg, func, ip); - } else { + js_cfunc_same_entrypoint(call_func, builtin_object_isPrototypeOf) + ) call_result = sv_isproto_ic_eval(js, call_this, call_arg, func, ip); else { ant_value_t call_args[1] = { call_arg }; frame->ip = ip; call_result = sv_vm_call(vm, js, call_func, call_this, call_args, 1, NULL, false); diff --git a/src/silver/glue.c b/src/silver/glue.c index fb79248..afdaca1 100644 --- a/src/silver/glue.c +++ b/src/silver/glue.c @@ -527,10 +527,7 @@ ant_value_t jit_helper_call_is_proto( ) { uint8_t *ip = NULL; if (func && bc_off >= 0 && bc_off < func->code_len) ip = func->code + bc_off; - if ( - vtype(call_func) == T_CFUNC && - js_as_cfunc(call_func) == builtin_object_isPrototypeOf - ) { + if (vtype(call_func) == T_CFUNC && js_cfunc_same_entrypoint(call_func, builtin_object_isPrototypeOf)) { return sv_isproto_ic_eval(js, call_this, arg, func, ip); } ant_value_t args[1] = { arg }; diff --git a/src/silver/ops/property.h b/src/silver/ops/property.h index fa66400..28dd119 100644 --- a/src/silver/ops/property.h +++ b/src/silver/ops/property.h @@ -103,13 +103,6 @@ static inline bool sv_try_get_shape_data_prop( } *out = (idx < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, idx) : js_mkundef(); - if (vtype(*out) == T_CFUNC && js->cfunc_promote_cache.len > 0) { - ant_value_t promoted = js_cfunc_lookup_promoted(js, *out); - if (vtype(promoted) != T_CFUNC) { - ant_object_prop_set_unchecked(ptr, idx, promoted); - gc_write_barrier(js, ptr, promoted); - *out = promoted; - }} return true; } @@ -281,11 +274,6 @@ static inline ant_value_t sv_prop_get_at( ) { uint8_t t = vtype(obj); - if (t == T_CFUNC && js->cfunc_promote_cache.len > 0) { - ant_value_t promoted = js_cfunc_lookup_promoted(js, obj); - if (vtype(promoted) != T_CFUNC) return sv_prop_get_at(js, promoted, str, len, func, ip); - } - if (t == T_NULL || t == T_UNDEF) { if (func && ip) js_set_error_site_from_bc(js, func, (int)(ip - func->code), func->filename); return js_mkerr_typed(js, JS_ERR_TYPE, diff --git a/src/silver/swarm.c b/src/silver/swarm.c index 915a6db..a52cfef 100644 --- a/src/silver/swarm.c +++ b/src/silver/swarm.c @@ -922,7 +922,7 @@ static sv_func_t *scan_closure_child(sv_func_t *func, uint8_t *ip) { if (idx >= (uint32_t)func->const_count) return NULL; ant_value_t cv = func->constants[idx]; - if (vtype(cv) != T_CFUNC) return NULL; + if (vtype(cv) != T_NTARG) return NULL; return (sv_func_t *)(uintptr_t)vdata(cv); } @@ -2020,7 +2020,7 @@ static bool jit_is_eligible(sv_func_t *func) { uint32_t idx = sv_get_u32(ip + 1); if (idx >= (uint32_t)func->const_count) return false; ant_value_t cv = func->constants[idx]; - if (vtype(cv) != T_CFUNC) return false; + if (vtype(cv) != T_NTARG) return false; break; } case OP_RE_EXEC_TRUTHY: diff --git a/src/streams/queuing.c b/src/streams/queuing.c index 04dceed..e5f4c61 100644 --- a/src/streams/queuing.c +++ b/src/streams/queuing.c @@ -76,7 +76,7 @@ static ant_value_t js_bytelength_qs_ctor(ant_t *js, ant_value_t *args, int nargs static ant_value_t make_size_fn(ant_t *js, ant_cfunc_t cfunc, int length) { ant_value_t obj = js_mkobj(js); - js_set_slot(obj, SLOT_CFUNC, js_mkfun(cfunc)); + js_set_slot(obj, SLOT_CFUNC, js_mkfun_dyn(cfunc)); js_mkprop_fast(js, obj, "name", 4, js_mkstr(js, "size", 4)); js_set_descriptor(js, obj, "name", 4, 0); js_mkprop_fast(js, obj, "length", 6, js_mknum(length)); diff --git a/tests/test_native_method_descriptors.cjs b/tests/test_native_method_descriptors.cjs new file mode 100644 index 0000000..7b18366 --- /dev/null +++ b/tests/test_native_method_descriptors.cjs @@ -0,0 +1,149 @@ +let pass = true; +const { EventEmitter } = require('events'); + +if ('a' + [].at !== 'afunction at() { [native code] }') { + console.log('FAIL: built-in methods should stringify with their exposed property name'); + pass = false; +} + +if (fetch.name !== 'fetch') { + console.log('FAIL: global native functions should expose their property name'); + pass = false; +} + +if (Number.parseInt !== globalThis.parseInt || Number.parseFloat !== globalThis.parseFloat) { + console.log('FAIL: Number parse aliases should reuse the global native functions'); + pass = false; +} + +if (Function.prototype.toString.call(fetch) !== 'function fetch() { [native code] }') { + console.log('FAIL: Function.prototype.toString should use the exposed global name for native functions'); + pass = false; +} + +const nameDesc = Object.getOwnPropertyDescriptor(Function.prototype.toString, 'name'); +if (!nameDesc || nameDesc.value !== 'toString') { + console.log('FAIL: Function.prototype.toString should expose a name descriptor'); + pass = false; +} + +if (!nameDesc || nameDesc.writable !== false || nameDesc.enumerable !== false || nameDesc.configurable !== true) { + console.log('FAIL: Function.prototype.toString.name descriptor flags should match builtin functions'); + pass = false; +} + +const lengthDesc = Object.getOwnPropertyDescriptor(Function.prototype.toString, 'length'); +if (!lengthDesc) { + console.log('FAIL: Function.prototype.toString should expose a length descriptor'); + pass = false; +} + +if (!lengthDesc || lengthDesc.writable !== false || lengthDesc.enumerable !== false || lengthDesc.configurable !== true) { + console.log('FAIL: Function.prototype.toString.length descriptor flags should match builtin functions'); + pass = false; +} + +const names = Object.getOwnPropertyNames(Function.prototype.toString); +if (!names.includes('name') || !names.includes('length')) { + console.log('FAIL: Object.getOwnPropertyNames should include builtin function metadata'); + pass = false; +} + +if (String.prototype.toLowerCase.name !== 'toLowerCase') { + console.log('FAIL: shared native entrypoints should preserve the primary exposed name'); + pass = false; +} + +if (String.prototype.toLocaleLowerCase.name !== 'toLocaleLowerCase') { + console.log('FAIL: shared native entrypoints should preserve alternate exposed names'); + pass = false; +} + +if (URLSearchParams.prototype[Symbol.iterator] !== URLSearchParams.prototype.entries) { + console.log('FAIL: URLSearchParams @@iterator should alias entries'); + pass = false; +} + +if (URLSearchParams.prototype[Symbol.iterator].name !== 'entries') { + console.log('FAIL: URLSearchParams @@iterator should keep the entries name'); + pass = false; +} + +if (Headers.prototype[Symbol.iterator] !== Headers.prototype.entries) { + console.log('FAIL: Headers @@iterator should alias entries'); + pass = false; +} + +if (Headers.prototype[Symbol.iterator].name !== 'entries') { + console.log('FAIL: Headers @@iterator should keep the entries name'); + pass = false; +} + +if (Map.prototype[Symbol.iterator] !== Map.prototype.entries) { + console.log('FAIL: Map @@iterator should alias entries'); + pass = false; +} + +if (Map.prototype[Symbol.iterator].name !== 'entries') { + console.log('FAIL: Map @@iterator should keep the entries name'); + pass = false; +} + +if (Set.prototype.keys !== Set.prototype.values || Set.prototype[Symbol.iterator] !== Set.prototype.values) { + console.log('FAIL: Set iterator aliases should reuse values'); + pass = false; +} + +if (Set.prototype[Symbol.iterator].name !== 'values') { + console.log('FAIL: Set @@iterator should keep the values name'); + pass = false; +} + +if (FormData.prototype[Symbol.iterator] !== FormData.prototype.entries) { + console.log('FAIL: FormData @@iterator should alias entries'); + pass = false; +} + +if (EventEmitter.prototype.on !== EventEmitter.prototype.addListener) { + console.log('FAIL: EventEmitter.addListener should alias on'); + pass = false; +} + +if (EventEmitter.prototype.off !== EventEmitter.prototype.removeListener) { + console.log('FAIL: EventEmitter.removeListener should alias off'); + pass = false; +} + +if (process.on !== process.addListener || process.off !== process.removeListener) { + console.log('FAIL: process event aliases should reuse the canonical methods'); + pass = false; +} + +if ( + process.stdin.off !== process.stdin.removeListener || + process.stdout.off !== process.stdout.removeListener || + process.stderr.off !== process.stderr.removeListener +) { + console.log('FAIL: stdio removeListener aliases should reuse the canonical methods'); + pass = false; +} + +if (Uint8Array.prototype[Symbol.iterator] !== Uint8Array.prototype.values) { + console.log('FAIL: TypedArray @@iterator should alias values'); + pass = false; +} + +if (Uint8Array.prototype[Symbol.iterator].name !== 'values') { + console.log('FAIL: TypedArray @@iterator should keep the values name'); + pass = false; +} + +const wrapped = (() => {}).bind(null); +Object.defineProperty(wrapped, 'name', nameDesc); + +if (wrapped.name !== 'toString') { + console.log('FAIL: builtin function name descriptors should round-trip through defineProperty'); + pass = false; +} + +if (pass) console.log('PASS');