diff options
Diffstat (limited to 'src/wasm2js.h')
-rw-r--r-- | src/wasm2js.h | 664 |
1 files changed, 157 insertions, 507 deletions
diff --git a/src/wasm2js.h b/src/wasm2js.h index fde436722..c6970e3e8 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -15,8 +15,8 @@ */ // -// WebAssembly-to-asm.js translator. Uses the Emscripten optimizer -// infrastructure. +// WebAssembly-to-JS code translator. Converts wasm functions into +// valid JavaScript (with a somewhat asm.js-ish flavor). // #ifndef wasm_wasm2js_h @@ -40,6 +40,7 @@ #include "ir/names.h" #include "ir/utils.h" #include "passes/passes.h" +#include "support/base64.h" namespace wasm { @@ -87,15 +88,15 @@ static uint64_t constOffset(const T& segment) { } // -// Wasm2JSBuilder - converts a WebAssembly module into asm.js +// Wasm2JSBuilder - converts a WebAssembly module's functions into JS // -// In general, asm.js => wasm is very straightforward, as can +// In general, JS (asm.js) => wasm is very straightforward, as can // be seen in asm2wasm.h. Just a single pass, plus a little // state bookkeeping (breakStack, etc.), and a few after-the -// fact corrections for imports, etc. However, wasm => asm.js +// fact corrections for imports, etc. However, wasm => JS // is tricky because wasm has statements == expressions, or in // other words, things like `break` and `if` can show up -// in places where asm.js can't handle them, like inside an +// in places where JS can't handle them, like inside an // a loop's condition check. // // We therefore need the ability to lower an expression into @@ -154,14 +155,12 @@ public: void scanFunctionBody(Expression* curr); // The second pass on an expression: process it fully, generating - // asm.js + // JS // @param result Whether the context we are in receives a value, // and its type, or if not, then we can drop our return, // if we have one. Ref processFunctionBody(Module* m, Function* func, IString result); - Ref processAsserts(Module* wasm, Element& e, SExpressionWasmBuilder& sexpBuilder); - // Get a temp var. IString getTemp(Type type, Function* func) { IString ret; @@ -272,8 +271,6 @@ private: bool almostASM = false; - void addEsmImports(Ref ast, Module* wasm); - void addEsmExportsAndInstantiate(Ref ast, Module* wasm, Name funcName); void addBasics(Ref ast); void addFunctionImport(Ref ast, Function* import); void addGlobalImport(Ref ast, Global* import); @@ -282,25 +279,7 @@ private: void addGlobal(Ref ast, Global* global); void setNeedsAlmostASM(const char *reason); void addMemoryGrowthFuncs(Ref ast); - bool isAssertHandled(Element& e); - Ref makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule); - Ref makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule); - Ref makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule); + Wasm2JSBuilder() = delete; Wasm2JSBuilder(const Wasm2JSBuilder &) = delete; Wasm2JSBuilder &operator=(const Wasm2JSBuilder&) = delete; @@ -315,7 +294,7 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { // i64-to-i32 lowering pass. runner.add("remove-non-js-ops"); // Currently the i64-to-32 lowering pass requires that `flatten` be run before - // it produce correct code. For some more details about this see #1480 + // it to produce correct code. For some more details about this see #1480 runner.add("flatten"); runner.add("i64-to-i32-lowering"); runner.add("flatten"); @@ -335,10 +314,8 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { #endif Ref ret = ValueBuilder::makeToplevel(); - addEsmImports(ret, wasm); Ref asmFunc = ValueBuilder::makeFunction(funcName); ret[1]->push_back(asmFunc); - addEsmExportsAndInstantiate(ret, wasm, funcName); ValueBuilder::appendArgumentToFunction(asmFunc, GLOBAL); ValueBuilder::appendArgumentToFunction(asmFunc, ENV); ValueBuilder::appendArgumentToFunction(asmFunc, BUFFER); @@ -409,204 +386,6 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { return ret; } -void Wasm2JSBuilder::addEsmImports(Ref ast, Module* wasm) { - std::unordered_map<Name, Name> baseModuleMap; - - auto noteImport = [&](Name module, Name base) { - // Right now codegen requires a flat namespace going into the module, - // meaning we don't support importing the same name from multiple namespaces yet. - if (baseModuleMap.count(base) && baseModuleMap[base] != module) { - Fatal() << "the name " << base << " cannot be imported from " - << "two different modules yet\n"; - abort(); - } - baseModuleMap[base] = module; - - std::ostringstream out; - out << "import { " - << base.str - << " } from '" - << module.str - << "'"; - std::string os = out.str(); - Name name(os.c_str()); - flattenAppend(ast, ValueBuilder::makeName(name)); - }; - - ImportInfo imports(*wasm); - - ModuleUtils::iterImportedGlobals(*wasm, [&](Global* import) { - Fatal() << "non-function imports aren't supported yet\n"; - noteImport(import->module, import->base); - }); - ModuleUtils::iterImportedFunctions(*wasm, [&](Function* import) { - noteImport(import->module, import->base); - }); -} - -static std::string base64Encode(std::vector<char> &data) { - std::string ret; - size_t i = 0; - - const char* alphabet = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - while (i + 3 <= data.size()) { - uint32_t bits = - (((uint32_t)(uint8_t) data[i + 0]) << 16) | - (((uint32_t)(uint8_t) data[i + 1]) << 8) | - (((uint32_t)(uint8_t) data[i + 2]) << 0); - ret += alphabet[(bits >> 18) & 0x3f]; - ret += alphabet[(bits >> 12) & 0x3f]; - ret += alphabet[(bits >> 6) & 0x3f]; - ret += alphabet[(bits >> 0) & 0x3f]; - i += 3; - } - - if (i + 2 == data.size()) { - uint32_t bits = - (((uint32_t)(uint8_t) data[i + 0]) << 8) | - (((uint32_t)(uint8_t) data[i + 1]) << 0); - ret += alphabet[(bits >> 10) & 0x3f]; - ret += alphabet[(bits >> 4) & 0x3f]; - ret += alphabet[(bits << 2) & 0x3f]; - ret += '='; - } else if (i + 1 == data.size()) { - uint32_t bits = (uint32_t)(uint8_t) data[i + 0]; - ret += alphabet[(bits >> 2) & 0x3f]; - ret += alphabet[(bits << 4) & 0x3f]; - ret += '='; - ret += '='; - } else { - assert(i == data.size()); - } - - return ret; -} - -void Wasm2JSBuilder::addEsmExportsAndInstantiate(Ref ast, Module *wasm, Name funcName) { - // Create an initial `ArrayBuffer` and populate it with static data. - // Currently we use base64 encoding to encode static data and we decode it at - // instantiation time. - // - // Note that the translation here expects that the lower values of this memory - // can be used for conversions, so make sure there's at least one page. - { - auto pages = wasm->memory.initial == 0 ? 1 : wasm->memory.initial.addr; - std::ostringstream out; - out << "const mem" << funcName.str << " = new ArrayBuffer(" - << pages * Memory::kPageSize - << ")"; - std::string os = out.str(); - IString name(os.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - } - - if (wasm->memory.segments.size() > 0) { - auto expr = R"( - function(mem) { - const _mem = new Uint8Array(mem); - return function(offset, s) { - if (typeof Buffer === 'undefined') { - const bytes = atob(s); - for (let i = 0; i < bytes.length; i++) - _mem[offset + i] = bytes.charCodeAt(i); - } else { - const bytes = Buffer.from(s, 'base64'); - for (let i = 0; i < bytes.length; i++) - _mem[offset + i] = bytes[i]; - } - } - } - )"; - - // const assign$name = ($expr)(mem$name); - std::ostringstream out; - out << "const assign" << funcName.str - << " = (" << expr << ")(mem" << funcName.str << ")"; - std::string os = out.str(); - IString name(os.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - } - for (auto& seg : wasm->memory.segments) { - assert(!seg.isPassive && "passive segments not implemented yet"); - std::ostringstream out; - out << "assign" << funcName.str << "(" - << constOffset(seg) - << ", \"" - << base64Encode(seg.data) - << "\")"; - std::string os = out.str(); - IString name(os.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - } - - // Actually invoke the `asmFunc` generated function, passing in all global - // values followed by all imports (imported via addEsmImports above) - std::ostringstream construct; - construct << "const ret" << funcName.str << " = " << funcName.str << "({" - << "Math," - << "Int8Array," - << "Uint8Array," - << "Int16Array," - << "Uint16Array," - << "Int32Array," - << "Uint32Array," - << "Float32Array," - << "Float64Array," - << "NaN," - << "Infinity" - << "}, {"; - - construct << "abort:function() { throw new Error('abort'); }"; - - ModuleUtils::iterImportedFunctions(*wasm, [&](Function* import) { - construct << "," << import->base.str; - }); - construct << "},mem" << funcName.str << ")"; - std::string sconstruct = construct.str(); - IString name(sconstruct.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - - if (flags.allowAsserts) { - return; - } - - // And now that we have our returned instance, export all our functions - // that are hanging off it. - for (auto& exp : wasm->exports) { - switch (exp->kind) { - case ExternalKind::Function: - case ExternalKind::Memory: - break; - - // Exported globals and function tables aren't supported yet - default: - continue; - } - std::ostringstream export_name; - for (auto *ptr = exp->name.str; *ptr; ptr++) { - if (*ptr == '-') { - export_name << '_'; - } else { - export_name << *ptr; - } - } - std::ostringstream out; - out << "export const " - << fromName(exp->name, NameScope::Top).str - << " = ret" - << funcName.str - << "." - << fromName(exp->name, NameScope::Top).str; - std::string os = out.str(); - IString name(os.c_str(), false); - flattenAppend(ast, ValueBuilder::makeName(name)); - } -} - void Wasm2JSBuilder::addBasics(Ref ast) { // heaps, var HEAP8 = new global.Int8Array(buffer); etc auto addHeap = [&](IString name, IString view) { @@ -2050,218 +1829,6 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, IString resul return ExpressionProcessor(this, m, func).visit(func->body, result); } -static void makeHelpers(Ref ret, Name funcName, Name moduleName, bool first) { - if (first) { - // TODO: nan and infinity shouldn't be needed once literal asm.js code isn't - // generated - flattenAppend(ret, ValueBuilder::makeName(R"( - var nan = NaN; - var infinity = Infinity; - )")); - - // When equating floating point values in spec tests we want to use bitwise - // equality like wasm does. Unfortunately though NaN makes this tricky. JS - // implementations like Spidermonkey and JSC will canonicalize NaN loads from - // `Float32Array`, but V8 will not. This means that NaN representations are - // kind of all over the place and difficult to bitwise equate. - // - // To work around this problem we just use a small shim which considers all - // NaN representations equivalent and otherwise tests for bitwise equality. - flattenAppend(ret, ValueBuilder::makeName(R"( - function f32Equal(a, b) { - var i = new Int32Array(1); - var f = new Float32Array(i.buffer); - f[0] = a; - var ai = f[0]; - f[0] = b; - var bi = f[0]; - - return (isNaN(a) && isNaN(b)) || a == b; - } - - function f64Equal(a, b) { - var i = new Int32Array(2); - var f = new Float64Array(i.buffer); - f[0] = a; - var ai1 = i[0]; - var ai2 = i[1]; - f[0] = b; - var bi1 = i[0]; - var bi2 = i[1]; - - return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2); - } - - function i64Equal(actual_lo, actual_hi, expected_lo, expected_hi) { - return actual_lo == (expected_lo | 0) && actual_hi == (expected_hi | 0); - } - )")); - } -} - -static void prefixCalls(Ref asmjs, Name asmModule) { - if (asmjs->isArray()) { - ArrayStorage& arr = asmjs->getArray(); - for (Ref& r : arr) { - prefixCalls(r, asmModule); - } - if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == CALL) { - assert(arr.size() >= 2); - if (arr[1]->getIString() == "f32Equal" || - arr[1]->getIString() == "f64Equal" || - arr[1]->getIString() == "i64Equal" || - arr[1]->getIString() == "isNaN") { - // ... - } else if (arr[1]->getIString() == "Math_fround") { - arr[1]->setString("Math.fround"); - } else { - Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(asmModule), - arr[1]->getIString()); - arr[1]->setArray(prefixed->getArray()); - } - } - } - - if (asmjs->isAssign()) { - prefixCalls(asmjs->asAssign()->target(), asmModule); - prefixCalls(asmjs->asAssign()->value(), asmModule); - } - if (asmjs->isAssignName()) { - prefixCalls(asmjs->asAssignName()->value(), asmModule); - } -} - -Ref Wasm2JSBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule) { - Expression* actual = sexpBuilder.parseExpression(e[1]); - Expression* body = nullptr; - if (e.size() == 2) { - if (actual->type == none) { - body = wasmBuilder.blockify( - actual, - wasmBuilder.makeConst(Literal(uint32_t(1))) - ); - } else { - body = actual; - } - } else if (e.size() == 3) { - Expression* expected = sexpBuilder.parseExpression(e[2]); - Type resType = expected->type; - actual->type = resType; - switch (resType) { - case i32: - body = wasmBuilder.makeBinary(EqInt32, actual, expected); - break; - - case i64: - body = wasmBuilder.makeCall( - "i64Equal", - {actual, wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32), expected}, - i32 - ); - break; - - case f32: { - body = wasmBuilder.makeCall("f32Equal", {actual, expected}, i32); - break; - } - case f64: { - body = wasmBuilder.makeCall("f64Equal", {actual, expected}, i32); - break; - } - - default: { - std::cerr << "Unhandled type in assert: " << resType << std::endl; - abort(); - } - } - } else { - assert(false && "Unexpected number of parameters in assert_return"); - } - std::unique_ptr<Function> testFunc( - wasmBuilder.makeFunction( - testFuncName, - std::vector<NameType>{}, - body->type, - std::vector<NameType>{}, - body - ) - ); - Ref jsFunc = processFunction(wasm, testFunc.get()); - prefixCalls(jsFunc, asmModule); - return jsFunc; -} - -Ref Wasm2JSBuilder::makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule) { - Expression* actual = sexpBuilder.parseExpression(e[1]); - Expression* body = wasmBuilder.makeCall("isNaN", {actual}, i32); - std::unique_ptr<Function> testFunc( - wasmBuilder.makeFunction( - testFuncName, - std::vector<NameType>{}, - body->type, - std::vector<NameType>{}, - body - ) - ); - Ref jsFunc = processFunction(wasm, testFunc.get()); - prefixCalls(jsFunc, asmModule); - return jsFunc; -} - -Ref Wasm2JSBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder, - Module* wasm, - Builder& wasmBuilder, - Element& e, - Name testFuncName, - Name asmModule) { - Name innerFuncName("f"); - Expression* expr = sexpBuilder.parseExpression(e[1]); - std::unique_ptr<Function> exprFunc( - wasmBuilder.makeFunction(innerFuncName, - std::vector<NameType>{}, - expr->type, - std::vector<NameType>{}, - expr) - ); - IString expectedErr = e[2]->str(); - Ref innerFunc = processFunction(wasm, exprFunc.get()); - prefixCalls(innerFunc, asmModule); - Ref outerFunc = ValueBuilder::makeFunction(testFuncName); - outerFunc[3]->push_back(innerFunc); - Ref tryBlock = ValueBuilder::makeBlock(); - ValueBuilder::appendToBlock(tryBlock, ValueBuilder::makeCall(innerFuncName)); - Ref catchBlock = ValueBuilder::makeBlock(); - ValueBuilder::appendToBlock( - catchBlock, - ValueBuilder::makeReturn( - ValueBuilder::makeCall( - ValueBuilder::makeDot( - ValueBuilder::makeName(IString("e")), - ValueBuilder::makeName(IString("message")), - ValueBuilder::makeName(IString("includes")) - ), - ValueBuilder::makeString(expectedErr) - ) - ) - ); - outerFunc[3]->push_back(ValueBuilder::makeTry( - tryBlock, - ValueBuilder::makeName((IString("e"))), - catchBlock)); - outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0))); - return outerFunc; -} - void Wasm2JSBuilder::setNeedsAlmostASM(const char *reason) { if (!almostASM) { almostASM = true; @@ -2420,74 +1987,157 @@ void Wasm2JSBuilder::addMemoryGrowthFuncs(Ref ast) { ast->push_back(currentMemoryFunc); } -bool Wasm2JSBuilder::isAssertHandled(Element& e) { - return e.isList() && e.size() >= 2 && e[0]->isStr() - && (e[0]->str() == Name("assert_return") || - e[0]->str() == Name("assert_return_nan") || - (flags.pedantic && e[0]->str() == Name("assert_trap"))) - && e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr() - && (*e[1])[0]->str() == Name("invoke"); +// Wasm2JSGlue emits the core of the module - the functions etc. that would +// be the asm.js function in an asm.js world. This class emits the rest of the +// "glue" around that. +class Wasm2JSGlue { +public: + Wasm2JSGlue(Module& wasm, Output& out, Wasm2JSBuilder::Flags flags, Name moduleName) : wasm(wasm), out(out), flags(flags), moduleName(moduleName) {} + + void emitPre(); + void emitPost(); + +private: + Module& wasm; + Output& out; + Wasm2JSBuilder::Flags flags; + Name moduleName; +}; + +void Wasm2JSGlue::emitPre() { + std::unordered_map<Name, Name> baseModuleMap; + + auto noteImport = [&](Name module, Name base) { + // Right now codegen requires a flat namespace going into the module, + // meaning we don't support importing the same name from multiple namespaces yet. + if (baseModuleMap.count(base) && baseModuleMap[base] != module) { + Fatal() << "the name " << base << " cannot be imported from " + << "two different modules yet\n"; + abort(); + } + baseModuleMap[base] = module; + + out << "import { " + << base.str + << " } from '" + << module.str + << "';\n"; + }; + + ImportInfo imports(wasm); + + ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { + Fatal() << "non-function imports aren't supported yet\n"; + noteImport(import->module, import->base); + }); + ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { + noteImport(import->module, import->base); + }); + + out << '\n'; } -Ref Wasm2JSBuilder::processAsserts(Module* wasm, - Element& root, - SExpressionWasmBuilder& sexpBuilder) { - Builder wasmBuilder(sexpBuilder.getAllocator()); - Ref ret = ValueBuilder::makeBlock(); - std::stringstream asmModuleS; - asmModuleS << "ret" << ASM_FUNC.c_str(); - Name asmModule(asmModuleS.str().c_str()); - makeHelpers(ret, ASM_FUNC, asmModule, true); - for (size_t i = 1; i < root.size(); ++i) { - Element& e = *root[i]; - if (e.isList() && e.size() >= 1 && e[0]->isStr() && e[0]->str() == Name("module")) { - std::stringstream funcNameS; - funcNameS << ASM_FUNC.c_str() << i; - std::stringstream moduleNameS; - moduleNameS << "ret" << ASM_FUNC.c_str() << i; - Name funcName(funcNameS.str().c_str()); - asmModule = Name(moduleNameS.str().c_str()); - Module wasm; - SExpressionWasmBuilder builder(wasm, e); - flattenAppend(ret, processWasm(&wasm, funcName)); - makeHelpers(ret, funcName, asmModule, false); - continue; - } - if (!isAssertHandled(e)) { - std::cerr << "skipping " << e << std::endl; - continue; - } - Name testFuncName(IString(("check" + std::to_string(i)).c_str(), false)); - bool isReturn = (e[0]->str() == Name("assert_return")); - bool isReturnNan = (e[0]->str() == Name("assert_return_nan")); - Element& testOp = *e[1]; - // Replace "invoke" with "call" - testOp[0]->setString(IString("call"), false, false); - // Need to claim dollared to get string as function target - testOp[1]->setString(testOp[1]->str(), /*dollared=*/true, false); - - Ref testFunc = isReturn ? - makeAssertReturnFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule) : - (isReturnNan ? - makeAssertReturnNanFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule) : - makeAssertTrapFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule)); - - flattenAppend(ret, testFunc); - std::stringstream failFuncName; - failFuncName << "fail" << std::to_string(i); - IString testName = fromName(testFuncName, NameScope::Top); - flattenAppend( - ret, - ValueBuilder::makeIf( - ValueBuilder::makeUnary(L_NOT, ValueBuilder::makeCall(testName)), - ValueBuilder::makeCall(IString(failFuncName.str().c_str(), false)), - Ref() - ) - ); +void Wasm2JSGlue::emitPost() { + // Create an initial `ArrayBuffer` and populate it with static data. + // Currently we use base64 encoding to encode static data and we decode it at + // instantiation time. + // + // Note that the translation here expects that the lower values of this memory + // can be used for conversions, so make sure there's at least one page. + { + auto pages = wasm.memory.initial == 0 ? 1 : wasm.memory.initial.addr; + out << "const mem" << moduleName.str << " = new ArrayBuffer(" + << pages * Memory::kPageSize + << ");\n"; } - return ret; -} + if (wasm.memory.segments.size() > 0) { + auto expr = R"( + function(mem) { + const _mem = new Uint8Array(mem); + return function(offset, s) { + if (typeof Buffer === 'undefined') { + const bytes = atob(s); + for (let i = 0; i < bytes.length; i++) + _mem[offset + i] = bytes.charCodeAt(i); + } else { + const bytes = Buffer.from(s, 'base64'); + for (let i = 0; i < bytes.length; i++) + _mem[offset + i] = bytes[i]; + } + } + } + )"; + + // const assign$name = ($expr)(mem$name); + out << "const assign" << moduleName.str + << " = (" << expr << ")(mem" << moduleName.str << ");\n"; + } + for (auto& seg : wasm.memory.segments) { + assert(!seg.isPassive && "passive segments not implemented yet"); + out << "assign" << moduleName.str << "(" + << constOffset(seg) + << ", \"" + << base64Encode(seg.data) + << "\");\n"; + } + + // Actually invoke the `asmFunc` generated function, passing in all global + // values followed by all imports + out << "const ret" << moduleName.str << " = " << moduleName.str << "({" + << "Math," + << "Int8Array," + << "Uint8Array," + << "Int16Array," + << "Uint16Array," + << "Int32Array," + << "Uint32Array," + << "Float32Array," + << "Float64Array," + << "NaN," + << "Infinity" + << "}, {"; + + out << "abort:function() { throw new Error('abort'); }"; + + ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { + out << "," << import->base.str; + }); + out << "},mem" << moduleName.str << ");\n"; + + if (flags.allowAsserts) { + return; + } + + // And now that we have our returned instance, export all our functions + // that are hanging off it. + for (auto& exp : wasm.exports) { + switch (exp->kind) { + case ExternalKind::Function: + case ExternalKind::Memory: + break; + + // Exported globals and function tables aren't supported yet + default: + continue; + } + std::ostringstream export_name; + for (auto *ptr = exp->name.str; *ptr; ptr++) { + if (*ptr == '-') { + export_name << '_'; + } else { + export_name << *ptr; + } + } + out << "export const " + << asmangle(exp->name.str) + << " = ret" + << moduleName.str + << "." + << asmangle(exp->name.str) + << ";\n"; + } +} } // namespace wasm |