diff options
Diffstat (limited to 'src/wasm2js.h')
-rw-r--r-- | src/wasm2js.h | 205 |
1 files changed, 150 insertions, 55 deletions
diff --git a/src/wasm2js.h b/src/wasm2js.h index 7377dad31..5cf01ea84 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -80,6 +80,18 @@ void sequenceAppend(Ref& ast, Ref extra) { ast = ValueBuilder::makeSeq(ast, extra); } +bool isTableExported(Module& wasm) { + if (!wasm.table.exists || wasm.table.imported()) { + return false; + } + for (auto& ex : wasm.exports) { + if (ex->kind == ExternalKind::Table && ex->value == wasm.table.name) { + return true; + } + } + return false; +} + IString stringToIString(std::string str) { return IString(str.c_str(), false); } // Used when taking a wasm name and generating a JS identifier. Each scope here @@ -384,13 +396,14 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) { ValueBuilder::makeDot(ValueBuilder::makeName(ENV), ValueBuilder::makeName(wasm->memory.base))); } - // for emscripten, add a table import - otherwise we would have - // FUNCTION_TABLE be an upvar, and not as easy to be minified. - if (flags.emscripten && wasm->table.exists && wasm->table.imported()) { + // add table import + if (wasm->table.exists && wasm->table.imported()) { Ref theVar = ValueBuilder::makeVar(); asmFunc[3]->push_back(theVar); ValueBuilder::appendToVar( - theVar, FUNCTION_TABLE, ValueBuilder::makeName("wasmTable")); + theVar, + FUNCTION_TABLE, + ValueBuilder::makeDot(ValueBuilder::makeName(ENV), wasm->table.base)); } // create heaps, etc addBasics(asmFunc[3]); @@ -541,31 +554,60 @@ void Wasm2JSBuilder::addGlobalImport(Ref ast, Global* import) { } void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) { + if (!wasm->table.exists) { + return; + } + + bool perElementInit = false; + // Emit a simple flat table as a JS array literal. Otherwise, // emit assignments separately for each index. - TableUtils::FlatTable flat(wasm->table); - if (flat.valid && !wasm->table.imported()) { - Ref theVar = ValueBuilder::makeVar(); - ast->push_back(theVar); - Ref theArray = ValueBuilder::makeArray(); - ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, theArray); - Name null("null"); - for (auto& name : flat.names) { - if (name.is()) { - name = fromName(name, NameScope::Top); - } else { - name = null; + Ref theArray = ValueBuilder::makeArray(); + if (!wasm->table.imported()) { + TableUtils::FlatTable flat(wasm->table); + if (flat.valid) { + Name null("null"); + for (auto& name : flat.names) { + if (name.is()) { + name = fromName(name, NameScope::Top); + } else { + name = null; + } + ValueBuilder::appendToArray(theArray, ValueBuilder::makeName(name)); } - ValueBuilder::appendToArray(theArray, ValueBuilder::makeName(name)); + } else { + perElementInit = true; + Ref initial = + ValueBuilder::makeInt(Address::address32_t(wasm->table.initial.addr)); + theArray = ValueBuilder::makeNew( + ValueBuilder::makeCall(IString("Array"), initial)); } } else { - if (!wasm->table.imported()) { - Ref theVar = ValueBuilder::makeVar(); - ast->push_back(theVar); - ValueBuilder::appendToVar( - theVar, FUNCTION_TABLE, ValueBuilder::makeArray()); + perElementInit = true; + } + + if (isTableExported(*wasm)) { + // If the table is exported use a fake WebAssembly.Table object + // We don't handle the case where a table is both imported and exported. + if (wasm->table.imported()) { + Fatal() << "wasm2js doesn't support a table that is both imported and " + "exported\n"; } + Ref theVar = ValueBuilder::makeVar(); + ast->push_back(theVar); + Ref table = + ValueBuilder::makeNew(ValueBuilder::makeCall(IString("Table"), theArray)); + ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, table); + } else if (!wasm->table.imported()) { + // Otherwise if the table is internal (neither imported not exported). + // Just use a plain array in this case, avoiding the Table. + Ref theVar = ValueBuilder::makeVar(); + ast->push_back(theVar); + ValueBuilder::appendToVar(theVar, FUNCTION_TABLE, theArray); + } + + if (perElementInit) { // TODO: optimize for size for (auto& segment : wasm->table.segments) { auto offset = segment.offset; @@ -600,36 +642,60 @@ void Wasm2JSBuilder::addStart(Ref ast, Module* wasm) { void Wasm2JSBuilder::addExports(Ref ast, Module* wasm) { Ref exports = ValueBuilder::makeObject(); for (auto& export_ : wasm->exports) { - if (export_->kind == ExternalKind::Function) { - ValueBuilder::appendToObjectWithQuotes( - exports, - fromName(export_->name, NameScope::Export), - ValueBuilder::makeName(fromName(export_->value, NameScope::Top))); - } - if (export_->kind == ExternalKind::Memory) { - Ref descs = ValueBuilder::makeObject(); - Ref growDesc = ValueBuilder::makeObject(); - ValueBuilder::appendToObjectWithQuotes(descs, IString("grow"), growDesc); - if (wasm->memory.max > wasm->memory.initial) { + switch (export_->kind) { + case ExternalKind::Function: { + ValueBuilder::appendToObjectWithQuotes( + exports, + fromName(export_->name, NameScope::Export), + ValueBuilder::makeName(fromName(export_->value, NameScope::Top))); + break; + } + case ExternalKind::Memory: { + Ref descs = ValueBuilder::makeObject(); + Ref growDesc = ValueBuilder::makeObject(); + ValueBuilder::appendToObjectWithQuotes( + descs, IString("grow"), growDesc); + if (wasm->memory.max > wasm->memory.initial) { + ValueBuilder::appendToObjectWithQuotes( + growDesc, + IString("value"), + ValueBuilder::makeName(WASM_MEMORY_GROW)); + } + Ref bufferDesc = ValueBuilder::makeObject(); + Ref bufferGetter = ValueBuilder::makeFunction(IString("")); + bufferGetter[3]->push_back( + ValueBuilder::makeReturn(ValueBuilder::makeName(BUFFER))); + ValueBuilder::appendToObjectWithQuotes( + bufferDesc, IString("get"), bufferGetter); + ValueBuilder::appendToObjectWithQuotes( + descs, IString("buffer"), bufferDesc); + Ref memory = ValueBuilder::makeCall( + ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), + IString("create")), + ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), + IString("prototype"))); + ValueBuilder::appendToCall(memory, descs); + ValueBuilder::appendToObjectWithQuotes( + exports, fromName(export_->name, NameScope::Export), memory); + break; + } + case ExternalKind::Table: { ValueBuilder::appendToObjectWithQuotes( - growDesc, IString("value"), ValueBuilder::makeName(WASM_MEMORY_GROW)); + exports, + fromName(export_->name, NameScope::Export), + ValueBuilder::makeName(FUNCTION_TABLE)); + break; } - Ref bufferDesc = ValueBuilder::makeObject(); - Ref bufferGetter = ValueBuilder::makeFunction(IString("")); - bufferGetter[3]->push_back( - ValueBuilder::makeReturn(ValueBuilder::makeName(BUFFER))); - ValueBuilder::appendToObjectWithQuotes( - bufferDesc, IString("get"), bufferGetter); - ValueBuilder::appendToObjectWithQuotes( - descs, IString("buffer"), bufferDesc); - Ref memory = ValueBuilder::makeCall( - ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), - IString("create")), - ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), - IString("prototype"))); - ValueBuilder::appendToCall(memory, descs); - ValueBuilder::appendToObjectWithQuotes( - exports, fromName(export_->name, NameScope::Export), memory); + case ExternalKind::Global: { + ValueBuilder::appendToObjectWithQuotes( + exports, + fromName(export_->name, NameScope::Export), + ValueBuilder::makeName(fromName(export_->value, NameScope::Top))); + break; + } + case ExternalKind::Event: + case ExternalKind::Invalid: + Fatal() << "unsupported export type: " << export_->name << "\n"; } } if (wasm->memory.exists) { @@ -2244,11 +2310,32 @@ void Wasm2JSGlue::emitPre() { emitPreES6(); } + if (isTableExported(wasm)) { + out << "function Table(ret) {\n"; + if (wasm.table.initial == wasm.table.max) { + out << " // grow method not included; table is not growable\n"; + } else { + out << " ret.grow = function(by) {\n" + << " var old = this.length;\n" + << " this.length = this.length + by;\n" + << " return old;\n" + << " };\n"; + } + out << " ret.set = function(i, func) {\n" + << " this[i] = func;\n" + << " };\n" + << " ret.get = function(i) {\n" + << " return this[i];\n" + << " };\n" + << " return ret;\n" + << "}\n\n"; + } + emitSpecialSupport(); } void Wasm2JSGlue::emitPreEmscripten() { - out << "function instantiate(asmLibraryArg, wasmMemory, wasmTable) {\n\n"; + out << "function instantiate(asmLibraryArg, wasmMemory) {\n"; } void Wasm2JSGlue::emitPreES6() { @@ -2272,6 +2359,8 @@ void Wasm2JSGlue::emitPreES6() { ModuleUtils::iterImportedGlobals( wasm, [&](Global* import) { noteImport(import->module, import->base); }); + ModuleUtils::iterImportedTables( + wasm, [&](Table* import) { noteImport(import->module, import->base); }); ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { // The special helpers are emitted in the glue, see code and comments // below. @@ -2281,10 +2370,6 @@ void Wasm2JSGlue::emitPreES6() { noteImport(import->module, import->base); }); - if (wasm.table.exists && wasm.table.imported()) { - out << "import { FUNCTION_TABLE } from 'env';\n"; - } - out << '\n'; } @@ -2365,6 +2450,16 @@ void Wasm2JSGlue::emitPostES6() { } out << "," << asmangle(import->base.str); }); + + ModuleUtils::iterImportedTables(wasm, [&](Table* import) { + // The special helpers are emitted in the glue, see code and comments + // below. + if (ABI::wasm2js::isHelper(import->base)) { + return; + } + out << "," << asmangle(import->base.str); + }); + out << "},mem" << moduleName.str << ");\n"; if (flags.allowAsserts) { |