summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm.h12
-rw-r--r--src/asm_v_wasm.h10
-rw-r--r--src/binaryen-c.cpp1
-rw-r--r--src/passes/OptimizeInstructions.cpp23
-rw-r--r--src/passes/Print.cpp55
-rw-r--r--src/passes/RemoveUnusedBrs.cpp7
-rw-r--r--src/shared-constants.h5
-rw-r--r--src/shell-interface.h22
-rw-r--r--src/tools/wasm-shell.cpp116
-rw-r--r--src/wasm-binary.h147
-rw-r--r--src/wasm-builder.h6
-rw-r--r--src/wasm-interpreter.h36
-rw-r--r--src/wasm-s-parser.h486
-rw-r--r--src/wasm-validator.h13
-rw-r--r--src/wasm.cpp5
-rw-r--r--src/wasm.h54
16 files changed, 700 insertions, 298 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index bce67a582..1c528d7f3 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -177,6 +177,7 @@ private:
else if (type == f64) value = Literal(double(0));
else WASM_UNREACHABLE();
global->init = wasm.allocator.alloc<Const>()->set(value);
+ global->mutable_ = true;
wasm.addGlobal(global);
}
@@ -502,9 +503,20 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
type = WasmType::f64;
}
if (type != WasmType::none) {
+ // we need imported globals to be mutable, but wasm doesn't support that yet, so we must
+ // import an immutable and create a mutable global initialized to its value
+ import->name = Name(std::string(import->name.str) + "$asm2wasm$import");
import->kind = Import::Global;
import->globalType = type;
mappedGlobals.emplace(name, type);
+ {
+ auto global = new Global();
+ global->name = name;
+ global->type = type;
+ global->init = builder.makeGetGlobal(import->name, type);
+ global->mutable_ = true;
+ wasm.addGlobal(global);
+ }
} else {
import->kind = Import::Function;
}
diff --git a/src/asm_v_wasm.h b/src/asm_v_wasm.h
index 7a4a0be31..53881861c 100644
--- a/src/asm_v_wasm.h
+++ b/src/asm_v_wasm.h
@@ -54,6 +54,16 @@ std::string getSig(WasmType result, const ListType& operands) {
return ret;
}
+template<typename ListType>
+std::string getSigFromStructs(WasmType result, const ListType& operands) {
+ std::string ret;
+ ret += getSig(result);
+ for (auto operand : operands) {
+ ret += getSig(operand.type);
+ }
+ return ret;
+}
+
WasmType sigToWasmType(char sig);
FunctionType* sigToFunctionType(std::string sig);
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 3ce24bdbd..14fc4680d 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -128,6 +128,7 @@ BinaryenFunctionTypeRef BinaryenAddFunctionType(BinaryenModuleRef module, const
auto* wasm = (Module*)module;
auto* ret = new FunctionType;
if (name) ret->name = name;
+ else ret->name = Name::fromInt(wasm->functionTypes.size());
ret->result = WasmType(result);
for (BinaryenIndex i = 0; i < numParams; i++) {
ret->params.push_back(WasmType(paramTypes[i]));
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 669a19b89..48639a341 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -55,15 +55,20 @@ struct PatternDatabase {
input = strdup(
#include "OptimizeInstructions.wast.processed"
);
- SExpressionParser parser(input);
- Element& root = *parser.root;
- SExpressionWasmBuilder builder(wasm, *root[0]);
- // parse module form
- auto* func = wasm.getFunction("patterns");
- auto* body = func->body->cast<Block>();
- for (auto* item : body->list) {
- auto* pair = item->cast<Block>();
- patternMap[pair->list[0]->_id].emplace_back(pair->list[0], pair->list[1]);
+ try {
+ SExpressionParser parser(input);
+ Element& root = *parser.root;
+ SExpressionWasmBuilder builder(wasm, *root[0]);
+ // parse module form
+ auto* func = wasm.getFunction("patterns");
+ auto* body = func->body->cast<Block>();
+ for (auto* item : body->list) {
+ auto* pair = item->cast<Block>();
+ patternMap[pair->list[0]->_id].emplace_back(pair->list[0], pair->list[1]);
+ }
+ } catch (ParseException& p) {
+ p.dump(std::cerr);
+ Fatal() << "error in parsing wasm binary";
}
}
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 43ddc954c..363147b98 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -109,6 +109,9 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
o << ' ';
printName(curr->name);
}
+ if (isConcreteWasmType(curr->type)) {
+ o << ' ' << printWasmType(curr->type);
+ }
incIndent();
if (curr->list.size() > 0 && curr->list[0]->is<Block>()) {
// recurse into the first element
@@ -224,7 +227,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
printCallBody(curr);
}
void visitCallImport(CallImport *curr) {
- printOpening(o, "call_import ");
+ printOpening(o, "call ");
printCallBody(curr);
}
void visitCallIndirect(CallIndirect *curr) {
@@ -537,8 +540,8 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
printText(o, curr->base.str) << ' ';
switch (curr->kind) {
case Export::Function: if (curr->functionType) visitFunctionType(curr->functionType, &curr->name); break;
- case Export::Table: o << "(table " << curr->name << ")"; break;
- case Export::Memory: o << "(memory " << curr->name << ")"; break;
+ case Export::Table: printTableHeader(&currModule->table); break;
+ case Export::Memory: printMemoryHeader(&currModule->memory); break;
case Export::Global: o << "(global " << curr->name << ' ' << printWasmType(curr->globalType) << ")"; break;
default: WASM_UNREACHABLE();
}
@@ -560,7 +563,11 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
void visitGlobal(Global *curr) {
printOpening(o, "global ");
printName(curr->name) << ' ';
- o << printWasmType(curr->type) << ' ';
+ if (curr->mutable_) {
+ o << "(mut " << printWasmType(curr->type) << ") ";
+ } else {
+ o << printWasmType(curr->type) << ' ';
+ }
visit(curr->init);
o << ')';
}
@@ -599,11 +606,26 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
}
decIndent();
}
- void visitTable(Table *curr) {
+ void printTableHeader(Table* curr) {
printOpening(o, "table") << ' ';
o << curr->initial;
if (curr->max && curr->max != Table::kMaxSize) o << ' ' << curr->max;
- o << " anyfunc)\n";
+ o << " anyfunc)";
+ }
+ void visitTable(Table *curr) {
+ // if table wasn't imported, declare it
+ bool found = false;
+ for (auto& import : currModule->imports) {
+ if (import->kind == Import::Table) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ doIndent(o, indent);
+ printTableHeader(curr);
+ o << '\n';
+ }
doIndent(o, indent);
for (auto& segment : curr->segments) {
printOpening(o, "elem ", true);
@@ -615,11 +637,26 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
o << ')';
}
}
- void visitMemory(Memory* curr) {
+ void printMemoryHeader(Memory* curr) {
printOpening(o, "memory") << ' ';
o << curr->initial;
if (curr->max && curr->max != Memory::kMaxSize) o << ' ' << curr->max;
- o << ")\n";
+ o << ")";
+ }
+ void visitMemory(Memory* curr) {
+ // if memory wasn't imported, declare it
+ bool found = false;
+ for (auto& import : currModule->imports) {
+ if (import->kind == Import::Memory) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ doIndent(o, indent);
+ printMemoryHeader(curr);
+ o << '\n';
+ }
for (auto segment : curr->segments) {
doIndent(o, indent);
printOpening(o, "data ", true);
@@ -652,7 +689,6 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
currModule = curr;
printOpening(o, "module", true);
incIndent();
- doIndent(o, indent);
visitMemory(&curr->memory);
if (curr->start.is()) {
doIndent(o, indent);
@@ -682,7 +718,6 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
o << maybeNewLine;
}
if (curr->table.segments.size() > 0 || curr->table.initial > 0 || curr->table.max != Table::kMaxSize) {
- doIndent(o, indent);
visitTable(&curr->table);
o << maybeNewLine;
}
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index fa8ab2f6c..15482de25 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -146,11 +146,10 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs, Visitor<R
// if without an else. try to reduce if (condition) br => br_if (condition)
Break* br = curr->ifTrue->dynCast<Break>();
if (br && !br->condition) { // TODO: if there is a condition, join them
- // if the br has a value, then if => br_if means we always execute the value, and also the order is value,condition vs condition,value
if (canTurnIfIntoBrIf(curr->condition, br->value)) {
br->condition = curr->condition;
br->finalize();
- replaceCurrent(br);
+ replaceCurrent(Builder(*getModule()).dropIfConcretelyTyped(br));
anotherCycle = true;
}
}
@@ -407,18 +406,18 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs, Visitor<R
auto* ifTrueBreak = iff->ifTrue->dynCast<Break>();
if (ifTrueBreak && !ifTrueBreak->condition && canTurnIfIntoBrIf(iff->condition, ifTrueBreak->value)) {
// we are an if-else where the ifTrue is a break without a condition, so we can do this
- list[i] = ifTrueBreak;
ifTrueBreak->condition = iff->condition;
ifTrueBreak->finalize();
+ list[i] = Builder(*getModule()).dropIfConcretelyTyped(ifTrueBreak);
ExpressionManipulator::spliceIntoBlock(curr, i + 1, iff->ifFalse);
continue;
}
// otherwise, perhaps we can flip the if
auto* ifFalseBreak = iff->ifFalse->dynCast<Break>();
if (ifFalseBreak && !ifFalseBreak->condition && canTurnIfIntoBrIf(iff->condition, ifFalseBreak->value)) {
- list[i] = ifFalseBreak;
ifFalseBreak->condition = Builder(*getModule()).makeUnary(EqZInt32, iff->condition);
ifFalseBreak->finalize();
+ list[i] = Builder(*getModule()).dropIfConcretelyTyped(ifFalseBreak);
ExpressionManipulator::spliceIntoBlock(curr, i + 1, iff->ifTrue);
continue;
}
diff --git a/src/shared-constants.h b/src/shared-constants.h
index f0148cb67..923ccc7de 100644
--- a/src/shared-constants.h
+++ b/src/shared-constants.h
@@ -47,12 +47,9 @@ extern Name GROW_WASM_MEMORY,
BR,
ANYFUNC,
FAKE_RETURN,
- ASSERT_RETURN,
- ASSERT_TRAP,
- ASSERT_INVALID,
+ MUT,
SPECTEST,
PRINT,
- INVOKE,
EXIT;
} // namespace wasm
diff --git a/src/shell-interface.h b/src/shell-interface.h
index ee9ff166a..b9a90131b 100644
--- a/src/shell-interface.h
+++ b/src/shell-interface.h
@@ -111,7 +111,24 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
}
}
- void importGlobals(std::map<Name, Literal>& globals, Module& wasm) override {}
+ void importGlobals(std::map<Name, Literal>& globals, Module& wasm) override {
+ // add spectest globals
+ for (auto& import : wasm.imports) {
+ if (import->kind == Import::Global && import->module == SPECTEST && import->base == GLOBAL) {
+ switch (import->globalType) {
+ case i32: globals[import->name] = Literal(int32_t(666)); break;
+ case i64: globals[import->name] = Literal(int64_t(666)); break;
+ case f32: globals[import->name] = Literal(float(666.6)); break;
+ case f64: globals[import->name] = Literal(double(666.6)); break;
+ default: WASM_UNREACHABLE();
+ }
+ } else if (import->kind == Import::Memory && import->module == SPECTEST && import->base == MEMORY) {
+ // imported memory has initial 1 and max 2
+ wasm.memory.initial = 1;
+ wasm.memory.max = 2;
+ }
+ }
+ }
Literal callImport(Import *import, LiteralList& arguments) override {
if (import->module == SPECTEST && import->base == PRINT) {
@@ -130,7 +147,8 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
Literal callTable(Index index, LiteralList& arguments, WasmType result, ModuleInstance& instance) override {
if (index >= table.size()) trap("callTable overflow");
- auto* func = instance.wasm.getFunction(table[index]);
+ auto* func = instance.wasm.checkFunction(table[index]);
+ if (!func) trap("uninitialized table element");
if (func->params.size() != arguments.size()) trap("callIndirect: bad # of arguments");
for (size_t i = 0; i < func->params.size(); i++) {
if (func->params[i] != arguments[i].type) {
diff --git a/src/tools/wasm-shell.cpp b/src/tools/wasm-shell.cpp
index a95ced87f..670f5783d 100644
--- a/src/tools/wasm-shell.cpp
+++ b/src/tools/wasm-shell.cpp
@@ -34,26 +34,55 @@
using namespace cashew;
using namespace wasm;
+Name ASSERT_RETURN("assert_return"),
+ ASSERT_TRAP("assert_trap"),
+ ASSERT_INVALID("assert_invalid"),
+ ASSERT_MALFORMED("assert_malformed"),
+ ASSERT_UNLINKABLE("assert_unlinkable"),
+ INVOKE("invoke"),
+ GET("get");
+
+// Modules named in the file
+
+std::map<Name, std::unique_ptr<Module>> modules;
+std::map<Name, std::unique_ptr<SExpressionWasmBuilder>> builders;
+std::map<Name, std::unique_ptr<ShellExternalInterface>> interfaces;
+std::map<Name, std::unique_ptr<ModuleInstance>> instances;
+
//
-// An invocation into a module
+// An operation on a module
//
-struct Invocation {
+struct Operation {
ModuleInstance* instance;
- IString name;
+ Name operation;
+ Name name;
LiteralList arguments;
- Invocation(Element& invoke, ModuleInstance* instance, SExpressionWasmBuilder& builder) : instance(instance) {
- assert(invoke[0]->str() == INVOKE);
- name = invoke[1]->str();
- for (size_t j = 2; j < invoke.size(); j++) {
- Expression* argument = builder.parseExpression(*invoke[j]);
+ Operation(Element& element, ModuleInstance* instanceInit, SExpressionWasmBuilder& builder) : instance(instanceInit) {
+ operation = element[0]->str();
+ Index i = 1;
+ if (element.size() >= 3 && element[2]->isStr()) {
+ // module also specified
+ Name moduleName = element[i++]->str();
+ instance = instances[moduleName].get();
+ }
+ name = element[i++]->str();
+ for (size_t j = i; j < element.size(); j++) {
+ Expression* argument = builder.parseExpression(*element[j]);
arguments.push_back(argument->dynCast<Const>()->value);
}
}
- Literal invoke() {
- return instance->callExport(name, arguments);
+ Literal operate() {
+ if (operation == INVOKE) {
+ return instance->callExport(name, arguments);
+ } else if (operation == GET) {
+ return instance->getExport(name);
+ } else {
+ Fatal() << "unknown operation: " << operation << '\n';
+ WASM_UNREACHABLE();
+ }
}
};
@@ -70,15 +99,17 @@ static void verify_result(Literal a, Literal b) {
}
}
-static void run_asserts(size_t* i, bool* checked, Module* wasm,
+static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
Element* root,
- std::unique_ptr<SExpressionWasmBuilder>* builder,
+ SExpressionWasmBuilder* builder,
Name entry) {
- std::unique_ptr<ShellExternalInterface> interface;
- std::unique_ptr<ModuleInstance> instance;
+ ModuleInstance* instance = nullptr;
if (wasm) {
- interface = wasm::make_unique<ShellExternalInterface>(); // prefix make_unique to work around visual studio bugs
- instance = wasm::make_unique<ModuleInstance>(*wasm, interface.get());
+ auto tempInterface = wasm::make_unique<ShellExternalInterface>(); // prefix make_unique to work around visual studio bugs
+ auto tempInstance = wasm::make_unique<ModuleInstance>(*wasm, tempInterface.get());
+ interfaces[moduleName].swap(tempInterface);
+ instances[moduleName].swap(tempInstance);
+ instance = instances[moduleName].get();
if (entry.is()) {
Function* function = wasm->getFunction(entry);
if (!function) {
@@ -109,7 +140,7 @@ static void run_asserts(size_t* i, bool* checked, Module* wasm,
Colors::green(std::cerr);
std::cerr << " [line: " << curr.line << "]\n";
Colors::normal(std::cerr);
- if (id == ASSERT_INVALID) {
+ if (id == ASSERT_INVALID || id == ASSERT_MALFORMED || id == ASSERT_UNLINKABLE) {
// a module invalidity test
Module wasm;
bool invalid = false;
@@ -125,6 +156,33 @@ static void run_asserts(size_t* i, bool* checked, Module* wasm,
// maybe parsed ok, but otherwise incorrect
invalid = !WasmValidator().validate(wasm);
}
+ if (!invalid && id == ASSERT_UNLINKABLE) {
+ // validate "instantiating" the mdoule
+ for (auto& import : wasm.imports) {
+ if (import->module == SPECTEST && import->base == PRINT) {
+ if (import->kind != Import::Function) {
+ std::cerr << "spectest.print should be a function, but is " << import->kind << '\n';
+ invalid = true;
+ break;
+ }
+ } else {
+ std::cerr << "unknown import: " << import->module << '.' << import->base << '\n';
+ invalid = true;
+ break;
+ }
+ }
+ for (auto& segment : wasm.table.segments) {
+ for (auto name : segment.data) {
+ // spec tests consider it illegal to use spectest.print in a table
+ if (auto* import = wasm.checkImport(name)) {
+ if (import->module == SPECTEST && import->base == PRINT) {
+ std::cerr << "cannot put spectest.print in table\n";
+ invalid = true;
+ }
+ }
+ }
+ }
+ }
if (!invalid) {
Colors::red(std::cerr);
std::cerr << "[should have been invalid]\n";
@@ -134,23 +192,23 @@ static void run_asserts(size_t* i, bool* checked, Module* wasm,
}
} else if (id == INVOKE) {
assert(wasm);
- Invocation invocation(curr, instance.get(), *builder->get());
- invocation.invoke();
+ Operation operation(curr, instance, *builder);
+ operation.operate();
} else if (wasm) { // if no wasm, we skipped the module
// an invoke test
bool trapped = false;
WASM_UNUSED(trapped);
Literal result;
try {
- Invocation invocation(*curr[1], instance.get(), *builder->get());
- result = invocation.invoke();
+ Operation operation(*curr[1], instance, *builder);
+ result = operation.operate();
} catch (const TrapException&) {
trapped = true;
}
if (id == ASSERT_RETURN) {
assert(!trapped);
if (curr.size() >= 3) {
- Literal expected = builder->get()
+ Literal expected = builder
->parseExpression(*curr[2])
->dynCast<Const>()
->value;
@@ -229,14 +287,16 @@ int main(int argc, const char* argv[]) {
Colors::green(std::cerr);
std::cerr << "BUILDING MODULE [line: " << curr.line << "]\n";
Colors::normal(std::cerr);
- Module wasm;
- std::unique_ptr<SExpressionWasmBuilder> builder;
- builder = wasm::make_unique<SExpressionWasmBuilder>(wasm, *root[i]);
+ auto module = wasm::make_unique<Module>();
+ Name moduleName;
+ auto builder = wasm::make_unique<SExpressionWasmBuilder>(*module, *root[i], &moduleName);
+ builders[moduleName].swap(builder);
+ modules[moduleName].swap(module);
i++;
- assert(WasmValidator().validate(wasm));
- run_asserts(&i, &checked, &wasm, &root, &builder, entry);
+ assert(WasmValidator().validate(*modules[moduleName]));
+ run_asserts(moduleName, &i, &checked, modules[moduleName].get(), &root, builders[moduleName].get(), entry);
} else {
- run_asserts(&i, &checked, nullptr, &root, nullptr, entry);
+ run_asserts(Name(), &i, &checked, nullptr, &root, nullptr, entry);
}
}
} catch (ParseException& p) {
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 561a310b0..bbfde5b21 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -411,7 +411,6 @@ enum ASTNodes {
SetLocal = 0x15,
CallFunction = 0x16,
CallIndirect = 0x17,
- CallImport = 0x18,
TeeLocal = 0x19,
GetGlobal = 0x1a,
SetGlobal = 0x1b,
@@ -653,7 +652,7 @@ public:
if (debug) std::cerr << "write one at" << o.size() << std::endl;
size_t sizePos = writeU32LEBPlaceholder();
size_t start = o.size();
- Function* function = wasm->getFunction(i);
+ Function* function = wasm->functions[i].get();
mappedLocals.clear();
numLocalsByType.clear();
if (debug) std::cerr << "writing" << function->name << std::endl;
@@ -727,27 +726,21 @@ public:
}
finishSection(start);
}
-
- std::map<Name, uint32_t> mappedImports; // name of the Import => index
- uint32_t getImportIndex(Name name) {
- if (!mappedImports.size()) {
- // Create name => index mapping.
- for (size_t i = 0; i < wasm->imports.size(); i++) {
- assert(mappedImports.count(wasm->imports[i]->name) == 0);
- mappedImports[wasm->imports[i]->name] = i;
- }
- }
- assert(mappedImports.count(name));
- return mappedImports[name];
- }
- std::map<Name, uint32_t> mappedFunctions; // name of the Function => index
+ std::map<Name, Index> mappedFunctions; // name of the Function => index. first imports, then internals
uint32_t getFunctionIndex(Name name) {
if (!mappedFunctions.size()) {
// Create name => index mapping.
+ for (auto& import : wasm->imports) {
+ if (import->kind != Import::Function) continue;
+ assert(mappedFunctions.count(import->name) == 0);
+ auto index = mappedFunctions.size();
+ mappedFunctions[import->name] = index;
+ }
for (size_t i = 0; i < wasm->functions.size(); i++) {
assert(mappedFunctions.count(wasm->functions[i]->name) == 0);
- mappedFunctions[wasm->functions[i]->name] = i;
+ auto index = mappedFunctions.size();
+ mappedFunctions[wasm->functions[i]->name] = index;
}
}
assert(mappedFunctions.count(name));
@@ -957,7 +950,7 @@ public:
for (auto* operand : curr->operands) {
recurse(operand);
}
- o << int8_t(BinaryConsts::CallImport) << U32LEB(curr->operands.size()) << U32LEB(getImportIndex(curr->target));
+ o << int8_t(BinaryConsts::CallFunction) << U32LEB(curr->operands.size()) << U32LEB(getFunctionIndex(curr->target));
}
void visitCallIndirect(CallIndirect *curr) {
if (debug) std::cerr << "zz node: CallIndirect" << std::endl;
@@ -1300,8 +1293,13 @@ public:
else if (match(BinaryConsts::Section::FunctionSignatures)) readFunctionSignatures();
else if (match(BinaryConsts::Section::Functions)) readFunctions();
else if (match(BinaryConsts::Section::ExportTable)) readExports();
- else if (match(BinaryConsts::Section::Globals)) readGlobals();
- else if (match(BinaryConsts::Section::DataSegments)) readDataSegments();
+ else if (match(BinaryConsts::Section::Globals)) {
+ readGlobals();
+ // imports can read global imports, so we run getGlobalName and create the mapping
+ // but after we read globals, we need to add the internal globals too, so do that here
+ mappedGlobals.clear(); // wipe the mapping
+ getGlobalName(0); // force rebuild
+ } else if (match(BinaryConsts::Section::DataSegments)) readDataSegments();
else if (match(BinaryConsts::Section::FunctionTable)) readFunctionTable();
else if (match(BinaryConsts::Section::Names)) readNames();
else {
@@ -1497,10 +1495,25 @@ public:
assert(numResults == 1);
curr->result = getWasmType();
}
+ curr->name = Name::fromInt(wasm.functionTypes.size());
wasm.addFunctionType(curr);
}
}
+ std::vector<Name> functionImportIndexes; // index in function index space => name of function import
+
+ // gets a name in the combined function import+defined function space
+ Name getFunctionIndexName(Index i) {
+ if (i < functionImportIndexes.size()) {
+ auto* import = wasm.getImport(functionImportIndexes[i]);
+ assert(import->kind == Import::Function);
+ return import->name;
+ } else {
+ i -= functionImportIndexes.size();
+ return wasm.functions.at(i)->name;
+ }
+ }
+
void readImports() {
if (debug) std::cerr << "== readImports" << std::endl;
size_t num = getU32LEB();
@@ -1511,16 +1524,17 @@ public:
curr->name = Name(std::string("import$") + std::to_string(i));
curr->kind = (Import::Kind)getU32LEB();
switch (curr->kind) {
- case Export::Function: {
+ case Import::Function: {
auto index = getU32LEB();
assert(index < wasm.functionTypes.size());
- curr->functionType = wasm.getFunctionType(index);
+ curr->functionType = wasm.functionTypes[index].get();
assert(curr->functionType->name.is());
+ functionImportIndexes.push_back(curr->name);
break;
}
- case Export::Table: break;
- case Export::Memory: break;
- case Export::Global: curr->globalType = getWasmType(); break;
+ case Import::Table: break;
+ case Import::Memory: break;
+ case Import::Global: curr->globalType = getWasmType(); break;
default: WASM_UNREACHABLE();
}
curr->module = getInlineString();
@@ -1529,7 +1543,7 @@ public:
}
}
- std::vector<FunctionType*> functionTypes;
+ std::vector<FunctionType*> functionTypes; // types of defined functions
void readFunctionSignatures() {
if (debug) std::cerr << "== readFunctionSignatures" << std::endl;
@@ -1538,7 +1552,7 @@ public:
for (size_t i = 0; i < num; i++) {
if (debug) std::cerr << "read one" << std::endl;
auto index = getU32LEB();
- functionTypes.push_back(wasm.getFunctionType(index));
+ functionTypes.push_back(wasm.functionTypes[index].get());
}
}
@@ -1551,9 +1565,9 @@ public:
// We read functions before we know their names, so we need to backpatch the names later
std::vector<Function*> functions; // we store functions here before wasm.addFunction after we know their names
- std::map<size_t, std::vector<Call*>> functionCalls; // at index i we have all calls to i
+ std::map<Index, std::vector<Call*>> functionCalls; // at index i we have all calls to the defined function i
Function* currFunction = nullptr;
- size_t endOfFunction;
+ Index endOfFunction = -1; // before we see a function (like global init expressions), there is no end of function to check
void readFunctions() {
if (debug) std::cerr << "== readFunctions" << std::endl;
@@ -1611,7 +1625,7 @@ public:
}
}
- std::map<Export*, size_t> exportIndexes;
+ std::map<Export*, Index> exportIndexes;
void readExports() {
if (debug) std::cerr << "== readExports" << std::endl;
@@ -1645,6 +1659,8 @@ public:
auto curr = new Global;
curr->type = getWasmType();
curr->init = readExpression();
+ curr->mutable_ = true; // TODO
+ curr->name = Name("global$" + std::to_string(wasm.globals.size()));
wasm.addGlobal(curr);
}
}
@@ -1698,14 +1714,17 @@ public:
}
// now that we have names for each function, apply things
- if (startIndex != static_cast<Index>(-1) && startIndex < wasm.functions.size()) {
- wasm.start = wasm.functions[startIndex]->name;
+ if (startIndex != static_cast<Index>(-1)) {
+ wasm.start = getFunctionIndexName(startIndex);
}
for (auto& iter : exportIndexes) {
Export* curr = iter.first;
switch (curr->kind) {
- case Export::Function: curr->value = wasm.functions[iter.second]->name; break;
+ case Export::Function: {
+ curr->value = getFunctionIndexName(iter.second);
+ break;
+ }
case Export::Table: curr->value = Name::fromInt(0); break;
case Export::Memory: curr->value = Name::fromInt(0); break;
case Export::Global: curr->value = getGlobalName(iter.second); break;
@@ -1726,7 +1745,7 @@ public:
auto i = pair.first;
auto& indexes = pair.second;
for (auto j : indexes) {
- wasm.table.segments[i].data.push_back(wasm.functions[j]->name);
+ wasm.table.segments[i].data.push_back(getFunctionIndexName(j));
}
}
}
@@ -1795,8 +1814,7 @@ public:
case BinaryConsts::Br:
case BinaryConsts::BrIf: visitBreak((curr = allocator.alloc<Break>())->cast<Break>(), code); break; // code distinguishes br from br_if
case BinaryConsts::TableSwitch: visitSwitch((curr = allocator.alloc<Switch>())->cast<Switch>()); break;
- case BinaryConsts::CallFunction: visitCall((curr = allocator.alloc<Call>())->cast<Call>()); break;
- case BinaryConsts::CallImport: visitCallImport((curr = allocator.alloc<CallImport>())->cast<CallImport>()); break;
+ case BinaryConsts::CallFunction: curr = visitCall(); break; // we don't know if it's a call or call_import yet
case BinaryConsts::CallIndirect: visitCallIndirect((curr = allocator.alloc<CallIndirect>())->cast<CallIndirect>()); break;
case BinaryConsts::GetLocal: visitGetLocal((curr = allocator.alloc<GetLocal>())->cast<GetLocal>()); break;
case BinaryConsts::TeeLocal:
@@ -1938,44 +1956,51 @@ public:
}
curr->default_ = getBreakName(getInt32());
}
- void visitCall(Call *curr) {
- if (debug) std::cerr << "zz node: Call" << std::endl;
- auto arity = getU32LEB();
- WASM_UNUSED(arity);
- auto index = getU32LEB();
- assert(index < functionTypes.size());
- auto type = functionTypes[index];
+
+ template<typename T>
+ void fillCall(T* call, FunctionType* type, Index arity) {
+ assert(type);
auto num = type->params.size();
assert(num == arity);
- curr->operands.resize(num);
+ call->operands.resize(num);
for (size_t i = 0; i < num; i++) {
- curr->operands[num - i - 1] = popExpression();
+ call->operands[num - i - 1] = popExpression();
}
- curr->type = type->result;
- functionCalls[index].push_back(curr);
+ call->type = type->result;
}
- void visitCallImport(CallImport *curr) {
- if (debug) std::cerr << "zz node: CallImport" << std::endl;
+
+ Expression* visitCall() {
+ if (debug) std::cerr << "zz node: Call" << std::endl;
auto arity = getU32LEB();
WASM_UNUSED(arity);
- auto import = wasm.getImport(getU32LEB());
- curr->target = import->name;
- auto type = import->functionType;
- assert(type);
- auto num = type->params.size();
- assert(num == arity);
- if (debug) std::cerr << "zz node: CallImport " << curr->target << " with type " << type->name << " and " << num << " params\n";
- curr->operands.resize(num);
- for (size_t i = 0; i < num; i++) {
- curr->operands[num - i - 1] = popExpression();
+ auto index = getU32LEB();
+ FunctionType* type;
+ Expression* ret;
+ if (index < functionImportIndexes.size()) {
+ // this is a call of an imported function
+ auto* call = allocator.alloc<CallImport>();
+ auto* import = wasm.getImport(functionImportIndexes[index]);
+ call->target = import->name;
+ type = import->functionType;
+ fillCall(call, type, arity);
+ ret = call;
+ } else {
+ // this is a call of a defined function
+ auto* call = allocator.alloc<Call>();
+ auto adjustedIndex = index - functionImportIndexes.size();
+ assert(adjustedIndex < functionTypes.size());
+ type = functionTypes[adjustedIndex];
+ fillCall(call, type, arity);
+ functionCalls[adjustedIndex].push_back(call); // we don't know function names yet
+ ret = call;
}
- curr->type = type->result;
+ return ret;
}
void visitCallIndirect(CallIndirect *curr) {
if (debug) std::cerr << "zz node: CallIndirect" << std::endl;
auto arity = getU32LEB();
WASM_UNUSED(arity);
- auto* fullType = wasm.getFunctionType(getU32LEB());
+ auto* fullType = wasm.functionTypes.at(getU32LEB()).get();
curr->fullType = fullType->name;
auto num = fullType->params.size();
assert(num == arity);
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index f9b2e73f6..546d72391 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -339,6 +339,12 @@ public:
input->finalize();
return ret;
}
+
+ // Drop an expression if it has a concrete type
+ Expression* dropIfConcretelyTyped(Expression* curr) {
+ if (!isConcreteWasmType(curr->type)) return curr;
+ return makeDrop(curr);
+ }
};
} // namespace wasm
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 292d6a521..942ba1ab6 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -51,14 +51,14 @@ class Flow {
public:
Flow() {}
Flow(Literal value) : value(value) {}
- Flow(IString breakTo) : breakTo(breakTo) {}
+ Flow(Name breakTo) : breakTo(breakTo) {}
Literal value;
- IString breakTo; // if non-null, a break is going on
+ Name breakTo; // if non-null, a break is going on
bool breaking() { return breakTo.is(); }
- void clearIf(IString target) {
+ void clearIf(Name target) {
if (breakTo == target) {
breakTo.clear();
}
@@ -155,18 +155,19 @@ public:
Flow visitBreak(Break *curr) {
NOTE_ENTER("Break");
bool condition = true;
- Flow flow(curr->name);
+ Flow flow;
if (curr->value) {
flow = visit(curr->value);
if (flow.breaking()) return flow;
- flow.breakTo = curr->name;
}
if (curr->condition) {
Flow conditionFlow = visit(curr->condition);
if (conditionFlow.breaking()) return conditionFlow;
condition = conditionFlow.value.getInteger() != 0;
+ if (!condition) return flow;
}
- return condition ? flow : Flow();
+ flow.breakTo = curr->name;
+ return flow;
}
Flow visitSwitch(Switch *curr) {
NOTE_ENTER("Switch");
@@ -553,12 +554,23 @@ public:
}
}
+ // call an exported function
Literal callExport(Name name, LiteralList& arguments) {
Export *export_ = wasm.checkExport(name);
if (!export_) externalInterface->trap("callExport not found");
return callFunction(export_->value, arguments);
}
+ // get an exported global
+ Literal getExport(Name name) {
+ Export *export_ = wasm.checkExport(name);
+ if (!export_) externalInterface->trap("getExport external not found");
+ Name internalName = export_->value;
+ auto iter = globals.find(internalName);
+ if (iter == globals.end()) externalInterface->trap("getExport internal not found");
+ return iter->second;
+ }
+
std::string printFunctionStack() {
std::string ret = "/== (binaryen interpreter stack trace)\n";
for (int i = int(functionStack.size()) - 1; i >= 0; i--) {
@@ -577,7 +589,7 @@ private:
std::vector<Name> functionStack;
// Call a function, starting an invocation.
- Literal callFunction(IString name, LiteralList& arguments) {
+ Literal callFunction(Name name, LiteralList& arguments) {
// if the last call ended in a jump up the stack, it might have left stuff for us to clean up here
callDepth = 0;
functionStack.clear();
@@ -586,7 +598,7 @@ private:
public:
// Internal function call. Must be public so that callTable implementations can use it (refactor?)
- Literal callFunctionInternal(IString name, LiteralList& arguments) {
+ Literal callFunctionInternal(Name name, LiteralList& arguments) {
class FunctionScope {
public:
@@ -695,6 +707,7 @@ public:
auto name = curr->name;
NOTE_EVAL1(name);
NOTE_EVAL1(instance.globals[name]);
+ assert(instance.globals.find(name) != instance.globals.end());
return instance.globals[name];
}
Flow visitSetGlobal(SetGlobal *curr) {
@@ -744,7 +757,7 @@ public:
return Literal(int32_t(ret));
}
case HasFeature: {
- IString id = curr->nameOperand;
+ Name id = curr->nameOperand;
if (id == WASM) return Literal(1);
return Literal((int32_t)0);
}
@@ -775,7 +788,10 @@ public:
assert(!flow.breaking() || flow.breakTo == RETURN_FLOW); // cannot still be breaking, it means we missed our stop
Literal ret = flow.value;
if (function->result == none) ret = Literal();
- assert(function->result == ret.type);
+ if (function->result != ret.type) {
+ std::cerr << "calling " << function->name << " resulted in " << ret << " but the function type is " << function->result << '\n';
+ abort();
+ }
callDepth = previousCallDepth; // may decrease more than one, if we jumped up the stack
// if we jumped up the stack, we also need to pop higher frames
while (functionStack.size() > previousFunctionStackSize) {
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 9abdea744..bf2ab7739 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -62,6 +62,8 @@ class Element {
bool dollared_;
bool quoted_;
+ #define element_assert(condition) assert((condition) ? true : (std::cerr << "on: " << *this << '\n' && 0));
+
public:
Element(MixedArena& allocator) : isList_(true), list_(allocator), line(-1), col(-1) {}
@@ -80,7 +82,7 @@ public:
}
Element* operator[](unsigned i) {
- if (i >= list().size()) throw ParseException("expected more elements in list", line, col);
+ if (i >= list().size()) element_assert(0 && "expected more elements in list");
return list()[i];
}
@@ -91,12 +93,12 @@ public:
// string methods
IString str() {
- assert(!isList_);
+ element_assert(!isList_);
return str_;
}
const char* c_str() {
- assert(!isList_);
+ element_assert(!isList_);
return str_.str;
}
@@ -130,8 +132,12 @@ public:
void dump() {
std::cout << "dumping " << this << " : " << *this << ".\n";
}
+
+ #undef element_assert
};
+#define element_assert(condition, element) assert((condition) ? true : (std::cerr << "on: " << element << " at " << element.line << ":" << element.col << '\n' && 0));
+
//
// Generic S-Expression parsing into lists
//
@@ -212,6 +218,10 @@ private:
if (depth == 0) {
break;
}
+ } else if (input[0] == '\n') {
+ line++;
+ lineStart = input;
+ input++;
} else {
input++;
}
@@ -265,19 +275,27 @@ class SExpressionWasmBuilder {
Module& wasm;
MixedArena& allocator;
std::vector<Name> functionNames;
+ std::vector<Name> functionTypeNames;
+ std::vector<Name> globalNames;
int functionCounter;
- int importCounter;
int globalCounter;
std::map<Name, WasmType> functionTypes; // we need to know function return types before we parse their contents
public:
// Assumes control of and modifies the input.
- SExpressionWasmBuilder(Module& wasm, Element& module) : wasm(wasm), allocator(wasm.allocator), importCounter(0), globalCounter(0) {
+ SExpressionWasmBuilder(Module& wasm, Element& module, Name* moduleName = nullptr) : wasm(wasm), allocator(wasm.allocator), globalCounter(0) {
assert(module[0]->str() == MODULE);
- if (module.size() > 1 && module[1]->isStr()) {
+ if (module.size() == 1) return;
+ Index i = 1;
+ if (module[i]->dollared()) {
+ if (moduleName) {
+ *moduleName = module[i]->str();
+ }
+ i++;
+ }
+ if (i < module.size() && module[i]->isStr()) {
// these s-expressions contain a binary module, actually
std::vector<char> data;
- size_t i = 1;
while (i < module.size()) {
auto str = module[i++]->c_str();
if (auto size = strlen(str)) {
@@ -288,14 +306,19 @@ public:
binaryBuilder.read();
return;
}
+ Index implementedFunctions = 0;
functionCounter = 0;
- for (unsigned i = 1; i < module.size(); i++) {
- preParseFunctionType(*module[i]);
- preParseImports(*module[i]);
+ for (unsigned j = i; j < module.size(); j++) {
+ auto& s = *module[j];
+ preParseFunctionType(s);
+ preParseImports(s);
+ if (s[0]->str() == FUNC && !isImport(s)) {
+ implementedFunctions++;
+ }
}
- functionCounter = 0;
- for (unsigned i = 1; i < module.size(); i++) {
- parseModuleElement(*module[i]);
+ functionCounter -= implementedFunctions; // we go through the functions again, now parsing them, and the counter begins from where imports ended
+ for (unsigned j = i; j < module.size(); j++) {
+ parseModuleElement(*module[j]);
}
}
@@ -325,8 +348,8 @@ private:
if (curr.size() > 2) throw ParseException("invalid result arity", curr.line, curr.col);
functionTypes[name] = stringToWasmType(curr[1]->str());
} else if (id == TYPE) {
- Name typeName = curr[1]->str();
- if (!wasm.checkFunctionType(typeName)) throw ParseException("unknown function", curr.line, curr.col);
+ Name typeName = getFunctionTypeName(*curr[1]);
+ if (!wasm.checkFunctionType(typeName)) throw ParseException("unknown function type", curr.line, curr.col);
type = wasm.getFunctionType(typeName);
functionTypes[name] = type->result;
} else if (id == PARAM && curr.size() > 1) {
@@ -356,17 +379,35 @@ private:
}
}
if (need) {
+ functionType->name = Name::fromInt(wasm.functionTypes.size());
+ functionTypeNames.push_back(functionType->name);
wasm.addFunctionType(functionType.release());
}
}
}
+ bool isImport(Element& curr) {
+ for (Index i = 0; i < curr.size(); i++) {
+ auto& x = *curr[i];
+ if (x.isList() && x.size() > 0 && x[0]->isStr() && x[0]->str() == IMPORT) return true;
+ }
+ return false;
+ }
+
void preParseImports(Element& curr) {
IString id = curr[0]->str();
if (id == IMPORT) parseImport(curr);
+ if (isImport(curr)) {
+ if (id == FUNC) parseFunction(curr, true /* preParseImport */);
+ else if (id == GLOBAL) parseGlobal(curr, true /* preParseImport */);
+ else if (id == TABLE) parseTable(curr, true /* preParseImport */);
+ else if (id == MEMORY) parseMemory(curr, true /* preParseImport */);
+ else throw ParseException("fancy import we don't support yet", curr.line, curr.col);
+ }
}
void parseModuleElement(Element& curr) {
+ if (isImport(curr)) return; // already done
IString id = curr[0]->str();
if (id == START) return parseStart(curr);
if (id == FUNC) return parseFunction(curr);
@@ -404,11 +445,33 @@ private:
} else {
// index
size_t offset = atoi(s.str().c_str());
- if (offset >= functionNames.size()) throw ParseException("unknown function");
+ if (offset >= functionNames.size()) throw ParseException("unknown function in getFunctionName");
return functionNames[offset];
}
}
+ Name getFunctionTypeName(Element& s) {
+ if (s.dollared()) {
+ return s.str();
+ } else {
+ // index
+ size_t offset = atoi(s.str().c_str());
+ if (offset >= functionTypeNames.size()) throw ParseException("unknown function type in getFunctionTypeName");
+ return functionTypeNames[offset];
+ }
+ }
+
+ Name getGlobalName(Element& s) {
+ if (s.dollared()) {
+ return s.str();
+ } else {
+ // index
+ size_t offset = atoi(s.str().c_str());
+ if (offset >= globalNames.size()) throw ParseException("unknown global in getGlobalName");
+ return globalNames[offset];
+ }
+ }
+
void parseStart(Element& s) {
wasm.addStart(getFunctionName(*s[1]));
}
@@ -435,25 +498,39 @@ private:
i++;
}
}
+#if 0
+ if (exportName.is() && !name.is()) {
+ name = exportName; // useful for debugging
+ }
+#endif
return i;
}
- void parseFunction(Element& s) {
+ void parseFunction(Element& s, bool preParseImport = false) {
size_t i = 1;
Name name, exportName;
i = parseFunctionNames(s, name, exportName);
- if (!name.is()) {
- // unnamed, use an index
- name = Name::fromInt(functionCounter);
+ if (!preParseImport) {
+ if (!name.is()) {
+ // unnamed, use an index
+ name = Name::fromInt(functionCounter);
+ }
+ functionCounter++;
+ } else {
+ // just preparsing, functionCounter was incremented by preParseFunctionType
+ if (!name.is()) {
+ // unnamed, use an index
+ name = functionNames[functionCounter - 1];
+ }
}
if (exportName.is()) {
auto ex = make_unique<Export>();
ex->name = exportName;
ex->value = name;
ex->kind = Export::Function;
+ if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col);
wasm.addExport(ex.release());
}
- functionCounter++;
Expression* body = nullptr;
localIndex = 0;
otherIndex = 0;
@@ -464,6 +541,7 @@ private:
WasmType result = none;
Name type;
Block* autoBlock = nullptr; // we may need to add a block for the very top level
+ Name importModule, importBase;
auto makeFunction = [&]() {
currFunction = std::unique_ptr<Function>(Builder(wasm).makeFunction(
name,
@@ -511,9 +589,9 @@ private:
if (curr.size() > 2) throw ParseException("invalid result arity", curr.line, curr.col);
result = stringToWasmType(curr[1]->str());
} else if (id == TYPE) {
- Name name = curr[1]->str();
+ Name name = getFunctionTypeName(*curr[1]);
type = name;
- if (!wasm.checkFunctionType(name)) throw ParseException("unknown function");
+ if (!wasm.checkFunctionType(name)) throw ParseException("unknown function type");
FunctionType* type = wasm.getFunctionType(name);
result = type->result;
for (size_t j = 0; j < type->params.size(); j++) {
@@ -522,6 +600,9 @@ private:
typeParams.emplace_back(name, currType);
currLocalTypes[name] = currType;
}
+ } else if (id == IMPORT) {
+ importModule = curr[1]->str();
+ importBase = curr[2]->str();
} else {
// body
if (typeParams.size() > 0 && params.size() == 0) {
@@ -537,6 +618,34 @@ private:
}
}
}
+ // see https://github.com/WebAssembly/spec/pull/301
+ if (type.isNull()) {
+ // if no function type name provided, then we generated one
+ std::unique_ptr<FunctionType> functionType = std::unique_ptr<FunctionType>(sigToFunctionType(getSigFromStructs(result, params)));
+ for (auto& existing : wasm.functionTypes) {
+ if (existing->structuralComparison(*functionType)) {
+ type = existing->name;
+ break;
+ }
+ }
+ if (!type.is()) throw ParseException("no function type [internal error?]", s.line, s.col);
+ }
+ if (importModule.is()) {
+ // this is an import, actually
+ assert(preParseImport);
+ std::unique_ptr<Import> im = make_unique<Import>();
+ im->name = name;
+ im->module = importModule;
+ im->base = importBase;
+ im->kind = Import::Function;
+ im->functionType = wasm.getFunctionType(type);
+ wasm.addImport(im.release());
+ assert(!currFunction);
+ currLocalTypes.clear();
+ labelStack.clear();
+ return;
+ }
+ assert(!preParseImport);
if (brokeToAutoBlock) {
ensureAutoBlock();
autoBlock->name = FAKE_RETURN;
@@ -549,21 +658,8 @@ private:
body = allocator.alloc<Nop>();
}
if (currFunction->result != result) throw ParseException("bad func declaration", s.line, s.col);
- // see https://github.com/WebAssembly/spec/pull/301
- if (type.isNull()) {
- // if no function type name provided, then we generated one
- std::unique_ptr<FunctionType> functionType = std::unique_ptr<FunctionType>(sigToFunctionType(getSig(currFunction.get())));
- for (auto& existing : wasm.functionTypes) {
- if (existing->structuralComparison(*functionType)) {
- type = existing->name;
- break;
- }
- }
- if (!type.is()) throw ParseException("no function type [internal error?]", s.line, s.col);
- }
currFunction->body = body;
currFunction->type = type;
-
wasm.addFunction(currFunction.release());
currLocalTypes.clear();
labelStack.clear();
@@ -586,6 +682,10 @@ private:
abort();
}
+ bool isWasmType(IString str) {
+ return stringToWasmType(str, true) != none;
+ }
+
public:
Expression* parseExpression(Element* s) {
return parseExpression(*s);
@@ -594,7 +694,7 @@ public:
#define abort_on(str) { throw ParseException(std::string("abort_on ") + str); }
Expression* parseExpression(Element& s) {
- if (!s.isList()) throw ParseException("invalid node for parseExpression, needed list", s.line, s.col);
+ element_assert(s.isList(), s);
IString id = s[0]->str();
const char *str = id.str;
const char *dot = strchr(str, '.');
@@ -954,7 +1054,7 @@ private:
Expression* makeGetGlobal(Element& s) {
auto ret = allocator.alloc<GetGlobal>();
- ret->name = s[1]->str();
+ ret->name = getGlobalName(*s[1]);
auto* global = wasm.checkGlobal(ret->name);
if (global) {
ret->type = global->type;
@@ -970,7 +1070,8 @@ private:
Expression* makeSetGlobal(Element& s) {
auto ret = allocator.alloc<SetGlobal>();
- ret->name = s[1]->str();
+ ret->name = getGlobalName(*s[1]);
+ if (wasm.checkGlobal(ret->name) && !wasm.checkGlobal(ret->name)->mutable_) throw ParseException("set_global of immutable", s.line, s.col);
ret->value = parseExpression(s[2]);
return ret;
}
@@ -985,13 +1086,23 @@ private:
auto& s = *sp;
size_t i = 1;
if (i < s.size() && s[i]->isStr()) {
- curr->name = s[i]->str();
- i++;
+ // could be a name or a type
+ if (s[i]->dollared() || stringToWasmType(s[i]->str(), true /* allowError */) == none) {
+ curr->name = s[i]->str();
+ i++;
+ } else {
+ curr->name = getPrefixedName("block");
+ }
} else {
curr->name = getPrefixedName("block");
}
labelStack.push_back(curr->name);
- if (i >= s.size()) break; // labeled empty block
+ if (i >= s.size()) break; // empty block
+ if (s[i]->isStr()) {
+ // block signature
+ i++; // TODO: parse the signature
+ if (i >= s.size()) break; // empty block
+ }
auto& first = *s[i];
if (first[0]->str() == BLOCK) {
// recurse
@@ -1008,7 +1119,7 @@ private:
auto& s = *sp;
size_t i = 1;
if (i < s.size()) {
- if (s[i]->isStr()) {
+ while (i < s.size() && s[i]->isStr()) {
i++;
}
if (t < int(stack.size()) - 1) {
@@ -1126,40 +1237,33 @@ private:
Expression* makeIf(Element& s) {
auto ret = allocator.alloc<If>();
- ret->condition = parseExpression(s[1]);
-
- // ifTrue and ifFalse may get implicit blocks
- auto handle = [&](const char* title, Element& s) {
- Name name = getPrefixedName(title);
- bool explicitThenElse = false;
- if (s[0]->str() == THEN || s[0]->str() == ELSE) {
- explicitThenElse = true;
- if (s[1]->isStr() && s[1]->dollared()) {
- name = s[1]->str();
- }
- }
- labelStack.push_back(name);
- auto* ret = parseExpression(&s);
- labelStack.pop_back();
- if (explicitThenElse) {
- ret->dynCast<Block>()->name = name;
- } else {
- // add a block if we must
- if (BreakSeeker::has(ret, name)) {
- auto* block = allocator.alloc<Block>();
- block->name = name;
- block->list.push_back(ret);
- block->finalize();
- ret = block;
- }
- }
- return ret;
- };
-
- ret->ifTrue = handle("if-true", *s[2]);
- if (s.size() == 4) {
- ret->ifFalse = handle("if-else", *s[3]);
- ret->finalize();
+ Index i = 1;
+ Name label;
+ if (s[i]->dollared()) {
+ // the if is labeled
+ label = s[i++]->str();
+ } else {
+ label = getPrefixedName("if");
+ }
+ labelStack.push_back(label);
+ if (s[i]->isStr()) {
+ // if type, TODO: parse?
+ i++;
+ }
+ ret->condition = parseExpression(s[i++]);
+ ret->ifTrue = parseExpression(*s[i++]);
+ if (i < s.size()) {
+ ret->ifFalse = parseExpression(*s[i++]);
+ }
+ ret->finalize();
+ labelStack.pop_back();
+ // create a break target if we must
+ if (BreakSeeker::has(ret, label)) {
+ auto* block = allocator.alloc<Block>();
+ block->name = label;
+ block->list.push_back(ret);
+ block->finalize();
+ return block;
}
return ret;
}
@@ -1182,16 +1286,20 @@ private:
auto ret = allocator.alloc<Loop>();
size_t i = 1;
Name out;
- if (s.size() > i + 1 && s[i]->isStr() && s[i + 1]->isStr()) { // out can only be named if both are
+ if (s.size() > i + 1 && s[i]->dollared() && s[i + 1]->dollared()) { // out can only be named if both are
out = s[i]->str();
i++;
}
- if (s.size() > i && s[i]->isStr()) {
+ if (s.size() > i && s[i]->dollared()) {
ret->name = s[i]->str();
i++;
} else {
ret->name = getPrefixedName("loop-in");
}
+ if (i < s.size() && s[i]->isStr()) {
+ // block signature
+ i++; // TODO: parse the signature
+ }
labelStack.push_back(ret->name);
ret->body = makeMaybeBlock(s, i);
labelStack.pop_back();
@@ -1207,8 +1315,18 @@ private:
}
Expression* makeCall(Element& s) {
+ auto target = getFunctionName(*s[1]);
+ auto* import = wasm.checkImport(target);
+ if (import && import->kind == Import::Function) {
+ auto ret = allocator.alloc<CallImport>();
+ ret->target = target;
+ Import* import = wasm.getImport(ret->target);
+ ret->type = import->functionType->result;
+ parseCallOperands(s, 2, s.size(), ret);
+ return ret;
+ }
auto ret = allocator.alloc<Call>();
- ret->target = s[1]->str();
+ ret->target = target;
ret->type = functionTypes[ret->target];
parseCallOperands(s, 2, s.size(), ret);
return ret;
@@ -1224,6 +1342,7 @@ private:
}
Expression* makeCallIndirect(Element& s) {
+ if (!seenTable) throw ParseException("no table");
auto ret = allocator.alloc<CallIndirect>();
IString type = s[1]->str();
auto* fullType = wasm.checkFunctionType(type);
@@ -1346,12 +1465,14 @@ private:
bool hasMemory = false;
- void parseMemory(Element& s) {
+ void parseMemory(Element& s, bool preParseImport = false) {
+ if (hasMemory) throw ParseException("too many memories");
hasMemory = true;
Index i = 1;
if (s[i]->dollared()) {
wasm.memory.name = s[i++]->str();
}
+ Name importModule, importBase;
if (s[i]->isList()) {
auto& inner = *s[i];
if (inner[0]->str() == EXPORT) {
@@ -1359,12 +1480,17 @@ private:
ex->name = inner[1]->str();
ex->value = wasm.memory.name;
ex->kind = Export::Memory;
+ if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col);
wasm.addExport(ex.release());
i++;
+ } else if (inner[0]->str() == IMPORT) {
+ importModule = inner[1]->str();
+ importBase = inner[2]->str();
+ i++;
} else {
assert(inner.size() > 0 ? inner[0]->str() != IMPORT : true);
// (memory (data ..)) format
- parseData(*s[i]);
+ parseInnerData(*s[i]);
wasm.memory.initial = wasm.memory.segments[0].data.size();
return;
}
@@ -1404,13 +1530,15 @@ private:
void parseData(Element& s) {
if (!hasMemory) throw ParseException("data but no memory");
Index i = 1;
- Expression* offset;
- if (i < s.size() && s[i]->isList()) {
- // there is an init expression
- offset = parseExpression(s[i++]);
- } else {
- offset = allocator.alloc<Const>()->set(Literal(int32_t(0)));
+ if (!s[i]->isList()) {
+ // the memory is named
+ i++;
}
+ auto* offset = parseExpression(s[i++]);
+ parseInnerData(s, i, offset);
+ }
+
+ void parseInnerData(Element& s, Index i = 1, Expression* offset = nullptr) {
std::vector<char> data;
while (i < s.size()) {
const char *input = s[i++]->c_str();
@@ -1418,6 +1546,9 @@ private:
stringToBinary(input, size, data);
}
}
+ if (!offset) {
+ offset = allocator.alloc<Const>()->set(Literal(int32_t(0)));
+ }
wasm.memory.segments.emplace_back(offset, data.data(), data.size());
}
@@ -1426,33 +1557,29 @@ private:
ex->name = s[1]->str();
if (s[2]->isList()) {
auto& inner = *s[2];
+ ex->value = inner[1]->str();
if (inner[0]->str() == FUNC) {
- ex->value = inner[1]->str();
ex->kind = Export::Function;
} else if (inner[0]->str() == MEMORY) {
if (!hasMemory) throw ParseException("memory exported but no memory");
- ex->value = Name::fromInt(0);
ex->kind = Export::Memory;
} else if (inner[0]->str() == TABLE) {
- ex->value = Name::fromInt(0);
ex->kind = Export::Table;
} else if (inner[0]->str() == GLOBAL) {
- ex->value = inner[1]->str();
- ex->kind = Export::Table;
+ ex->kind = Export::Global;
+ if (wasm.checkGlobal(ex->value) && wasm.getGlobal(ex->value)->mutable_) throw ParseException("cannot export a mutable global", s.line, s.col);
} else {
WASM_UNREACHABLE();
}
} else if (!s[2]->dollared() && !std::isdigit(s[2]->str()[0])) {
+ ex->value = s[3]->str();
if (s[2]->str() == MEMORY) {
if (!hasMemory) throw ParseException("memory exported but no memory");
- ex->value = Name::fromInt(0);
ex->kind = Export::Memory;
} else if (s[2]->str() == TABLE) {
- ex->value = Name::fromInt(0);
ex->kind = Export::Table;
} else if (s[2]->str() == GLOBAL) {
- ex->value = s[3]->str();
- ex->kind = Export::Table;
+ ex->kind = Export::Global;
} else {
WASM_UNREACHABLE();
}
@@ -1461,6 +1588,7 @@ private:
ex->value = s[2]->str();
ex->kind = Export::Function;
}
+ if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col);
wasm.addExport(ex.release());
}
@@ -1473,22 +1601,39 @@ private:
im->kind = Import::Function;
} else if ((*s[3])[0]->str() == MEMORY) {
im->kind = Import::Memory;
+ if (hasMemory) throw ParseException("too many memories");
+ hasMemory = true;
} else if ((*s[3])[0]->str() == TABLE) {
im->kind = Import::Table;
+ if (seenTable) throw ParseException("more than one table");
+ seenTable = true;
} else if ((*s[3])[0]->str() == GLOBAL) {
im->kind = Import::Global;
} else {
newStyle = false; // either (param..) or (result..)
}
}
+ Index newStyleInner = 1;
if (s.size() > 3 && s[3]->isStr()) {
im->name = s[i++]->str();
- } else if (newStyle) {
- im->name = (*s[3])[1]->str();
- } else {
- im->name = Name::fromInt(importCounter);
+ } else if (newStyle && newStyleInner < s[3]->size() && (*s[3])[newStyleInner]->dollared()) {
+ im->name = (*s[3])[newStyleInner++]->str();
+ }
+ if (!im->name.is()) {
+ if (im->kind == Import::Function) {
+ im->name = Name("import$function$" + std::to_string(functionCounter++));
+ functionNames.push_back(im->name);
+ } else if (im->kind == Import::Global) {
+ im->name = Name("import$global" + std::to_string(globalCounter++));
+ globalNames.push_back(im->name);
+ } else if (im->kind == Import::Memory) {
+ im->name = Name("import$memory$" + std::to_string(0));
+ } else if (im->kind == Import::Table) {
+ im->name = Name("import$table$" + std::to_string(0));
+ } else {
+ WASM_UNREACHABLE();
+ }
}
- importCounter++;
if (!s[i]->quoted()) {
if (s[i]->str() == MEMORY) {
im->kind = Import::Memory;
@@ -1508,7 +1653,7 @@ private:
im->base = s[i++]->str();
// parse internals
Element& inner = newStyle ? *s[3] : s;
- Index j = newStyle ? 2 : i;
+ Index j = newStyle ? newStyleInner : i;
if (im->kind == Import::Function) {
std::unique_ptr<FunctionType> type = make_unique<FunctionType>();
if (inner.size() > j) {
@@ -1535,51 +1680,115 @@ private:
}
im->functionType = ensureFunctionType(getSig(type.get()), &wasm);
} else if (im->kind == Import::Global) {
- im->globalType = stringToWasmType(inner[j]->str());
+ if (inner[j]->isStr()) {
+ im->globalType = stringToWasmType(inner[j]->str());
+ } else {
+ auto& inner2 = *inner[j];
+ assert(inner2[0]->str() == MUT);
+ im->globalType = stringToWasmType(inner2[1]->str());
+ throw ParseException("cannot import a mutable global", s.line, s.col);
+ }
+ } else if (im->kind == Import::Table) {
+ if (j < inner.size() - 1) {
+ wasm.table.initial = atoi(inner[j++]->c_str());
+ }
+ if (j < inner.size() - 1) {
+ wasm.table.max = atoi(inner[j++]->c_str());
+ } else {
+ wasm.table.max = wasm.table.initial;
+ }
+ // ends with the table element type
+ } else if (im->kind == Import::Memory) {
+ if (j < inner.size()) {
+ wasm.memory.initial = atoi(inner[j++]->c_str());
+ }
+ if (j < inner.size()) {
+ wasm.memory.max = atoi(inner[j++]->c_str());
+ } else {
+ wasm.memory.max = wasm.memory.initial;
+ }
}
wasm.addImport(im.release());
}
- void parseGlobal(Element& s) {
+ void parseGlobal(Element& s, bool preParseImport = false) {
std::unique_ptr<Global> global = make_unique<Global>();
size_t i = 1;
- if (s[i]->dollared()) {
+ if (s[i]->dollared() && !(s[i]->isStr() && isWasmType(s[i]->str()))) {
global->name = s[i++]->str();
} else {
global->name = Name::fromInt(globalCounter);
}
globalCounter++;
- if (s[i]->isList()) {
+ globalNames.push_back(global->name);
+ bool mutable_ = false;
+ WasmType type = none;
+ bool exported = false;
+ Name importModule, importBase;
+ while (i < s.size() && s[i]->isList()) {
auto& inner = *s[i];
if (inner[0]->str() == EXPORT) {
auto ex = make_unique<Export>();
ex->name = inner[1]->str();
ex->value = global->name;
ex->kind = Export::Global;
+ if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col);
wasm.addExport(ex.release());
+ exported = true;
+ i++;
+ } else if (inner[0]->str() == IMPORT) {
+ importModule = inner[1]->str();
+ importBase = inner[2]->str();
+ i++;
+ } else if (inner[0]->str() == MUT) {
+ mutable_ = true;
+ type = stringToWasmType(inner[1]->str());
i++;
} else {
- WASM_UNREACHABLE();
+ break;
}
}
- global->type = stringToWasmType(s[i++]->str());
- global->init = parseExpression(s[i++]);
+ if (exported && mutable_) throw ParseException("cannot export a mutable global", s.line, s.col);
+ if (type == none) {
+ type = stringToWasmType(s[i++]->str());
+ }
+ if (importModule.is()) {
+ // this is an import, actually
+ assert(preParseImport);
+ if (mutable_) throw ParseException("cannot import a mutable global", s.line, s.col);
+ std::unique_ptr<Import> im = make_unique<Import>();
+ im->name = global->name;
+ im->module = importModule;
+ im->base = importBase;
+ im->kind = Import::Global;
+ im->globalType = type;
+ wasm.addImport(im.release());
+ return;
+ }
+ assert(!preParseImport);
+ global->type = type;
+ if (i < s.size()) {
+ global->init = parseExpression(s[i++]);
+ } else {
+ throw ParseException("global without init", s.line, s.col);
+ }
+ global->mutable_ = mutable_;
assert(i == s.size());
wasm.addGlobal(global.release());
}
bool seenTable = false;
- void parseTable(Element& s) {
+ void parseTable(Element& s, bool preParseImport = false) {
+ if (seenTable) throw ParseException("more than one table");
seenTable = true;
Index i = 1;
if (i == s.size()) return; // empty table in old notation
-#if 0 // TODO: new table notation
if (s[i]->dollared()) {
wasm.table.name = s[i++]->str();
}
-#endif
if (i == s.size()) return;
+ Name importModule, importBase;
if (s[i]->isList()) {
auto& inner = *s[i];
if (inner[0]->str() == EXPORT) {
@@ -1587,8 +1796,13 @@ private:
ex->name = inner[1]->str();
ex->value = wasm.table.name;
ex->kind = Export::Table;
+ if (wasm.checkExport(ex->name)) throw ParseException("duplicate export", s.line, s.col);
wasm.addExport(ex.release());
i++;
+ } else if (inner[0]->str() == IMPORT) {
+ importModule = inner[1]->str();
+ importBase = inner[2]->str();
+ i++;
} else {
WASM_UNREACHABLE();
}
@@ -1597,30 +1811,48 @@ private:
if (!s[i]->dollared()) {
if (s[i]->str() == ANYFUNC) {
// (table type (elem ..))
- parseElem(*s[i + 1]);
- wasm.table.initial = wasm.table.max = wasm.table.segments[0].data.size();
+ parseInnerElem(*s[i + 1]);
+ if (wasm.table.segments.size() > 0) {
+ wasm.table.initial = wasm.table.max = wasm.table.segments[0].data.size();
+ } else {
+ wasm.table.initial = wasm.table.max = 0;
+ }
return;
}
// first element isn't dollared, and isn't anyfunc. this could be old syntax for (table 0 1) which means function 0 and 1, or it could be (table initial max? type), look for type
if (s[s.size() - 1]->str() == ANYFUNC) {
// (table initial max? type)
- wasm.table.initial = atoi(s[i]->c_str());
- wasm.table.max = atoi(s[i + 1]->c_str());
+ if (i < s.size() - 1) {
+ wasm.table.initial = atoi(s[i++]->c_str());
+ }
+ if (i < s.size() - 1) {
+ wasm.table.max = atoi(s[i++]->c_str());
+ }
return;
}
}
// old notation (table func1 func2 ..)
- parseElem(s, i);
- wasm.table.initial = wasm.table.max = wasm.table.segments[0].data.size();
+ parseInnerElem(s, i);
+ if (wasm.table.segments.size() > 0) {
+ wasm.table.initial = wasm.table.max = wasm.table.segments[0].data.size();
+ } else {
+ wasm.table.initial = wasm.table.max = 0;
+ }
}
- void parseElem(Element& s, Index i = 1) {
+ void parseElem(Element& s) {
+ Index i = 1;
+ if (!s[i]->isList()) {
+ // the table is named
+ i++;
+ }
+ auto* offset = parseExpression(s[i++]);
+ parseInnerElem(s, i, offset);
+ }
+
+ void parseInnerElem(Element& s, Index i = 1, Expression* offset = nullptr) {
if (!seenTable) throw ParseException("elem without table", s.line, s.col);
- Expression* offset;
- if (s[i]->isList()) {
- // there is an init expression
- offset = parseExpression(s[i++]);
- } else {
+ if (!offset) {
offset = allocator.alloc<Const>()->set(Literal(int32_t(0)));
}
Table::Segment segment(offset);
@@ -1650,6 +1882,10 @@ private:
type->result = stringToWasmType(curr[1]->str());
}
}
+ if (!type->name.is()) {
+ type->name = Name::fromInt(wasm.functionTypes.size());
+ }
+ functionTypeNames.push_back(type->name);
wasm.addFunctionType(type.release());
}
};
diff --git a/src/wasm-validator.h b/src/wasm-validator.h
index e23221337..988d1104d 100644
--- a/src/wasm-validator.h
+++ b/src/wasm-validator.h
@@ -179,7 +179,6 @@ public:
}
}
void visitCallIndirect(CallIndirect *curr) {
- shouldBeTrue(getModule()->table.segments.size() > 0, curr, "no table");
auto* type = getModule()->checkFunctionType(curr->fullType);
if (!shouldBeTrue(!!type, curr, "call_indirect type must exist")) return;
shouldBeEqualOrFirstIsUnreachable(curr->target->type, i32, curr, "indirect call target must be an i32");
@@ -335,7 +334,7 @@ public:
}
void visitGlobal(Global* curr) {
- shouldBeTrue(curr->init->is<Const>(), curr->name, "global init must be valid");
+ shouldBeTrue(curr->init->is<Const>() || curr->init->is<GetGlobal>(), curr->name, "global init must be valid");
shouldBeEqual(curr->type, curr->init->type, nullptr, "global init must have correct type");
}
@@ -394,7 +393,15 @@ public:
break;
}
}
- shouldBeTrue(found, name, "module exports must be found");
+ shouldBeTrue(found, name, "module function exports must be found");
+ } else if (exp->kind == Export::Global) {
+ shouldBeTrue(curr->checkGlobal(name), name, "module global exports must be found");
+ } else if (exp->kind == Export::Table) {
+ shouldBeTrue(name == Name("0") || name == curr->table.name, name, "module table exports must be found");
+ } else if (exp->kind == Export::Memory) {
+ shouldBeTrue(name == Name("0") || name == curr->memory.name, name, "module memory exports must be found");
+ } else {
+ WASM_UNREACHABLE();
}
Name exportName = exp->name;
shouldBeFalse(exportNames.count(exportName) > 0, exportName, "module exports must be unique");
diff --git a/src/wasm.cpp b/src/wasm.cpp
index f6486eb50..a910575f0 100644
--- a/src/wasm.cpp
+++ b/src/wasm.cpp
@@ -72,12 +72,9 @@ Name GROW_WASM_MEMORY("__growWasmMemory"),
BR("br"),
ANYFUNC("anyfunc"),
FAKE_RETURN("fake_return_waka123"),
- ASSERT_RETURN("assert_return"),
- ASSERT_TRAP("assert_trap"),
- ASSERT_INVALID("assert_invalid"),
+ MUT("mut"),
SPECTEST("spectest"),
PRINT("print"),
- INVOKE("invoke"),
EXIT("exit");
// core AST type checking
diff --git a/src/wasm.h b/src/wasm.h
index 4d5cf4d70..53b4e2938 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1027,7 +1027,11 @@ public:
void finalize() {
if (condition) {
- type = none;
+ if (value) {
+ type = value->type;
+ } else {
+ type = none;
+ }
} else {
type = unreachable;
}
@@ -1519,6 +1523,7 @@ public:
Name name;
WasmType type;
Expression* init;
+ bool mutable_;
};
class Module {
@@ -1547,12 +1552,6 @@ private:
public:
Module() {};
- FunctionType* getFunctionType(Index i) { assert(i < functionTypes.size()); return functionTypes[i].get(); }
- Import* getImport(Index i) { assert(i < imports.size()); return imports[i].get(); }
- Export* getExport(Index i) { assert(i < exports.size()); return exports[i].get(); }
- Function* getFunction(Index i) { assert(i < functions.size()); return functions[i].get(); }
- Global* getGlobal(Index i) { assert(i < globals.size()); return globals[i].get(); }
-
FunctionType* getFunctionType(Name name) { assert(functionTypesMap.count(name)); return functionTypesMap[name]; }
Import* getImport(Name name) { assert(importsMap.count(name)); return importsMap[name]; }
Export* getExport(Name name) { assert(exportsMap.count(name)); return exportsMap[name]; }
@@ -1565,56 +1564,35 @@ public:
Function* checkFunction(Name name) { if (!functionsMap.count(name)) return nullptr; return functionsMap[name]; }
Global* checkGlobal(Name name) { if (!globalsMap.count(name)) return nullptr; return globalsMap[name]; }
- FunctionType* checkFunctionType(Index i) { if (i >= functionTypes.size()) return nullptr; return functionTypes[i].get(); }
- Import* checkImport(Index i) { if (i >= imports.size()) return nullptr; return imports[i].get(); }
- Export* checkExport(Index i) { if (i >= exports.size()) return nullptr; return exports[i].get(); }
- Function* checkFunction(Index i) { if (i >= functions.size()) return nullptr; return functions[i].get(); }
- Global* checkGlobal(Index i) { if (i >= globals.size()) return nullptr; return globals[i].get(); }
-
void addFunctionType(FunctionType* curr) {
- Name numericName = Name::fromInt(functionTypes.size()); // TODO: remove all these, assert on names already existing, do numeric stuff in wasm-s-parser etc.
- if (curr->name.isNull()) {
- curr->name = numericName;
- }
+ assert(curr->name.is());
functionTypes.push_back(std::unique_ptr<FunctionType>(curr));
+ assert(functionTypesMap.find(curr->name) == functionTypesMap.end());
functionTypesMap[curr->name] = curr;
- functionTypesMap[numericName] = curr;
}
void addImport(Import* curr) {
- Name numericName = Name::fromInt(imports.size());
- if (curr->name.isNull()) {
- curr->name = numericName;
- }
+ assert(curr->name.is());
imports.push_back(std::unique_ptr<Import>(curr));
+ assert(importsMap.find(curr->name) == importsMap.end());
importsMap[curr->name] = curr;
- importsMap[numericName] = curr;
}
void addExport(Export* curr) {
- Name numericName = Name::fromInt(exports.size());
- if (curr->name.isNull()) {
- curr->name = numericName;
- }
+ assert(curr->name.is());
exports.push_back(std::unique_ptr<Export>(curr));
+ assert(exportsMap.find(curr->name) == exportsMap.end());
exportsMap[curr->name] = curr;
- exportsMap[numericName] = curr;
}
void addFunction(Function* curr) {
- Name numericName = Name::fromInt(functions.size());
- if (curr->name.isNull()) {
- curr->name = numericName;
- }
+ assert(curr->name.is());
functions.push_back(std::unique_ptr<Function>(curr));
+ assert(functionsMap.find(curr->name) == functionsMap.end());
functionsMap[curr->name] = curr;
- functionsMap[numericName] = curr;
}
void addGlobal(Global* curr) {
- Name numericName = Name::fromInt(globals.size());
- if (curr->name.isNull()) {
- curr->name = numericName;
- }
+ assert(curr->name.is());
globals.push_back(std::unique_ptr<Global>(curr));
+ assert(globalsMap.find(curr->name) == globalsMap.end());
globalsMap[curr->name] = curr;
- globalsMap[numericName] = curr;
}
void addStart(const Name &s) {