diff --git a/include/internal.h b/include/internal.h index 192d4e7..8b9307b 100644 --- a/include/internal.h +++ b/include/internal.h @@ -359,7 +359,6 @@ static inline bool js_cfunc_same_entrypoint(ant_value_t fn_val, ant_cfunc_t fn) 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); diff --git a/src/ant.c b/src/ant.c index 416a7f0..4e6ce66 100644 --- a/src/ant.c +++ b/src/ant.c @@ -2293,13 +2293,6 @@ js_intern_stats_t js_intern_stats(void) { }; } -bool is_internal_prop(const char *key, ant_offset_t klen) { - if (klen < 2) return false; - if (key[0] != '_' || key[1] != '_') return false; - if (klen == STR_PROTO_LEN && memcmp(key, STR_PROTO, STR_PROTO_LEN) == 0) return false; - return true; -} - struct func_format { const char *prefix; size_t prefix_len; @@ -6031,7 +6024,7 @@ static ant_value_t object_enum(ant_t *js, ant_value_t obj, enum obj_enum_mode mo ant_object_t *ptr = js_obj_ptr(obj); if (!ptr || !ptr->shape) return mkarr(js); if (ptr->is_exotic && ptr->exotic_keys) { - if (mode == OBJ_ENUM_KEYS && (!ptr->exotic_ops || !ptr->exotic_ops->getter)) return ptr->exotic_keys(js, obj); + if (mode == OBJ_ENUM_KEYS) return ptr->exotic_keys(js, obj); if (ptr->exotic_ops && ptr->exotic_ops->getter) { dynamic_kv_mapper_fn mapper = (mode == OBJ_ENUM_ENTRIES) ? map_to_entry : NULL; return iterate_dynamic_keys(js, obj, mapper); @@ -6069,7 +6062,6 @@ static ant_value_t object_enum(ant_t *js, ant_value_t obj, enum obj_enum_mode mo ant_offset_t klen = (ant_offset_t)strlen(key); ant_value_t val = ant_object_prop_get_unchecked(ptr, i); - if (is_internal_prop(key, klen)) continue; if (is_arr && is_array_index(key, klen)) { ant_offset_t doff = get_dense_buf(obj); if (doff) { @@ -6259,7 +6251,6 @@ static ant_value_t for_in_keys_add(ant_t *js, ant_value_t out, ant_value_t seen, ant_offset_t key_off = vstr(js, key, &key_len); const char *key_ptr = (const char *)(uintptr_t)(key_off); - if (is_internal_prop(key_ptr, key_len)) goto done; if (lkp(js, seen, key_ptr, key_len) != 0) goto done; ant_value_t mark = setprop_cstr(js, seen, key_ptr, key_len, js_true); @@ -6352,8 +6343,6 @@ shape_iter: const char *kstr = prop->key.interned; klen = (ant_offset_t)strlen(kstr); - if (is_internal_prop(kstr, klen)) continue; - if (is_arr && is_array_index(kstr, klen)) { doff = get_dense_buf(as_cur); if (doff) { @@ -7245,7 +7234,6 @@ static ant_value_t builtin_object_defineProperties(ant_t *js, ant_value_t *args, ant_value_t descriptor = js_mkundef(); while (js_prop_iter_next(&iter, &key, &key_len, &descriptor)) { - if (is_internal_prop(key, key_len)) continue; ant_value_t prop_key = js_mkstr(js, key, key_len); ant_value_t define_args[3] = { obj, prop_key, descriptor }; ant_value_t result = builtin_object_defineProperty(js, define_args, 3); @@ -7273,7 +7261,7 @@ bool js_is_own_enumerable_prop( return (ant_shape_get_attrs(source_ptr->shape, key->slot) & ANT_PROP_ATTR_ENUMERABLE) != 0; } - if (!key->str || is_internal_prop(key->str, key->key_len)) return false; + if (!key->str) return false; if (!source_ptr || source_ptr->is_exotic) { descriptor_entry_t *desc = lookup_descriptor(js_as_obj(source), key->str, key->key_len); @@ -7348,8 +7336,6 @@ ant_value_t builtin_object_freeze(ant_t *js, ant_value_t *args, int nargs) { if (prop->type == ANT_SHAPE_KEY_STRING) { const char *key = prop->key.interned; - size_t klen = strlen(key); - if (is_internal_prop(key, klen)) continue; ant_shape_set_attrs_interned(ptr->shape, key, attrs); } else ant_shape_set_attrs_symbol(ptr->shape, prop->key.sym_off, attrs); } @@ -7395,8 +7381,6 @@ static ant_value_t builtin_object_seal(ant_t *js, ant_value_t *args, int nargs) if (prop->type == ANT_SHAPE_KEY_STRING) { const char *key = prop->key.interned; - size_t klen = strlen(key); - if (is_internal_prop(key, klen)) continue; ant_shape_set_attrs_interned(ptr->shape, key, attrs); } else { ant_shape_set_attrs_symbol(ptr->shape, prop->key.sym_off, attrs); @@ -7708,7 +7692,6 @@ static ant_value_t builtin_object_getOwnPropertyNames(ant_t *js, ant_value_t *ar const char *key = prop->key.interned; ant_offset_t klen = (ant_offset_t)strlen(key); - if (is_internal_prop(key, klen)) continue; if (is_arr_obj && own_prop_names_is_dense_shadow(js, obj, key, klen)) continue; arr_set(js, arr, idx++, js_mkstr(js, key, (size_t)klen)); } @@ -14587,7 +14570,7 @@ static ant_value_t setup_func_prototype_property(ant_t *js, ant_value_t func, bo } 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; + if (vtype(cfunc) != T_CFUNC || !name) return cfunc; const ant_cfunc_meta_t *base = js_as_cfunc_meta(cfunc); if (!base) return cfunc; diff --git a/src/modules/ffi.c b/src/modules/ffi.c index 887caf3..b7ac171 100644 --- a/src/modules/ffi.c +++ b/src/modules/ffi.c @@ -16,6 +16,7 @@ #include #include +#include "ptr.h" #include "errors.h" #include "gc/modules.h" #include "internal.h" @@ -66,6 +67,8 @@ typedef struct ffi_callback { UT_hash_handle hh; } ffi_callback_t; +enum { FFI_LIBRARY_NATIVE_TAG = 0x4646494Cu }; // FFIL + static ffi_lib_t *ffi_libraries = NULL; static ffi_ptr_t *ffi_pointers = NULL; static ffi_callback_t *ffi_callbacks = NULL; @@ -83,67 +86,16 @@ static const UT_icd ffi_func_icd = { .dtor = NULL, }; -static ant_value_t ffi_dlopen(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_define(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_lib_call(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_call_function(ant_t *js, ffi_func_t *func, ant_value_t *args, int nargs); -static ant_value_t ffi_alloc_memory(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_free_memory(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_read_memory(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_write_memory(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_get_pointer(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_create_callback(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_free_callback(ant_t *js, ant_value_t *args, int nargs); -static ant_value_t ffi_read_ptr(ant_t *js, ant_value_t *args, int nargs); - static ffi_type *get_ffi_type(const char *type_str); static void *js_to_ffi_value(ant_t *js, ant_value_t val, ffi_type *type, void *buffer); -static ant_value_t ffi_to_js_value(ant_t *js, void *val, ffi_type *type, const char *type_str); -static void ffi_callback_handler(ffi_cif *cif, void *ret, void **args, void *user_data); - -ant_value_t ffi_library(ant_t *js) { - ant_value_t ffi_obj = js_mkobj(js); - - js_set(js, ffi_obj, "dlopen", js_mkfun(ffi_dlopen)); - js_set(js, ffi_obj, "alloc", js_mkfun(ffi_alloc_memory)); - js_set(js, ffi_obj, "free", js_mkfun(ffi_free_memory)); - js_set(js, ffi_obj, "read", js_mkfun(ffi_read_memory)); - js_set(js, ffi_obj, "write", js_mkfun(ffi_write_memory)); - js_set(js, ffi_obj, "pointer", js_mkfun(ffi_get_pointer)); - js_set(js, ffi_obj, "callback", js_mkfun(ffi_create_callback)); - js_set(js, ffi_obj, "freeCallback", js_mkfun(ffi_free_callback)); - js_set(js, ffi_obj, "readPtr", js_mkfun(ffi_read_ptr)); - - const char *suffix; -#ifdef __APPLE__ - suffix = "dylib"; -#elif defined(__linux__) - suffix = "so"; -#elif defined(_WIN32) - suffix = "dll"; -#else - suffix = "so"; -#endif - js_set(js, ffi_obj, "suffix", js_mkstr(js, suffix, strlen(suffix))); - ant_value_t ffi_types = js_mkobj(js); - js_set(js, ffi_types, "void", js_mkstr(js, "void", 4)); - js_set(js, ffi_types, "int8", js_mkstr(js, "int8", 4)); - js_set(js, ffi_types, "int16", js_mkstr(js, "int16", 5)); - js_set(js, ffi_types, "int", js_mkstr(js, "int", 3)); - js_set(js, ffi_types, "int64", js_mkstr(js, "int64", 5)); - js_set(js, ffi_types, "uint8", js_mkstr(js, "uint8", 5)); - js_set(js, ffi_types, "uint16", js_mkstr(js, "uint16", 6)); - js_set(js, ffi_types, "uint64", js_mkstr(js, "uint64", 6)); - js_set(js, ffi_types, "float", js_mkstr(js, "float", 5)); - js_set(js, ffi_types, "double", js_mkstr(js, "double", 6)); - js_set(js, ffi_types, "pointer", js_mkstr(js, "pointer", 7)); - 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_sym(js, ffi_obj, get_toStringTag_sym(), js_mkstr(js, "FFI", 3)); +static ant_value_t ffi_define(ant_t *js, ant_value_t *args, int nargs); +static ant_value_t ffi_lib_call(ant_t *js, ant_value_t *args, int nargs); +static ant_value_t ffi_to_js_value(ant_t *js, void *val, ffi_type *type, const char *type_str); - return ffi_obj; +static ffi_lib_t *ffi_get_library_this(ant_t *js, ant_value_t this_obj) { + if (!is_object_type(this_obj) || !js_check_native_tag(this_obj, FFI_LIBRARY_NATIVE_TAG)) return NULL; + return (ffi_lib_t *)js_get_native_ptr(this_obj); } static void ffi_init_array(void) { @@ -193,7 +145,9 @@ static ant_value_t ffi_dlopen(ant_t *js, ant_value_t *args, int nargs) { lib->handle = handle; lib->js_obj = js_mkobj(js); - js_set(js, lib->js_obj, "__lib_ptr", js_mknum((double)(uint64_t)lib)); + js_set_native_ptr(lib->js_obj, lib); + js_set_native_tag(lib->js_obj, FFI_LIBRARY_NATIVE_TAG); + js_set(js, lib->js_obj, "define", js_mkfun(ffi_define)); js_set(js, lib->js_obj, "call", js_mkfun(ffi_lib_call)); @@ -205,27 +159,26 @@ static ant_value_t ffi_dlopen(ant_t *js, ant_value_t *args, int nargs) { } static ant_value_t ffi_define(ant_t *js, ant_value_t *args, int nargs) { + ant_value_t this_obj; + ffi_lib_t *lib; + if (nargs < 2 || vtype(args[0]) != T_STR) { return js_mkerr(js, "define() requires function name string and signature"); } - ant_value_t this_obj = js_getthis(js); - ant_value_t lib_ptr_val = js_get(js, this_obj, "__lib_ptr"); - if (vtype(lib_ptr_val) != T_NUM) { - return js_mkerr(js, "Invalid library object"); - } + this_obj = js_getthis(js); + lib = ffi_get_library_this(js, this_obj); + if (!lib) return js_mkerr(js, "Invalid library object"); size_t func_name_len; const char *func_name = js_getstr(js, args[0], &func_name_len); ant_value_t sig = args[1]; int sig_type = vtype(sig); - if (sig_type == T_STR || sig_type == T_NUM || sig_type == T_NULL || - sig_type == T_UNDEF) { - return js_mkerr(js, - "Signature must be an array [returnType, [argTypes...]] or " - "an object {args: [...], returns: type}"); - } + + if (sig_type == T_STR || sig_type == T_NUM || sig_type == T_NULL || sig_type == T_UNDEF) return js_mkerr(js, + "Signature must be an array [returnType, [argTypes...]] or an object {args: [...], returns: type}" + ); const char *ret_type_str; ant_value_t arg_types_arr; @@ -273,7 +226,6 @@ static ant_value_t ffi_define(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr(js, "Unknown return type: %s", ret_type_str); } - ffi_lib_t *lib = (ffi_lib_t *)(uint64_t)js_getnum(lib_ptr_val); void *func_ptr = dlsym(lib->handle, func_name); if (!func_ptr) { return js_mkerr(js, "Function '%s' not found", func_name); @@ -353,17 +305,16 @@ static ant_value_t ffi_define(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t ffi_lib_call(ant_t *js, ant_value_t *args, int nargs) { ant_value_t lib_obj = js_getthis(js); - if (nargs < 1 || vtype(args[0]) != T_STR) - return js_mkerr(js, "call() requires function name string"); + + if (!ffi_get_library_this(js, lib_obj)) return js_mkerr(js, "Invalid library object"); + if (nargs < 1 || vtype(args[0]) != T_STR) return js_mkerr(js, "call() requires function name string"); size_t func_name_len; const char *func_name = js_getstr(js, args[0], &func_name_len); ant_value_t ffi_val = js_get(js, lib_obj, func_name); int func_index = js_getffi(ffi_val); - if (func_index < 0) { - return js_mkerr(js, "Function '%s' not defined", func_name); - } + if (func_index < 0) return js_mkerr(js, "Function '%s' not defined", func_name); return ffi_call_by_index(js, (unsigned int)func_index, args + 1, nargs - 1); } @@ -994,6 +945,51 @@ ret_undef: return js_mkundef(); } +ant_value_t ffi_library(ant_t *js) { + ant_value_t ffi_obj = js_mkobj(js); + + js_set(js, ffi_obj, "dlopen", js_mkfun(ffi_dlopen)); + js_set(js, ffi_obj, "alloc", js_mkfun(ffi_alloc_memory)); + js_set(js, ffi_obj, "free", js_mkfun(ffi_free_memory)); + js_set(js, ffi_obj, "read", js_mkfun(ffi_read_memory)); + js_set(js, ffi_obj, "write", js_mkfun(ffi_write_memory)); + js_set(js, ffi_obj, "pointer", js_mkfun(ffi_get_pointer)); + js_set(js, ffi_obj, "callback", js_mkfun(ffi_create_callback)); + js_set(js, ffi_obj, "freeCallback", js_mkfun(ffi_free_callback)); + js_set(js, ffi_obj, "readPtr", js_mkfun(ffi_read_ptr)); + + const char *suffix; +#ifdef __APPLE__ + suffix = "dylib"; +#elif defined(__linux__) + suffix = "so"; +#elif defined(_WIN32) + suffix = "dll"; +#else + suffix = "so"; +#endif + js_set(js, ffi_obj, "suffix", js_mkstr(js, suffix, strlen(suffix))); + + ant_value_t ffi_types = js_mkobj(js); + js_set(js, ffi_types, "void", js_mkstr(js, "void", 4)); + js_set(js, ffi_types, "int8", js_mkstr(js, "int8", 4)); + js_set(js, ffi_types, "int16", js_mkstr(js, "int16", 5)); + js_set(js, ffi_types, "int", js_mkstr(js, "int", 3)); + js_set(js, ffi_types, "int64", js_mkstr(js, "int64", 5)); + js_set(js, ffi_types, "uint8", js_mkstr(js, "uint8", 5)); + js_set(js, ffi_types, "uint16", js_mkstr(js, "uint16", 6)); + js_set(js, ffi_types, "uint64", js_mkstr(js, "uint64", 6)); + js_set(js, ffi_types, "float", js_mkstr(js, "float", 5)); + js_set(js, ffi_types, "double", js_mkstr(js, "double", 6)); + js_set(js, ffi_types, "pointer", js_mkstr(js, "pointer", 7)); + 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_sym(js, ffi_obj, get_toStringTag_sym(), js_mkstr(js, "FFI", 3)); + + return ffi_obj; +} + void gc_mark_ffi(ant_t *js, gc_mark_fn mark) { pthread_mutex_lock(&ffi_callbacks_mutex); ffi_callback_t *cb, *tmp; @@ -1002,4 +998,3 @@ void gc_mark_ffi(ant_t *js, gc_mark_fn mark) { } pthread_mutex_unlock(&ffi_callbacks_mutex); } - diff --git a/src/modules/process.c b/src/modules/process.c index b646833..20b4229 100644 --- a/src/modules/process.c +++ b/src/modules/process.c @@ -1446,7 +1446,6 @@ static void env_foreach(ant_t *js, ant_value_t env_obj, env_iter_cb cb, void *ct const char *key; size_t key_len; ant_value_t value; while (js_prop_iter_next(&iter, &key, &key_len, &value)) { - if (key_len >= 2 && key[0] == '_' && key[1] == '_') continue; if (vtype(value) != T_STR) continue; CSTR_BUF(buf, 256); diff --git a/src/modules/reflect.c b/src/modules/reflect.c index db6fd35..df2b2a7 100644 --- a/src/modules/reflect.c +++ b/src/modules/reflect.c @@ -110,8 +110,6 @@ static ant_value_t reflect_own_keys(ant_t *js, ant_value_t *args, int nargs) { const char *key; size_t key_len; ant_value_t value; while (js_prop_iter_next(&iter, &key, &key_len, &value)) { - if (key_len >= 9 && memcmp(key, STR_PROTO, STR_PROTO_LEN) == 0) continue; - if (key_len >= 2 && key[0] == '_' && key[1] == '_') continue; js_arr_push(js, keys_arr, js_mkstr(js, key, key_len)); } diff --git a/tests/test_double_underscore_enumeration.mjs b/tests/test_double_underscore_enumeration.mjs new file mode 100644 index 0000000..8cbea09 --- /dev/null +++ b/tests/test_double_underscore_enumeration.mjs @@ -0,0 +1,52 @@ +function assert(condition, message) { + if (!condition) throw new Error(message); +} + +const plain = { __x: 1, visible: 2 }; +assert( + JSON.stringify(Object.keys(plain)) === JSON.stringify(['__x', 'visible']), + 'Object.keys() should retain double-underscore own properties' +); +assert( + JSON.stringify(Object.entries(plain)) === JSON.stringify([['__x', 1], ['visible', 2]]), + 'Object.entries() should retain double-underscore own properties' +); +assert( + JSON.stringify(Reflect.ownKeys(plain)) === JSON.stringify(['__x', 'visible']), + 'Reflect.ownKeys() should retain double-underscore own properties' +); + +const forInKeys = []; +for (const key in plain) forInKeys.push(key); +assert( + JSON.stringify(forInKeys) === JSON.stringify(['__x', 'visible']), + 'for...in should retain double-underscore own properties' +); + +const ctorBytes = new Uint8Array([ + 0, 97, 115, 109, 1, 0, 0, 0, + 1, 5, 1, 96, 0, 1, 127, + 3, 2, 1, 0, + 7, 21, 1, 17, 95, 95, 119, 97, 115, 109, 95, 99, 97, 108, 108, 95, 99, 116, 111, 114, 115, 0, 0, + 10, 6, 1, 4, 0, 65, 1, 11, +]); + +const module = new WebAssembly.Module(ctorBytes); +const instance = new WebAssembly.Instance(module); +assert( + typeof instance.exports.__wasm_call_ctors === 'function', + 'wasm export named __wasm_call_ctors should be callable' +); +assert( + JSON.stringify(Object.keys(instance.exports)) === JSON.stringify(['__wasm_call_ctors']), + 'Object.keys(instance.exports) should retain __wasm_call_ctors' +); + +const copiedExports = Object.fromEntries(Object.entries(instance.exports)); +assert( + typeof copiedExports.__wasm_call_ctors === 'function', + 'Object.entries(instance.exports) should preserve __wasm_call_ctors for Asyncify-style wrappers' +); +assert(copiedExports.__wasm_call_ctors() === 1, 'copied wasm export should still execute'); + +console.log('double-underscore-enumeration:ok'); diff --git a/tests/test_primitive_wrappers.js b/tests/test_primitive_wrappers.js index 0de2b76..c50adc7 100644 --- a/tests/test_primitive_wrappers.js +++ b/tests/test_primitive_wrappers.js @@ -5,43 +5,36 @@ let failed = 0; function test(name, condition) { if (condition) { - console.log("✓", name); + console.log('✓', name); passed++; } else { - console.log("✗", name); + console.log('✗', name); failed++; } } // String wrapper -let s = new String("hello"); -test("String wrapper typeof", typeof s === "object"); -test("String wrapper + concat", s + " world" === "hello world"); -test("String wrapper length", s.length === 5); -test("String wrapper valueOf", s.valueOf() === "hello"); +let s = new String('hello'); +test('String wrapper typeof', typeof s === 'object'); +test('String wrapper + concat', s + ' world' === 'hello world'); +test('String wrapper length', s.length === 5); +test('String wrapper valueOf', s.valueOf() === 'hello'); // Number wrapper let n = new Number(42); -test("Number wrapper typeof", typeof n === "object"); -test("Number wrapper + arithmetic", n + 8 === 50); -test("Number wrapper valueOf", n.valueOf() === 42); +test('Number wrapper typeof', typeof n === 'object'); +test('Number wrapper + arithmetic', n + 8 === 50); +test('Number wrapper valueOf', n.valueOf() === 42); // Boolean wrapper let b = new Boolean(true); -test("Boolean wrapper typeof", typeof b === "object"); -test("Boolean wrapper is truthy", !!b === true); -test("Boolean wrapper valueOf", b.valueOf() === true); - -// Slot should be hidden from enumeration -let sKeys = Object.keys(s); -test("String keys excludes internal slot", !sKeys.includes("__primitive_value__")); - -let nKeys = Object.keys(n); -test("Number keys excludes internal slot", !nKeys.includes("__primitive_value__")); +test('Boolean wrapper typeof', typeof b === 'object'); +test('Boolean wrapper is truthy', !!b === true); +test('Boolean wrapper valueOf', b.valueOf() === true); // Object() wrapping primitives -let wrapped = Object("test"); -test("Object(string) is object", typeof wrapped === "object"); -test("Object(string) valueOf", wrapped.valueOf() === "test"); +let wrapped = Object('test'); +test('Object(string) is object', typeof wrapped === 'object'); +test('Object(string) valueOf', wrapped.valueOf() === 'test'); -console.log("\nResults:", passed, "passed,", failed, "failed"); +console.log('\nResults:', passed, 'passed,', failed, 'failed'); diff --git a/tests/test_process_env_double_underscore.cjs b/tests/test_process_env_double_underscore.cjs new file mode 100644 index 0000000..facbf54 --- /dev/null +++ b/tests/test_process_env_double_underscore.cjs @@ -0,0 +1,21 @@ +function assert(condition, message) { + if (!condition) throw new Error(message); +} + +process.env.__ANT_ENV_DOUBLE = 'from-js'; + +assert(process.env.__ANT_ENV_DOUBLE === 'from-js', 'process.env getter should read __* assignments'); + +const keys = Object.keys(process.env); +assert(keys.includes('__ANT_ENV_DOUBLE'), 'Object.keys(process.env) should include __* assignments'); + +const obj = process.env.toObject(); +assert(obj.__ANT_ENV_DOUBLE === 'from-js', 'process.env.toObject() should include __* assignments'); + +const envString = process.env.toString(); +assert( + envString.split('\n').includes('__ANT_ENV_DOUBLE=from-js'), + 'process.env.toString() should include __* assignments' +); + +console.log('process-env-double-underscore:ok');