summaryrefslogtreecommitdiff
path: root/src/wasm2js.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm2js.h')
-rw-r--r--src/wasm2js.h664
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