diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 7c8dcab97be..d76d9ca7c8c 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 f798496ad1c..cd2287b80dc 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -1220,6 +1220,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 +// once not needed anymore. +BINARYEN_API char* +BinaryenExpressionAllocateAndWriteText(BinaryenExpressionRef expr); // Block diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index a3494d31ca4..1d7f88122bf 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; @@ -3308,12 +3316,14 @@ 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 = Module['_BinaryenExpressionAllocateAndWriteText'](expr); + try { + return UTF8ToString(textPtr); + } finally { + if (textPtr) { + _free(textPtr); + } + } }; // Calls a function, wrapping it in error handling code so that if it hits a 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) {