From d512fde0470b2c4780d036e5dd7868d652aa670a Mon Sep 17 00:00:00 2001 From: nishad shabbir Date: Fri, 21 Aug 2026 11:50:56 +0530 Subject: [PATCH 1/2] Don't generate a bare "$" from an empty import or export name --generate-names derives names from import and export names, and builds them as "$" followed by the name. An empty export name therefore produces just "$", which is not a valid identifier, so wasm2wat emits output it cannot read back: (func $ (type $t0)) (export "" (func $)) error: empty identifier. Skip an empty name so the item keeps whatever name it gets from the rest of the pass: another export name if it has one, otherwise the index-based name that would have been used anyway. --- src/generate-names.cc | 6 +++++ test/roundtrip/generate-empty-export-name.txt | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/roundtrip/generate-empty-export-name.txt diff --git a/src/generate-names.cc b/src/generate-names.cc index f61b4ff58e..c562a668c3 100644 --- a/src/generate-names.cc +++ b/src/generate-names.cc @@ -179,6 +179,12 @@ void NameGenerator::MaybeUseAndBindName(BindingHash* bindings, Index index, std::string* str) { if (!HasName(*str)) { + if (!HasName(name)) { + // An empty import or export name would produce a bare "$", which is not + // a valid identifier. Leave the name unset; the index-based pass that + // runs afterwards will give it a usable one. + return; + } unsigned disambiguator = 0; while (true) { GenerateName(name, kInvalidIndex, disambiguator, str); diff --git a/test/roundtrip/generate-empty-export-name.txt b/test/roundtrip/generate-empty-export-name.txt new file mode 100644 index 0000000000..ede62d7ff3 --- /dev/null +++ b/test/roundtrip/generate-empty-export-name.txt @@ -0,0 +1,22 @@ +;;; TOOL: run-roundtrip +;;; ARGS: --stdout --generate-names + +(module + ;; With no name section the generated names come from the exports. An empty + ;; export name cannot be used as an identifier, since it would produce a bare + ;; "$", so it has to be skipped: this func has no other export to take a name + ;; from and falls back to an index-based name. + (func $f) + (export "" (func $f)) + + (func $g) + (export "named" (func $g)) +) +(;; STDOUT ;;; +(module + (type $t0 (func)) + (func $f0 (type $t0)) + (func $named (type $t0)) + (export "" (func $f0)) + (export "named" (func $named))) +;;; STDOUT ;;) From f4e5059ef1f2aad9c09e8aef3f536a485d495f14 Mon Sep 17 00:00:00 2001 From: nishad shabbir Date: Fri, 21 Aug 2026 13:46:10 +0530 Subject: [PATCH 2/2] Update wasm2c expectation for the renamed function wasm2c derives its C identifiers from the same generated names, so the func that used to be named "$" is now named after the export it does have. Its C symbol changes from w2c_test__0 to w2c_test_0x2A0x2F_0. --- test/wasm2c/export-names.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/wasm2c/export-names.txt b/test/wasm2c/export-names.txt index 775054988a..dfce091aba 100644 --- a/test/wasm2c/export-names.txt +++ b/test/wasm2c/export-names.txt @@ -926,7 +926,7 @@ DEFINE_TABLE_FILL(externref) wasm_rt_tailcallee_t* next) #endif -static void w2c_test__0(w2c_test*); +static void w2c_test_0x2A0x2F_0(w2c_test*); FUNC_TYPE_T(w2c_test_t0) = "\x36\xa9\xe7\xf1\xc9\x5b\x82\xff\xb9\x97\x43\xe0\xc5\xc4\xce\x95\xd8\x3c\x9a\x43\x0a\xac\x59\xf8\x4e\xf3\xcb\xfa\xb6\x14\x50\x68"; @@ -941,7 +941,7 @@ void w2c_test_(w2c_test* instance) { #endif wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif - w2c_test__0(instance); + w2c_test_0x2A0x2F_0(instance); #if WASM_RT_USE_SEGUE_FOR_THIS_MODULE && !WASM_RT_SEGUE_FREE_SEGMENT wasm_rt_segue_write_base(segue_saved_base); #endif @@ -955,7 +955,7 @@ void w2c_test_0x2A0x2F(w2c_test* instance) { #endif wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif - w2c_test__0(instance); + w2c_test_0x2A0x2F_0(instance); #if WASM_RT_USE_SEGUE_FOR_THIS_MODULE && !WASM_RT_SEGUE_FREE_SEGMENT wasm_rt_segue_write_base(segue_saved_base); #endif @@ -969,7 +969,7 @@ void w2c_test_0x3F0x3F0x2F(w2c_test* instance) { #endif wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif - w2c_test__0(instance); + w2c_test_0x2A0x2F_0(instance); #if WASM_RT_USE_SEGUE_FOR_THIS_MODULE && !WASM_RT_SEGUE_FREE_SEGMENT wasm_rt_segue_write_base(segue_saved_base); #endif @@ -983,7 +983,7 @@ void w2c_test_0x0A(w2c_test* instance) { #endif wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif - w2c_test__0(instance); + w2c_test_0x2A0x2F_0(instance); #if WASM_RT_USE_SEGUE_FOR_THIS_MODULE && !WASM_RT_SEGUE_FREE_SEGMENT wasm_rt_segue_write_base(segue_saved_base); #endif @@ -997,7 +997,7 @@ void w2c_test_0xE20x9D0xA40xEF0xB80x8F(w2c_test* instance) { #endif wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif - w2c_test__0(instance); + w2c_test_0x2A0x2F_0(instance); #if WASM_RT_USE_SEGUE_FOR_THIS_MODULE && !WASM_RT_SEGUE_FREE_SEGMENT wasm_rt_segue_write_base(segue_saved_base); #endif @@ -1045,7 +1045,7 @@ wasm_rt_func_type_t wasm2c_test_get_func_type(uint32_t param_count, uint32_t res return NULL; } -void w2c_test__0(w2c_test* instance) { +void w2c_test_0x2A0x2F_0(w2c_test* instance) { FUNC_PROLOGUE; uint8_t* const wasm_rt_local_memory_base = (instance->w2c_0x5Cmodule_import0x200x2A0x2F)->data; uint64_t wasm_rt_local_memory_size = (instance->w2c_0x5Cmodule_import0x200x2A0x2F)->size;