summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
authorHeejin Ahn <aheejin@gmail.com>2019-05-31 20:02:37 -0700
committerGitHub <noreply@github.com>2019-05-31 20:02:37 -0700
commitfe99e3458f11d1a01fa3ad5b68883dbcba3903af (patch)
tree6f5eda61c7c7cba9c3b16be5e361cdc148d8b315 /src/wasm
parent7306f60a4474ca1fa948bddee5c068e7c2f635f6 (diff)
downloadbinaryen-fe99e3458f11d1a01fa3ad5b68883dbcba3903af.tar.gz
binaryen-fe99e3458f11d1a01fa3ad5b68883dbcba3903af.tar.bz2
binaryen-fe99e3458f11d1a01fa3ad5b68883dbcba3903af.zip
Add event section (#2151)
This adds support for the event and the event section, as specified in https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md#changes-to-the-binary-model. Wasm events are features that suspend the current execution and transfer the control flow to a corresponding handler. Currently the only supported event kind is exceptions. For events, this includes support for - Binary file reading/writing - Wast file reading/writing - Binaryen.js API - Fuzzer - Validation - Metadce - Passes: metrics, minify-imports-and-exports, remove-unused-module-elements
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-binary.cpp95
-rw-r--r--src/wasm/wasm-s-parser.cpp149
-rw-r--r--src/wasm/wasm-type.cpp3
-rw-r--r--src/wasm/wasm-validator.cpp35
-rw-r--r--src/wasm/wasm.cpp47
5 files changed, 324 insertions, 5 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index d8ce18de5..91a2184e9 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -37,6 +37,7 @@ void WasmBinaryWriter::prepare() {
ModuleUtils::BinaryIndexes indexes(*wasm);
mappedFunctions = std::move(indexes.functionIndexes);
mappedGlobals = std::move(indexes.globalIndexes);
+ mappedEvents = std::move(indexes.eventIndexes);
importInfo = wasm::make_unique<ImportInfo>(*wasm);
}
@@ -63,6 +64,7 @@ void WasmBinaryWriter::write() {
writeDataCount();
writeFunctions();
writeDataSegments();
+ writeEvents();
if (debugInfo) {
writeNames();
}
@@ -245,6 +247,15 @@ void WasmBinaryWriter::writeImports() {
o << binaryType(global->type);
o << U32LEB(global->mutable_);
});
+ ModuleUtils::iterImportedEvents(*wasm, [&](Event* event) {
+ if (debug) {
+ std::cerr << "write one event" << std::endl;
+ }
+ writeImportHeader(event);
+ o << U32LEB(int32_t(ExternalKind::Event));
+ o << U32LEB(event->attribute);
+ o << U32LEB(getFunctionTypeIndex(event->type));
+ });
if (wasm->memory.imported()) {
if (debug) {
std::cerr << "write one memory" << std::endl;
@@ -401,6 +412,9 @@ void WasmBinaryWriter::writeExports() {
case ExternalKind::Global:
o << U32LEB(getGlobalIndex(curr->value));
break;
+ case ExternalKind::Event:
+ o << U32LEB(getEventIndex(curr->value));
+ break;
default:
WASM_UNREACHABLE();
}
@@ -453,6 +467,11 @@ uint32_t WasmBinaryWriter::getGlobalIndex(Name name) {
return mappedGlobals[name];
}
+uint32_t WasmBinaryWriter::getEventIndex(Name name) {
+ assert(mappedEvents.count(name));
+ return mappedEvents[name];
+}
+
void WasmBinaryWriter::writeFunctionTableDeclaration() {
if (!wasm->table.exists || wasm->table.imported()) {
return;
@@ -493,6 +512,27 @@ void WasmBinaryWriter::writeTableElements() {
finishSection(start);
}
+void WasmBinaryWriter::writeEvents() {
+ if (importInfo->getNumDefinedEvents() == 0) {
+ return;
+ }
+ if (debug) {
+ std::cerr << "== writeEvents" << std::endl;
+ }
+ auto start = startSection(BinaryConsts::Section::Event);
+ auto num = importInfo->getNumDefinedEvents();
+ o << U32LEB(num);
+ ModuleUtils::iterDefinedEvents(*wasm, [&](Event* event) {
+ if (debug) {
+ std::cerr << "write one" << std::endl;
+ }
+ o << U32LEB(event->attribute);
+ o << U32LEB(getFunctionTypeIndex(event->type));
+ });
+
+ finishSection(start);
+}
+
void WasmBinaryWriter::writeNames() {
bool hasContents = false;
if (wasm->functions.size() > 0) {
@@ -828,6 +868,9 @@ void WasmBinaryBuilder::read() {
case BinaryConsts::Section::Table:
readFunctionTableDeclaration();
break;
+ case BinaryConsts::Section::Event:
+ readEvents();
+ break;
default: {
readUserSection(payloadLen);
if (pos > oldPos + payloadLen) {
@@ -1210,6 +1253,13 @@ Name WasmBinaryBuilder::getGlobalName(Index index) {
return wasm.globals[index]->name;
}
+Name WasmBinaryBuilder::getEventName(Index index) {
+ if (index >= wasm.events.size()) {
+ throwError("invalid event index");
+ }
+ return wasm.events[index]->name;
+}
+
void WasmBinaryBuilder::getResizableLimits(Address& initial,
Address& max,
bool& shared,
@@ -1310,6 +1360,23 @@ void WasmBinaryBuilder::readImports() {
wasm.addGlobal(curr);
break;
}
+ case ExternalKind::Event: {
+ auto name = Name(std::string("eimport$") + std::to_string(i));
+ auto attribute = getU32LEB();
+ auto index = getU32LEB();
+ if (index >= wasm.functionTypes.size()) {
+ throwError("invalid event index " + std::to_string(index) + " / " +
+ std::to_string(wasm.functionTypes.size()));
+ }
+ Name type = wasm.functionTypes[index]->name;
+ std::vector<Type> params = wasm.functionTypes[index]->params;
+ auto* curr =
+ builder.makeEvent(name, attribute, type, std::move(params));
+ curr->module = module;
+ curr->base = base;
+ wasm.addEvent(curr);
+ break;
+ }
default: { throwError("bad import kind"); }
}
}
@@ -1841,6 +1908,9 @@ void WasmBinaryBuilder::processFunctions() {
case ExternalKind::Global:
curr->value = getGlobalName(index);
break;
+ case ExternalKind::Event:
+ curr->value = getEventName(index);
+ break;
default:
throwError("bad export kind");
}
@@ -1954,6 +2024,31 @@ void WasmBinaryBuilder::readTableElements() {
}
}
+void WasmBinaryBuilder::readEvents() {
+ if (debug) {
+ std::cerr << "== readEvents" << std::endl;
+ }
+ size_t numEvents = getU32LEB();
+ if (debug) {
+ std::cerr << "num: " << numEvents << std::endl;
+ }
+ for (size_t i = 0; i < numEvents; i++) {
+ if (debug) {
+ std::cerr << "read one" << std::endl;
+ }
+ auto attribute = getU32LEB();
+ auto typeIndex = getU32LEB();
+ if (typeIndex >= wasm.functionTypes.size()) {
+ throwError("invalid event index " + std::to_string(typeIndex) + " / " +
+ std::to_string(wasm.functionTypes.size()));
+ }
+ Name type = wasm.functionTypes[typeIndex]->name;
+ std::vector<Type> params = wasm.functionTypes[typeIndex]->params;
+ wasm.addEvent(Builder::makeEvent(
+ "event$" + std::to_string(i), attribute, type, std::move(params)));
+ }
+}
+
static bool isIdChar(char ch) {
return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') || ch == '!' || ch == '#' || ch == '$' ||
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 3a5dfbced..8ead42608 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -388,6 +388,8 @@ void SExpressionWasmBuilder::preParseImports(Element& curr) {
parseTable(curr, true /* preParseImport */);
} else if (id == MEMORY) {
parseMemory(curr, true /* preParseImport */);
+ } else if (id == EVENT) {
+ parseEvent(curr, true /* preParseImport */);
} else {
throw ParseException(
"fancy import we don't support yet", curr.line, curr.col);
@@ -430,6 +432,9 @@ void SExpressionWasmBuilder::parseModuleElement(Element& curr) {
if (id == TYPE) {
return; // already done
}
+ if (id == EVENT) {
+ return parseEvent(curr);
+ }
std::cerr << "bad module element " << id.str << '\n';
throw ParseException("unknown module element", curr.line, curr.col);
}
@@ -473,6 +478,19 @@ Name SExpressionWasmBuilder::getGlobalName(Element& s) {
}
}
+Name SExpressionWasmBuilder::getEventName(Element& s) {
+ if (s.dollared()) {
+ return s.str();
+ } else {
+ // index
+ size_t offset = atoi(s.str().c_str());
+ if (offset >= eventNames.size()) {
+ throw ParseException("unknown event in getEventName");
+ }
+ return eventNames[offset];
+ }
+}
+
// Parse various forms of (param ...) or (local ...) element. This ignores all
// parameter or local names when specified.
std::vector<Type> SExpressionWasmBuilder::parseParamOrLocal(Element& s) {
@@ -1877,6 +1895,8 @@ void SExpressionWasmBuilder::parseExport(Element& s) {
ex->kind = ExternalKind::Table;
} else if (elementStartsWith(inner, GLOBAL)) {
ex->kind = ExternalKind::Global;
+ } else if (inner[0]->str() == EVENT) {
+ ex->kind = ExternalKind::Event;
} else {
throw ParseException("invalid export");
}
@@ -1913,6 +1933,8 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
wasm.table.exists = true;
} else if (elementStartsWith(*s[3], GLOBAL)) {
kind = ExternalKind::Global;
+ } else if ((*s[3])[0]->str() == EVENT) {
+ kind = ExternalKind::Event;
} else {
newStyle = false; // either (param..) or (result..)
}
@@ -1936,6 +1958,9 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
name = Name("import$memory$" + std::to_string(0));
} else if (kind == ExternalKind::Table) {
name = Name("import$table$" + std::to_string(0));
+ } else if (kind == ExternalKind::Event) {
+ name = Name("import$event" + std::to_string(eventCounter++));
+ eventNames.push_back(name);
} else {
throw ParseException("invalid import");
}
@@ -1957,7 +1982,7 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
if (kind == ExternalKind::Function) {
FunctionType* functionType = nullptr;
auto func = make_unique<Function>();
- parseTypeUse(inner, j, functionType, func->params, func->result);
+ j = parseTypeUse(inner, j, functionType, func->params, func->result);
func->name = name;
func->module = module;
func->base = base;
@@ -1968,9 +1993,9 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
Type type;
bool mutable_ = false;
if (inner[j]->isStr()) {
- type = stringToType(inner[j]->str());
+ type = stringToType(inner[j++]->str());
} else {
- auto& inner2 = *inner[j];
+ auto& inner2 = *inner[j++];
if (inner2[0]->str() != MUT) {
throw ParseException("expected mut");
}
@@ -1997,6 +2022,7 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
} else {
wasm.table.max = Table::kUnlimitedSize;
}
+ j++; // funcref
// ends with the table element type
} else if (kind == ExternalKind::Memory) {
wasm.memory.module = module;
@@ -2007,10 +2033,32 @@ void SExpressionWasmBuilder::parseImport(Element& s) {
throw ParseException("bad memory limit declaration");
}
wasm.memory.shared = true;
- parseMemoryLimits(limits, 1);
+ j = parseMemoryLimits(limits, 1);
} else {
- parseMemoryLimits(inner, j);
+ j = parseMemoryLimits(inner, j);
+ }
+ } else if (kind == ExternalKind::Event) {
+ FunctionType* functionType = nullptr;
+ auto event = make_unique<Event>();
+ if (j >= inner.size()) {
+ throw ParseException("event does not have an attribute", s.line, s.col);
+ }
+ auto& attrElem = *inner[j++];
+ if (!elementStartsWith(attrElem, ATTR) || attrElem.size() != 2) {
+ throw ParseException("invalid attribute", attrElem.line, attrElem.col);
}
+ event->attribute = atoi(attrElem[1]->c_str());
+ Type fakeResult; // just to call parseTypeUse
+ j = parseTypeUse(inner, j, functionType, event->params, fakeResult);
+ event->name = name;
+ event->module = module;
+ event->base = base;
+ event->type = functionType->name;
+ wasm.addEvent(event.release());
+ }
+ // If there are more elements, they are invalid
+ if (j < inner.size()) {
+ throw ParseException("invalid element", inner[j]->line, inner[j]->col);
}
}
@@ -2234,4 +2282,95 @@ void SExpressionWasmBuilder::parseType(Element& s) {
wasm.addFunctionType(std::move(type));
}
+void SExpressionWasmBuilder::parseEvent(Element& s, bool preParseImport) {
+ auto event = make_unique<Event>();
+ size_t i = 1;
+
+ // Parse name
+ if (s[i]->isStr() && s[i]->dollared()) {
+ auto& inner = *s[i++];
+ event->name = inner.str();
+ if (wasm.getEventOrNull(event->name)) {
+ throw ParseException("duplicate event", inner.line, inner.col);
+ }
+ } else {
+ event->name = Name::fromInt(eventCounter);
+ assert(!wasm.getEventOrNull(event->name));
+ }
+ eventCounter++;
+ eventNames.push_back(event->name);
+
+ // Parse import, if any
+ if (i < s.size() && elementStartsWith(*s[i], IMPORT)) {
+ assert(preParseImport && "import element in non-preParseImport mode");
+ auto& importElem = *s[i++];
+ if (importElem.size() != 3) {
+ throw ParseException("invalid import", importElem.line, importElem.col);
+ }
+ if (!importElem[1]->isStr() || importElem[1]->dollared()) {
+ throw ParseException(
+ "invalid import module name", importElem[1]->line, importElem[1]->col);
+ }
+ if (!importElem[2]->isStr() || importElem[2]->dollared()) {
+ throw ParseException(
+ "invalid import base name", importElem[2]->line, importElem[2]->col);
+ }
+ event->module = importElem[1]->str();
+ event->base = importElem[2]->str();
+ }
+
+ // Parse export, if any
+ if (i < s.size() && elementStartsWith(*s[i], EXPORT)) {
+ auto& exportElem = *s[i++];
+ if (event->module.is()) {
+ throw ParseException("import and export cannot be specified together",
+ exportElem.line,
+ exportElem.col);
+ }
+ if (exportElem.size() != 2) {
+ throw ParseException("invalid export", exportElem.line, exportElem.col);
+ }
+ if (!exportElem[1]->isStr() || exportElem[1]->dollared()) {
+ throw ParseException(
+ "invalid export name", exportElem[1]->line, exportElem[1]->col);
+ }
+ auto ex = make_unique<Export>();
+ ex->name = exportElem[1]->str();
+ if (wasm.getExportOrNull(ex->name)) {
+ throw ParseException(
+ "duplicate export", exportElem[1]->line, exportElem[1]->col);
+ }
+ ex->value = event->name;
+ ex->kind = ExternalKind::Event;
+ }
+
+ // Parse attribute
+ if (i >= s.size()) {
+ throw ParseException("event does not have an attribute", s.line, s.col);
+ }
+ auto& attrElem = *s[i++];
+ if (!elementStartsWith(attrElem, ATTR) || attrElem.size() != 2) {
+ throw ParseException("invalid attribute", attrElem.line, attrElem.col);
+ }
+ if (!attrElem[1]->isStr()) {
+ throw ParseException(
+ "invalid attribute", attrElem[1]->line, attrElem[1]->col);
+ }
+ event->attribute = atoi(attrElem[1]->c_str());
+
+ // Parse typeuse
+ FunctionType* functionType = nullptr;
+ Type fakeResult; // just co call parseTypeUse
+ i = parseTypeUse(s, i, functionType, event->params, fakeResult);
+ assert(functionType && "functionType should've been set by parseTypeUse");
+ event->type = functionType->name;
+
+ // If there are more elements, they are invalid
+ if (i < s.size()) {
+ throw ParseException("invalid element", s[i]->line, s[i]->col);
+ }
+
+ wasm.addEvent(event.release());
+}
+
} // namespace wasm
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index ebaba3f24..2c2723bf6 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -68,6 +68,9 @@ FeatureSet getFeatures(Type type) {
if (type == v128) {
return FeatureSet::SIMD;
}
+ if (type == except_ref) {
+ return FeatureSet::ExceptionHandling;
+ }
return FeatureSet();
}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 01fe7e976..1d12c2452 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -1745,6 +1745,10 @@ static void validateExports(Module& module, ValidationInfo& info) {
info.shouldBeTrue(name == Name("0") || name == module.memory.name,
name,
"module memory exports must be found");
+ } else if (exp->kind == ExternalKind::Event) {
+ info.shouldBeTrue(module.getEventOrNull(name),
+ name,
+ "module event exports must be found");
} else {
WASM_UNREACHABLE();
}
@@ -1855,6 +1859,36 @@ static void validateTable(Module& module, ValidationInfo& info) {
}
}
+static void validateEvents(Module& module, ValidationInfo& info) {
+ if (!module.events.empty()) {
+ info.shouldBeTrue(module.features.hasExceptionHandling(),
+ module.events[0]->name,
+ "Module has events (event-handling is disabled)");
+ }
+ for (auto& curr : module.events) {
+ info.shouldBeTrue(
+ curr->type.is(), curr->name, "Event should have a valid type");
+ FunctionType* ft = module.getFunctionType(curr->type);
+ info.shouldBeEqual(
+ ft->result, none, curr->name, "Event type's result type should be none");
+ info.shouldBeTrue(!curr->params.empty(),
+ curr->name,
+ "There should be 1 or more values in an event type");
+ info.shouldBeEqual(curr->attribute,
+ (unsigned)0,
+ curr->attribute,
+ "Currently only attribute 0 is supported");
+ for (auto type : curr->params) {
+ info.shouldBeTrue(isIntegerType(type) || isFloatType(type),
+ curr->name,
+ "Values in an event should have integer or float type");
+ }
+ info.shouldBeTrue(curr->params == ft->params,
+ curr->name,
+ "Event's function type and internal type should match");
+ }
+}
+
static void validateModule(Module& module, ValidationInfo& info) {
// start
if (module.start.is()) {
@@ -1889,6 +1923,7 @@ bool WasmValidator::validate(Module& module, Flags flags) {
validateGlobals(module, info);
validateMemory(module, info);
validateTable(module, info);
+ validateEvents(module, info);
validateModule(module, info);
}
// validate additional internal IR details when in pass-debug mode
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index c32ead836..c543686ed 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -82,6 +82,8 @@ Name SPECTEST("spectest");
Name PRINT("print");
Name EXIT("exit");
Name SHARED("shared");
+Name EVENT("event");
+Name ATTR("attr");
// Expressions
@@ -908,11 +910,20 @@ Function* Module::getFunction(Name name) {
Global* Module::getGlobal(Name name) {
auto iter = globalsMap.find(name);
if (iter == globalsMap.end()) {
+ assert(false);
Fatal() << "Module::getGlobal: " << name << " does not exist";
}
return iter->second;
}
+Event* Module::getEvent(Name name) {
+ auto iter = eventsMap.find(name);
+ if (iter == eventsMap.end()) {
+ Fatal() << "Module::getEvent: " << name << " does not exist";
+ }
+ return iter->second;
+}
+
FunctionType* Module::getFunctionTypeOrNull(Name name) {
auto iter = functionTypesMap.find(name);
if (iter == functionTypesMap.end()) {
@@ -945,6 +956,14 @@ Global* Module::getGlobalOrNull(Name name) {
return iter->second;
}
+Event* Module::getEventOrNull(Name name) {
+ auto iter = eventsMap.find(name);
+ if (iter == eventsMap.end()) {
+ return nullptr;
+ }
+ return iter->second;
+}
+
FunctionType* Module::addFunctionType(std::unique_ptr<FunctionType> curr) {
if (!curr->name.is()) {
Fatal() << "Module::addFunctionType: empty name";
@@ -1009,6 +1028,20 @@ Global* Module::addGlobal(Global* curr) {
return curr;
}
+Event* Module::addEvent(Event* curr) {
+ if (!curr->name.is()) {
+ Fatal() << "Module::addEvent: empty name";
+ }
+ if (getEventOrNull(curr->name)) {
+ Fatal() << "Module::addEvent: " << curr->name << " already exists";
+ }
+
+ events.emplace_back(curr);
+
+ eventsMap[curr->name] = curr;
+ return curr;
+}
+
void Module::addStart(const Name& s) { start = s; }
void Module::removeFunctionType(Name name) {
@@ -1051,6 +1084,16 @@ void Module::removeGlobal(Name name) {
globalsMap.erase(name);
}
+void Module::removeEvent(Name name) {
+ for (size_t i = 0; i < events.size(); i++) {
+ if (events[i]->name == name) {
+ events.erase(events.begin() + i);
+ break;
+ }
+ }
+ eventsMap.erase(name);
+}
+
// TODO: remove* for other elements
void Module::updateMaps() {
@@ -1070,6 +1113,10 @@ void Module::updateMaps() {
for (auto& curr : globals) {
globalsMap[curr->name] = curr.get();
}
+ eventsMap.clear();
+ for (auto& curr : events) {
+ eventsMap[curr->name] = curr.get();
+ }
}
void Module::clearDebugInfo() { debugInfoFileNames.clear(); }