diff --git a/meson.build b/meson.build index bd56a17..036f6ac 100644 --- a/meson.build +++ b/meson.build @@ -74,7 +74,7 @@ endif build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() version_conf = configuration_data() -version_conf.set('ANT_VERSION', '0.0.8.4') +version_conf.set('ANT_VERSION', '0.0.8.5') version_conf.set('ANT_GIT_HASH', git_hash) version_conf.set('ANT_BUILD_DATE', build_date) diff --git a/src/ant.c b/src/ant.c index d08425a..490597f 100644 --- a/src/ant.c +++ b/src/ant.c @@ -2533,6 +2533,14 @@ static jsoff_t lkp_proto(struct js *js, jsval_t obj, const char *key, size_t len if (vtype(proto) == T_NULL || vtype(proto) == T_UNDEF) break; cur = proto; depth++; + } else if (t == T_CFUNC) { + jsval_t func_proto = get_ctor_proto(js, "Function", 8); + if (vtype(func_proto) == T_OBJ || vtype(func_proto) == T_ARR || vtype(func_proto) == T_FUNC) { + jsval_t as_obj = (vtype(func_proto) == T_OBJ) ? func_proto : mkval(T_OBJ, vdata(func_proto)); + jsoff_t off = lkp(js, as_obj, key, len); + if (off != 0) return off; + } + break; } else break; } @@ -2821,6 +2829,13 @@ static jsval_t do_dot_op(struct js *js, jsval_t l, jsval_t r) { return prop; } + if (t == T_CFUNC) { + jsoff_t off = lkp_proto(js, l, ptr, plen); + if (off != 0) return resolveprop(js, mkval(T_PROP, off)); + if (streq(ptr, plen, "name", 4)) return js_mkstr(js, "", 0); + return js_mkundef(); + } + if (t != T_OBJ && t != T_ARR) { return js_mkerr(js, "lookup in non-obj"); } @@ -7781,6 +7796,50 @@ static jsval_t builtin_object_defineProperty(struct js *js, jsval_t *args, int n return obj; } +static jsval_t builtin_object_toString(struct js *js, jsval_t *args, int nargs) { + (void)args; (void)nargs; + jsval_t obj = js->this_val; + + obj = resolveprop(js, obj); + uint8_t t = vtype(obj); + + if (t == T_OBJ || t == T_ARR || t == T_FUNC) { + jsval_t check_obj = (t == T_FUNC) ? mkval(T_OBJ, vdata(obj)) : obj; + jsoff_t tag_off = lkp(js, check_obj, "@@toStringTag", 13); + if (tag_off != 0) { + jsval_t tag_val = resolveprop(js, mkval(T_PROP, tag_off)); + if (vtype(tag_val) == T_STR) { + jsoff_t tag_len, tag_str_off = vstr(js, tag_val, &tag_len); + const char *tag_str = (const char *)&js->mem[tag_str_off]; + + char buf[256]; + int n = snprintf(buf, sizeof(buf), "[object %.*s]", (int)tag_len, tag_str); + return js_mkstr(js, buf, n); + } + } + } + + const char *type_name = NULL; + + switch (t) { + case T_UNDEF: type_name = "Undefined"; break; + case T_NULL: type_name = "Null"; break; + case T_BOOL: type_name = "Boolean"; break; + case T_NUM: type_name = "Number"; break; + case T_STR: type_name = "String"; break; + case T_ARR: type_name = "Array"; break; + case T_FUNC: type_name = "Function"; break; + case T_ERR: type_name = "Error"; break; + case T_BIGINT: type_name = "BigInt"; break; + case T_OBJ: type_name = "Object"; break; + default: type_name = "Unknown"; break; + } + + char buf[256]; + int n = snprintf(buf, sizeof(buf), "[object %s]", type_name); + return js_mkstr(js, buf, n); +} + static jsval_t builtin_array_push(struct js *js, jsval_t *args, int nargs) { jsval_t arr = js->this_val; arr = resolveprop(js, arr); @@ -10576,6 +10635,7 @@ struct js *js_create(void *buf, size_t len) { jsval_t glob = js->scope; jsval_t object_proto = js_mkobj(js); + setprop(js, object_proto, js_mkstr(js, "toString", 8), js_mkfun(builtin_object_toString)); jsval_t function_proto = js_mkobj(js); set_proto(js, function_proto, object_proto); diff --git a/src/modules/buffer.c b/src/modules/buffer.c index 4ca15f1..22f544e 100644 --- a/src/modules/buffer.c +++ b/src/modules/buffer.c @@ -750,5 +750,6 @@ void init_buffer_module() { jsval_t buffer_obj = js_mkobj(js); js_set(js, buffer_obj, "from", js_mkfun(js_buffer_from)); js_set(js, buffer_obj, "alloc", js_mkfun(js_buffer_alloc)); + js_set(js, buffer_obj, "@@toStringTag", js_mkstr(js, "Buffer", 6)); js_set(js, glob, "Buffer", buffer_obj); } diff --git a/src/modules/crypto.c b/src/modules/crypto.c index 35ae965..b5ea018 100644 --- a/src/modules/crypto.c +++ b/src/modules/crypto.c @@ -134,11 +134,13 @@ void init_crypto_module() { jsval_t ant_obj = rt->ant_obj; jsval_t crypto_obj = js_mkobj(js); - js_set(js, ant_obj, "Crypto", crypto_obj); js_set(js, crypto_obj, "random", js_mkfun(js_crypto_random)); js_set(js, crypto_obj, "randomBytes", js_mkfun(js_crypto_random_bytes)); js_set(js, crypto_obj, "randomUUID", js_mkfun(js_crypto_random_uuid)); js_set(js, crypto_obj, "randomUUIDv7", js_mkfun(js_crypto_random_uuidv7)); + + js_set(js, crypto_obj, "@@toStringTag", js_mkstr(js, "Crypto", 6)); + js_set(js, ant_obj, "Crypto", crypto_obj); } diff --git a/src/modules/ffi.c b/src/modules/ffi.c index dcdced8..817e54a 100644 --- a/src/modules/ffi.c +++ b/src/modules/ffi.c @@ -125,6 +125,7 @@ jsval_t ffi_library(struct js *js) { js_set(js, ffi_types, "string", js_mkstr(js, "string", 6)); js_set(js, ffi_types, "spread", js_mkstr(js, "...", 3)); js_set(js, ffi_obj, "FFIType", ffi_types); + js_set(js, ffi_obj, "@@toStringTag", js_mkstr(js, "FFI", 3)); return ffi_obj; } diff --git a/src/modules/fs.c b/src/modules/fs.c index b77820c..21da792 100644 --- a/src/modules/fs.c +++ b/src/modules/fs.c @@ -725,6 +725,7 @@ jsval_t fs_library(struct js *js) { js_set(js, lib, "rmdirSync", js_mkfun(builtin_fs_rmdirSync)); js_set(js, lib, "stat", js_mkfun(builtin_fs_stat)); js_set(js, lib, "statSync", js_mkfun(builtin_fs_statSync)); + js_set(js, lib, "@@toStringTag", js_mkstr(js, "fs", 2)); return lib; } diff --git a/src/modules/io.c b/src/modules/io.c index 38836ec..5eecc3d 100644 --- a/src/modules/io.c +++ b/src/modules/io.c @@ -192,10 +192,12 @@ void init_console_module() { struct js *js = rt->js; jsval_t console_obj = js_mkobj(js); - js_set(js, js_glob(js), "console", console_obj); js_set(js, console_obj, "log", js_mkfun(js_console_log)); js_set(js, console_obj, "error", js_mkfun(js_console_error)); js_set(js, console_obj, "warn", js_mkfun(js_console_warn)); js_set(js, console_obj, "assert", js_mkfun(js_console_assert)); js_set(js, console_obj, "trace", js_mkfun(js_console_trace)); + + js_set(js, console_obj, "@@toStringTag", js_mkstr(js, "console", 7)); + js_set(js, js_glob(js), "console", console_obj); } \ No newline at end of file diff --git a/src/modules/json.c b/src/modules/json.c index 68f988f..f6e86b3 100644 --- a/src/modules/json.c +++ b/src/modules/json.c @@ -215,7 +215,9 @@ void init_json_module() { struct js *js = rt->js; jsval_t json_obj = js_mkobj(js); - js_set(js, js_glob(js), "JSON", json_obj); js_set(js, json_obj, "parse", js_mkfun(js_json_parse)); js_set(js, json_obj, "stringify", js_mkfun(js_json_stringify)); + + js_set(js, json_obj, "@@toStringTag", js_mkstr(js, "JSON", 4)); + js_set(js, js_glob(js), "JSON", json_obj); } \ No newline at end of file diff --git a/src/modules/path.c b/src/modules/path.c index 00ff068..7de2801 100644 --- a/src/modules/path.c +++ b/src/modules/path.c @@ -411,6 +411,7 @@ jsval_t path_library(struct js *js) { js_set(js, lib, "sep", js_mkstr(js, PATH_SEP_STR, 1)); char delimiter_str[2] = {PATH_DELIMITER, '\0'}; js_set(js, lib, "delimiter", js_mkstr(js, delimiter_str, 1)); + js_set(js, lib, "@@toStringTag", js_mkstr(js, "path", 4)); return lib; } diff --git a/src/modules/process.c b/src/modules/process.c index 8f51c2f..f6d49e7 100644 --- a/src/modules/process.c +++ b/src/modules/process.c @@ -93,7 +93,6 @@ void init_process_module(void) { jsval_t process_obj = js_mkobj(js); jsval_t env_obj = js_mkobj(js); - js_set(js, js_glob(js), "process", process_obj); js_set(js, process_obj, "env", env_obj); js_set(js, process_obj, "exit", js_mkfun(process_exit)); @@ -128,4 +127,7 @@ void init_process_module(void) { load_dotenv_file(js, env_obj); js_set_getter(js, env_obj, env_getter); + + js_set(js, process_obj, "@@toStringTag", js_mkstr(js, "process", 7)); + js_set(js, js_glob(js), "process", process_obj); } diff --git a/src/modules/shell.c b/src/modules/shell.c index 4c45da3..461cf92 100644 --- a/src/modules/shell.c +++ b/src/modules/shell.c @@ -215,7 +215,9 @@ static jsval_t builtin_shell_dollar(struct js *js, jsval_t *args, int nargs) { jsval_t shell_library(struct js *js) { jsval_t lib = js_mkobj(js); + js_set(js, lib, "$", js_mkfun(builtin_shell_dollar)); + js_set(js, lib, "@@toStringTag", js_mkstr(js, "shell", 5)); return lib; } diff --git a/tests/test_all_modules.cjs b/tests/test_all_modules.cjs new file mode 100644 index 0000000..42676fc --- /dev/null +++ b/tests/test_all_modules.cjs @@ -0,0 +1,34 @@ +// Test toStringTag for all modules + +console.log("Testing @@toStringTag for all modules:\n"); + +// Built-in modules +console.log("Atomics:", Object.prototype.toString.call(Atomics)); +console.log("console:", Object.prototype.toString.call(console)); +console.log("JSON:", Object.prototype.toString.call(JSON)); +console.log("process:", Object.prototype.toString.call(process)); +console.log("Buffer:", Object.prototype.toString.call(Buffer)); + +// Test Ant.Crypto if available +if (typeof Ant !== 'undefined' && Ant.Crypto) { + console.log("Ant.Crypto:", Object.prototype.toString.call(Ant.Crypto)); +} + +// Test imported modules +console.log("\nTesting imported modules:"); + +// Import path module +import * as path from 'ant:path'; +console.log("path:", Object.prototype.toString.call(path)); + +// Import fs module +import * as fs from 'ant:fs'; +console.log("fs:", Object.prototype.toString.call(fs)); + +// Import shell module +import * as shell from 'ant:shell'; +console.log("shell:", Object.prototype.toString.call(shell)); + +// Import ffi module +import * as ffi from 'ant:ffi'; +console.log("ffi:", Object.prototype.toString.call(ffi)); diff --git a/tests/test_toString.cjs b/tests/test_toString.cjs new file mode 100644 index 0000000..298db40 --- /dev/null +++ b/tests/test_toString.cjs @@ -0,0 +1,22 @@ +// Test Object.prototype.toString + +console.log("Testing Object.prototype.toString:"); + +// Test with Atomics (has @@toStringTag) +console.log("Object.prototype.toString.call(Atomics):", Object.prototype.toString.call(Atomics)); + +// Test with other built-ins +console.log("\nOther types:"); +console.log("Object.prototype.toString.call({}):", Object.prototype.toString.call({})); +console.log("Object.prototype.toString.call([]):", Object.prototype.toString.call([])); +console.log("Object.prototype.toString.call(function(){}):", Object.prototype.toString.call(function(){})); +console.log("Object.prototype.toString.call(42):", Object.prototype.toString.call(42)); +console.log("Object.prototype.toString.call('hello'):", Object.prototype.toString.call("hello")); +console.log("Object.prototype.toString.call(true):", Object.prototype.toString.call(true)); +console.log("Object.prototype.toString.call(null):", Object.prototype.toString.call(null)); +console.log("Object.prototype.toString.call(undefined):", Object.prototype.toString.call(undefined)); + +// Test with custom @@toStringTag +console.log("\nCustom @@toStringTag:"); +const custom = { "@@toStringTag": "MyCustomType" }; +console.log("Object.prototype.toString.call(custom):", Object.prototype.toString.call(custom)); diff --git a/tests/test_toString_simple.cjs b/tests/test_toString_simple.cjs new file mode 100644 index 0000000..b90ab20 --- /dev/null +++ b/tests/test_toString_simple.cjs @@ -0,0 +1,23 @@ +// Test Object.prototype.toString directly + +console.log("Testing toString:"); + +// Test on objects directly +const obj = {}; +console.log("obj.toString():", obj.toString()); + +const arr = []; +console.log("arr.toString():", arr.toString()); + +const atomics = Atomics; +console.log("Atomics object:", atomics); +console.log("Atomics.toString:", atomics.toString); + +// If toString exists, call it +if (atomics.toString) { + console.log("Calling Atomics.toString():", atomics.toString()); +} + +// Test custom object with @@toStringTag +const custom = { "@@toStringTag": "MyCustomType" }; +console.log("custom.toString():", custom.toString());