From 6457bc608da0775645cbebfe8d8667f469c0ee7d Mon Sep 17 00:00:00 2001 From: Chris Harvey <1362083+chharvey@users.noreply.github.com> Date: Sat, 16 May 2026 19:36:57 -0400 Subject: [PATCH 1/6] feat: add BinaryenExpressionAllocateAndWriteText --- src/binaryen-c.cpp | 14 ++++++++++++++ src/binaryen-c.h | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 0f8cb4805f9..592d426b1c6 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -2077,6 +2077,20 @@ BinaryenExpressionRef BinaryenExpressionCopy(BinaryenExpressionRef expr, BinaryenModuleRef module) { return ExpressionManipulator::copy(expr, *(Module*)module); } +char* BinaryenExpressionAllocateAndWriteText(BinaryenExpressionRef expr) { + std::ostringstream os; + bool colors = Colors::isEnabled(); + + Colors::setEnabled(false); // do not use colors for writing + os << *(Expression*)expr; + Colors::setEnabled(colors); // restore colors state + + auto str = os.str(); + const size_t len = str.length() + 1; + char* output = (char*)malloc(len); + std::copy_n(str.c_str(), len, output); + return output; +} // Specific expression utility diff --git a/src/binaryen-c.h b/src/binaryen-c.h index fbde9d2a08d..dfafbc6efa4 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -1218,6 +1218,10 @@ BINARYEN_API void BinaryenExpressionFinalize(BinaryenExpressionRef expr); // Makes a deep copy of the given expression. BINARYEN_API BinaryenExpressionRef BinaryenExpressionCopy(BinaryenExpressionRef expr, BinaryenModuleRef module); +// Serialize an expression in s-expression form. Implicitly allocates the returned +// char* with malloc(), and expects the user to free() them manually +// once not needed anymore. +BINARYEN_API char* BinaryenExpressionAllocateAndWriteText(BinaryenExpressionRef expr); // Block From b3f7afbb72117702758d587ec03801cbc04e523a Mon Sep 17 00:00:00 2001 From: Chris Harvey <1362083+chharvey@users.noreply.github.com> Date: Tue, 19 May 2026 19:29:44 -0400 Subject: [PATCH 2/6] fix: lint --- src/binaryen-c.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/binaryen-c.h b/src/binaryen-c.h index dfafbc6efa4..c6f0ce1d6e9 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -1218,10 +1218,11 @@ BINARYEN_API void BinaryenExpressionFinalize(BinaryenExpressionRef expr); // Makes a deep copy of the given expression. BINARYEN_API BinaryenExpressionRef BinaryenExpressionCopy(BinaryenExpressionRef expr, BinaryenModuleRef module); -// Serialize an expression in s-expression form. Implicitly allocates the returned -// char* with malloc(), and expects the user to free() them manually +// Serialize an expression in s-expression form. Implicitly allocates the +// returned char* with malloc(), and expects the user to free() them manually // once not needed anymore. -BINARYEN_API char* BinaryenExpressionAllocateAndWriteText(BinaryenExpressionRef expr); +BINARYEN_API char* +BinaryenExpressionAllocateAndWriteText(BinaryenExpressionRef expr); // Block From fdbba7fdc285f98409c973f7dbced503dd24b572 Mon Sep 17 00:00:00 2001 From: Chris Harvey <1362083+chharvey@users.noreply.github.com> Date: Tue, 19 May 2026 19:41:48 -0400 Subject: [PATCH 3/6] feat: update `Module['emitText']` to use new C api --- src/js/binaryen.js-post.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index a3494d31ca4..f9d78a4a1e9 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -3308,12 +3308,10 @@ Module['emitText'] = function(expr) { if (typeof expr === 'object') { return expr.emitText(); } - const old = out; - let ret = ''; - out = x => { ret += x + '\n' }; - Module['_BinaryenExpressionPrint'](expr); - out = old; - return ret; + const textPtr = BinaryenObj["_BinaryenExpressionAllocateAndWriteText"](expr); + const text = UTF8ToString(textPtr); + if (textPtr) _free(textPtr); + return text; }; // Calls a function, wrapping it in error handling code so that if it hits a From 8f69c851c1c428255b5d2dbeb2de667df4f25725 Mon Sep 17 00:00:00 2001 From: Chris Harvey <1362083+chharvey@users.noreply.github.com> Date: Tue, 19 May 2026 20:14:34 -0400 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20wrap=20UTF8ToString=20in=20try?= =?UTF-8?q?=E2=80=93finally?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/js/binaryen.js-post.js | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index f9d78a4a1e9..742bd60cab7 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -2905,16 +2905,24 @@ function wrapModule(module, self = {}) { return Module['_BinaryenGetElementSegmentByIndex'](module, index); }; self['emitText'] = function() { - let textPtr = Module['_BinaryenModuleAllocateAndWriteText'](module); - let text = UTF8ToString(textPtr); - if (textPtr) _free(textPtr); - return text; + const textPtr = Module['_BinaryenModuleAllocateAndWriteText'](module); + try { + return UTF8ToString(textPtr); + } finally { + if (textPtr) { + _free(textPtr); + } + } }; self['emitStackIR'] = function() { - let textPtr = Module['_BinaryenModuleAllocateAndWriteStackIR'](module); - let text = UTF8ToString(textPtr); - if (textPtr) _free(textPtr); - return text; + const textPtr = Module['_BinaryenModuleAllocateAndWriteStackIR'](module); + try { + return UTF8ToString(textPtr); + } finally { + if (textPtr) { + _free(textPtr); + } + } }; self['emitAsmjs'] = function() { const old = out; @@ -3309,9 +3317,13 @@ Module['emitText'] = function(expr) { return expr.emitText(); } const textPtr = BinaryenObj["_BinaryenExpressionAllocateAndWriteText"](expr); - const text = UTF8ToString(textPtr); - if (textPtr) _free(textPtr); - return text; + try { + return UTF8ToString(textPtr); + } finally { + if (textPtr) { + _free(textPtr); + } + } }; // Calls a function, wrapping it in error handling code so that if it hits a From 0843a21fd6d5aba6ffbb1539b923baaef13ee4a5 Mon Sep 17 00:00:00 2001 From: Chris Harvey <1362083+chharvey@users.noreply.github.com> Date: Tue, 19 May 2026 21:41:23 -0400 Subject: [PATCH 5/6] fixup! fdbba7fdc --- src/js/binaryen.js-post.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 742bd60cab7..1d7f88122bf 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -3316,7 +3316,7 @@ Module['emitText'] = function(expr) { if (typeof expr === 'object') { return expr.emitText(); } - const textPtr = BinaryenObj["_BinaryenExpressionAllocateAndWriteText"](expr); + const textPtr = Module['_BinaryenExpressionAllocateAndWriteText'](expr); try { return UTF8ToString(textPtr); } finally { From 4ad5310b285ff521275d1768cbf4d08de03f7086 Mon Sep 17 00:00:00 2001 From: Chris Harvey <1362083+chharvey@users.noreply.github.com> Date: Wed, 27 May 2026 11:37:45 -0400 Subject: [PATCH 6/6] test: add test to kitchen sink --- test/example/c-api-kitchen-sink.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index a03f670034d..eeffd8fd35e 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -1280,6 +1280,14 @@ void test_core() { BinaryenExpressionPrint( valueList[3]); // test printing a standalone expression + // test stringifying an expression + { + char* textPtr = BinaryenExpressionAllocateAndWriteText(valueList[3]); + assert(textPtr); + assert(strstr(textPtr, "f32.neg")); + assert(!strstr(textPtr, "\x1b")); // ensure color escapes are not emitted + free(textPtr); + } // Add drops of concrete expressions for (int i = 0; i < sizeof(valueList) / sizeof(valueList[0]); ++i) {