diff --git a/build-js.sh b/build-js.sh index 37bdc00ee9f..1e0127d1ce7 100755 --- a/build-js.sh +++ b/build-js.sh @@ -18,7 +18,7 @@ if [ "$1" == "-h" ] || [ "$1" == "--help" ] || [ "$1" == "-help" ]; then echo "usage: $0 [-g]" >&2 echo " -g produce debug build" >&2 echo "" - echo "If EMSCRIPTEN is set in the envionment, emscripten will be loaded" + echo "If EMSCRIPTEN is set in the environment, emscripten will be loaded" echo "from that directory. Otherwise the location of emscripten is resolved" echo "through PATH." exit 1 @@ -811,6 +811,12 @@ export_function "_BinaryenMemoryFillGetDest" export_function "_BinaryenMemoryFillGetValue" export_function "_BinaryenMemoryFillGetSize" +# 'Segments' query operations. +export_function "_BinaryenGetNumMemorySegments" +export_function "_BinaryenGetMemorySegmentByteOffset" +export_function "_BinaryenGetMemorySegmentByteLength" +export_function "_BinaryenCopyMemorySegmentData" + # 'Try' expression operations export_function "_BinaryenTryGetBody" export_function "_BinaryenTryGetCatchBody" @@ -840,12 +846,16 @@ export_function "_BinaryenGetFunctionTypeBySignature" export_function "_BinaryenAddFunction" export_function "_BinaryenGetFunction" export_function "_BinaryenRemoveFunction" +export_function "_BinaryenGetNumFunctions" +export_function "_BinaryenGetFunctionByIndex" export_function "_BinaryenAddGlobal" export_function "_BinaryenGetGlobal" export_function "_BinaryenRemoveGlobal" export_function "_BinaryenAddEvent" export_function "_BinaryenGetEvent" export_function "_BinaryenRemoveEvent" +export_function "_BinaryenGetNumExports" +export_function "_BinaryenGetExportByIndex" export_function "_BinaryenAddFunctionImport" export_function "_BinaryenAddTableImport" export_function "_BinaryenAddMemoryImport" diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 1cb0963a239..6bb6f6e1415 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -3142,6 +3142,26 @@ void BinaryenRemoveFunction(BinaryenModuleRef module, const char* name) { auto* wasm = (Module*)module; wasm->removeFunction(name); } +uint32_t BinaryenGetNumFunctions(BinaryenModuleRef module) { + if (tracing) { + std::cout << " BinaryenGetNumFunctions(the_module);\n"; + } + + auto* wasm = (Module*)module; + return wasm->functions.size(); +} +BinaryenFunctionRef BinaryenGetFunctionByIndex(BinaryenModuleRef module, + BinaryenIndex id) { + if (tracing) { + std::cout << " BinaryenGetFunctionByIndex(the_module, " << id << ");\n"; + } + + auto* wasm = (Module*)module; + if (wasm->functions.size() <= id) { + Fatal() << "invalid function id."; + } + return wasm->functions[id].get(); +} // Globals @@ -3578,6 +3598,83 @@ void BinaryenSetMemory(BinaryenModuleRef module, } } +// Memory segments + +uint32_t BinaryenGetNumMemorySegments(BinaryenModuleRef module) { + if (tracing) { + std::cout << " BinaryenGetNumMemorySegments(the_module);\n"; + } + + auto* wasm = (Module*)module; + return wasm->memory.segments.size(); +} +int64_t BinaryenGetMemorySegmentByteOffset(BinaryenModuleRef module, + BinaryenIndex id) { + if (tracing) { + std::cout << " BinaryenGetMemorySegmentByteOffset(the_module, " << id + << ");\n"; + } + + auto* wasm = (Module*)module; + if (wasm->memory.segments.size() <= id) { + Fatal() << "invalid segment id."; + } + + auto globalOffset = [&](const Expression* const& expr, + int64_t& result) -> bool { + if (auto* c = expr->dynCast()) { + result = c->value.getInteger(); + return true; + } + return false; + }; + + const Memory::Segment& segment = wasm->memory.segments[id]; + + int64_t ret; + if (globalOffset(segment.offset, ret)) { + return ret; + } + if (auto* get = segment.offset->dynCast()) { + Global* global = wasm->getGlobal(get->name); + if (globalOffset(global->init, ret)) { + return ret; + } + } + + Fatal() << "non-constant offsets aren't supported yet"; + return 0; +} +size_t BinaryenGetMemorySegmentByteLength(BinaryenModuleRef module, + BinaryenIndex id) { + if (tracing) { + std::cout << " BinaryenGetMemorySegmentByteLength(the_module, " << id + << ");\n"; + } + + auto* wasm = (Module*)module; + if (wasm->memory.segments.size() <= id) { + Fatal() << "invalid segment id."; + } + const Memory::Segment& segment = wasm->memory.segments[id]; + return segment.data.size(); +} +void BinaryenCopyMemorySegmentData(BinaryenModuleRef module, + BinaryenIndex id, + char* buffer) { + if (tracing) { + std::cout << " BinaryenCopyMemorySegmentData(the_module, " << id << ", " + << static_cast(buffer) << ");\n"; + } + + auto* wasm = (Module*)module; + if (wasm->memory.segments.size() <= id) { + Fatal() << "invalid segment id."; + } + const Memory::Segment& segment = wasm->memory.segments[id]; + std::copy(segment.data.cbegin(), segment.data.cend(), buffer); +} + // Start function. One per module void BinaryenSetStart(BinaryenModuleRef module, BinaryenFunctionRef start) { @@ -4305,6 +4402,26 @@ const char* BinaryenExportGetValue(BinaryenExportRef export_) { return ((Export*)export_)->value.c_str(); } +uint32_t BinaryenGetNumExports(BinaryenModuleRef module) { + if (tracing) { + std::cout << " BinaryenGetNumExports(the_module);\n"; + } + + auto* wasm = (Module*)module; + return wasm->exports.size(); +} +BinaryenExportRef BinaryenGetExportByIndex(BinaryenModuleRef module, + BinaryenIndex id) { + if (tracing) { + std::cout << " BinaryenGetExportByIndex(the_module, " << id << ");\n"; + } + + auto* wasm = (Module*)module; + if (wasm->exports.size() <= id) { + Fatal() << "invalid export id."; + } + return wasm->exports[id].get(); +} // // ========= Custom sections ========= diff --git a/src/binaryen-c.h b/src/binaryen-c.h index ae0a068352e..0488ececa48 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -1058,6 +1058,12 @@ BINARYEN_API BinaryenFunctionRef BinaryenGetFunction(BinaryenModuleRef module, BINARYEN_API void BinaryenRemoveFunction(BinaryenModuleRef module, const char* name); +// Gets the number of functions in the module. +BINARYEN_API uint32_t BinaryenGetNumFunctions(BinaryenModuleRef module); +// Get function pointer from its index. +BINARYEN_API BinaryenFunctionRef +BinaryenGetFunctionByIndex(BinaryenModuleRef module, BinaryenIndex id); + // Imports BINARYEN_API void @@ -1162,6 +1168,17 @@ BINARYEN_API void BinaryenSetMemory(BinaryenModuleRef module, BinaryenIndex numSegments, uint8_t shared); +// Memory segments. Query utilities. + +BINARYEN_API uint32_t BinaryenGetNumMemorySegments(BinaryenModuleRef module); +BINARYEN_API int64_t +BinaryenGetMemorySegmentByteOffset(BinaryenModuleRef module, BinaryenIndex id); +BINARYEN_API size_t BinaryenGetMemorySegmentByteLength(BinaryenModuleRef module, + BinaryenIndex id); +BINARYEN_API void BinaryenCopyMemorySegmentData(BinaryenModuleRef module, + BinaryenIndex id, + char* buffer); + // Start function. One per module BINARYEN_API void BinaryenSetStart(BinaryenModuleRef module, @@ -1431,6 +1448,11 @@ BinaryenExportGetKind(BinaryenExportRef export_); BINARYEN_API const char* BinaryenExportGetName(BinaryenExportRef export_); // Gets the internal name of the specified export. BINARYEN_API const char* BinaryenExportGetValue(BinaryenExportRef export_); +// Gets the number of exports in the module. +BINARYEN_API uint32_t BinaryenGetNumExports(BinaryenModuleRef module); +// Get export pointer from its index. +BINARYEN_API BinaryenExportRef +BinaryenGetExportByIndex(BinaryenModuleRef module, BinaryenIndex id); // // ========= Custom sections ========= diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 7c99ded15ef..0da3f2df48b 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -2096,6 +2096,23 @@ function wrapModule(module, self) { ); }); }; + self['getNumMemorySegments'] = function() { + return Module['_BinaryenGetNumMemorySegments'](module); + } + self['getMemorySegmentInfoByIndex'] = function(id) { + return { + 'byteOffset': Module['_BinaryenGetMemorySegmentByteOffset'](module, id), + 'data': (function(){ + var size = Module['_BinaryenGetMemorySegmentByteLength'](module, id); + var ptr = _malloc(size); + Module['_BinaryenCopyMemorySegmentData'](module, id, ptr); + var res = new Uint8Array(size); + res.set(new Uint8Array(buffer, ptr, size)); + _free(ptr); + return res.buffer; + })() + }; + } self['setStart'] = function(start) { return Module['_BinaryenSetStart'](module, start); }; @@ -2110,6 +2127,18 @@ function wrapModule(module, self) { return Module['_BinaryenAddCustomSection'](module, strToStack(name), i8sToStack(contents), contents.length); }); }; + self['getNumExports'] = function() { + return Module['_BinaryenGetNumExports'](module); + } + self['getExportByIndex'] = function(id) { + return Module['_BinaryenGetExportByIndex'](module, id); + } + self['getNumFunctions'] = function() { + return Module['_BinaryenGetNumFunctions'](module); + } + self['getFunctionByIndex'] = function(id) { + return Module['_BinaryenGetFunctionByIndex'](module, id); + } self['emitText'] = function() { var old = out; var ret = ''; diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js index 1d955eb6f42..54fa9abd8db 100644 --- a/test/binaryen.js/kitchen-sink.js +++ b/test/binaryen.js/kitchen-sink.js @@ -887,6 +887,60 @@ function test_internals() { console.log('sizeof Literal: ' + Binaryen['_BinaryenSizeofLiteral']()); } +function test_for_each() { + module = new Binaryen.Module(); + + var v = module.addFunctionType("v", Binaryen.None, []); + + var fns = [ + module.addFunction("fn0", v, [], module.nop()), + module.addFunction("fn1", v, [], module.nop()), + module.addFunction("fn2", v, [], module.nop()) + ]; + + var i; + for (i = 0 ; i < module.getNumFunctions() ; i++) { + assert(module.getFunctionByIndex(i) === fns[i]); + } + + var exps = [ + module.addFunctionExport("fn0", "export0"), + module.addFunctionExport("fn1", "export1"), + module.addFunctionExport("fn2", "export2") + ]; + + for (i = 0 ; i < module.getNumExports() ; i++) { + assert(module.getExportByIndex(i) === exps[i]); + } + + var expected_offsets = [10, 125]; + var expected_data = ["hello, world", "segment data 2"]; + + var global = module.addGlobal("a-global", Binaryen.i32, false, module.i32.const(expected_offsets[1])) + module.setMemory(1, 256, "mem", [ + { + passive: false, + offset: module.i32.const(expected_offsets[0]), + data: expected_data[0].split('').map(function(x) { return x.charCodeAt(0) }) + }, + { + passive: false, + offset: module.global.get("a-global"), + data: expected_data[1].split('').map(function(x) { return x.charCodeAt(0) }) + } + ], false); + for (i = 0 ; i < module.getNumMemorySegments() ; i++) { + var segment = module.getMemorySegmentInfoByIndex(i); + assert(expected_offsets[i] === segment.byteOffset); + var data8 = new Uint8Array(segment.data); + var str = String.fromCharCode.apply(null, data8); + assert(expected_data[i] === str); + } + + console.log(module.emitText()); + module.dispose(); +} + function main() { test_types(); test_features(); @@ -899,6 +953,7 @@ function main() { test_tracing(); test_parsing(); test_internals(); + test_for_each(); } main(); diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 87a6bc97c80..ea2ff9d0ce0 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -9699,3 +9699,23 @@ module loaded from text form: ) sizeof Literal: 24 +(module + (type $v (func)) + (memory $0 1 256) + (data (i32.const 10) "hello, world") + (data (global.get $a-global) "segment data 2") + (global $a-global i32 (i32.const 125)) + (export "export0" (func $fn0)) + (export "export1" (func $fn1)) + (export "export2" (func $fn2)) + (export "mem" (memory $0)) + (func $fn0 (; 0 ;) (type $v) + (nop) + ) + (func $fn1 (; 1 ;) (type $v) + (nop) + ) + (func $fn2 (; 2 ;) (type $v) + (nop) + ) +) diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 32443a85064..6848ee805a6 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -994,6 +995,59 @@ void test_color_status() { BinaryenSetColorsEnabled(old_state); } +void test_for_each() { + uint32_t i; + + BinaryenModuleRef module = BinaryenModuleCreate(); + { + BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "v", BinaryenTypeNone(), NULL, 0); + + BinaryenFunctionRef fns[3] = {0}; + fns[0] = BinaryenAddFunction(module, "fn0", v, NULL, 0, BinaryenNop(module)); + fns[1] = BinaryenAddFunction(module, "fn1", v, NULL, 0, BinaryenNop(module)); + fns[2] = BinaryenAddFunction(module, "fn2", v, NULL, 0, BinaryenNop(module)); + + for (i = 0; i < BinaryenGetNumFunctions(module) ; i++) { + assert(BinaryenGetFunctionByIndex(module, i) == fns[i]); + } + + BinaryenExportRef exps[3] = {0}; + exps[0] = BinaryenAddFunctionExport(module, "fn0", "export0"); + exps[1] = BinaryenAddFunctionExport(module, "fn1", "export1"); + exps[2] = BinaryenAddFunctionExport(module, "fn2", "export2"); + + for (i = 0; i < BinaryenGetNumExports(module) ; i++) { + assert(BinaryenGetExportByIndex(module, i) == exps[i]); + } + + BinaryenAddGlobal(module, "a-global", BinaryenTypeInt32(), 0, makeInt32(module, 125)); + + const char* segments[] = { "hello, world", "segment data 2" }; + int8_t segmentPassive[] = { 0, 0 }; + BinaryenExpressionRef segmentOffsets[] = { + BinaryenConst(module, BinaryenLiteralInt32(10)), + BinaryenGlobalGet(module, "a-global", BinaryenTypeInt32()) + }; + BinaryenIndex segmentSizes[] = { 12, 14 }; + BinaryenSetMemory(module, 1, 256, "mem", segments, segmentPassive, segmentOffsets, segmentSizes, 2, 0); + + for (i = 0; i < BinaryenGetNumMemorySegments(module) ; i++) { + char out[15] = {0}; + assert(BinaryenGetMemorySegmentByteOffset(module, i) == (0==i?10:125)); + assert(BinaryenGetMemorySegmentByteLength(module, i) == segmentSizes[i]); + BinaryenCopyMemorySegmentData(module, i, &out[0]); + if (0 == i) { + assert(0 == strcmp("hello, world", &out[0])); + } + else { + assert(0 == strcmp("segment data 2", &out[0])); + } + } + } + BinaryenModulePrint(module); + BinaryenModuleDispose(module); +} + int main() { test_types(); test_features(); @@ -1005,6 +1059,7 @@ int main() { test_nonvalid(); test_tracing(); test_color_status(); + test_for_each(); return 0; } diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 25338bc423c..6a8e50c91c5 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -6348,3 +6348,23 @@ optimized: relooperBlocks.clear(); return 0; } +(module + (type $v (func)) + (memory $0 1 256) + (data (i32.const 10) "hello, world") + (data (global.get $a-global) "segment data 2") + (global $a-global i32 (i32.const 125)) + (export "export0" (func $fn0)) + (export "export1" (func $fn1)) + (export "export2" (func $fn2)) + (export "mem" (memory $0)) + (func $fn0 (; 0 ;) (type $v) + (nop) + ) + (func $fn1 (; 1 ;) (type $v) + (nop) + ) + (func $fn2 (; 2 ;) (type $v) + (nop) + ) +)