summaryrefslogtreecommitdiff
path: root/src/wasm-s-parser.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm-s-parser.h')
-rw-r--r--src/wasm-s-parser.h486
1 files changed, 361 insertions, 125 deletions
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());
}
};