summaryrefslogtreecommitdiff
path: root/src/wasm/wasm-validator.cpp
diff options
context:
space:
mode:
authorHeejin Ahn <aheejin@gmail.com>2019-08-13 00:29:26 +0900
committerGitHub <noreply@github.com>2019-08-13 00:29:26 +0900
commite2f49d8227f2b71e4dede5cf4074bb9f65e3d77f (patch)
tree30b132b02824839d1d7718ed32c6b90cc0828151 /src/wasm/wasm-validator.cpp
parent69ad1e8a8d2e1d395e30230433742f4f5668563b (diff)
downloadbinaryen-e2f49d8227f2b71e4dede5cf4074bb9f65e3d77f.tar.gz
binaryen-e2f49d8227f2b71e4dede5cf4074bb9f65e3d77f.tar.bz2
binaryen-e2f49d8227f2b71e4dede5cf4074bb9f65e3d77f.zip
Add basic exception handling support (#2282)
This adds basic support for exception handling instructions, according to the spec: https://github.com/WebAssembly/exception-handling/blob/master/proposals/Exceptions.md This PR includes support for: - Binary reading/writing - Wast reading/writing - Stack IR - Validation - binaryen.js + C API - Few IR routines: branch-utils, type-updating, etc - Few passes: just enough to make `wasm-opt -O` pass - Tests This PR does not include support for many optimization passes, fuzzer, or interpreter. They will be follow-up PRs. Try-catch construct is modeled in Binaryen IR in a similar manner to that of if-else: each of try body and catch body will contain a block, which can be omitted if there is only a single instruction. This block will not be emitted in wast or binary, as in if-else. As in if-else, `class Try` contains two expressions each for try body and catch body, and `catch` is not modeled as an instruction. `exnref` value pushed by `catch` is get by `pop` instruction. `br_on_exn` is special: it returns different types of values when taken and not taken. We make `exnref`, the type `br_on_exn` pushes if not taken, as `br_on_exn`'s type.
Diffstat (limited to 'src/wasm/wasm-validator.cpp')
-rw-r--r--src/wasm/wasm-validator.cpp105
1 files changed, 101 insertions, 4 deletions
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 73731d3e5..8eb3341de 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -254,6 +254,7 @@ public:
}
void noteBreak(Name name, Expression* value, Expression* curr);
+ void noteBreak(Name name, Type valueType, Expression* curr);
void visitBreak(Break* curr);
void visitSwitch(Switch* curr);
void visitCall(Call* curr);
@@ -284,6 +285,10 @@ public:
void visitDrop(Drop* curr);
void visitReturn(Return* curr);
void visitHost(Host* curr);
+ void visitTry(Try* curr);
+ void visitThrow(Throw* curr);
+ void visitRethrow(Rethrow* curr);
+ void visitBrOnExn(BrOnExn* curr);
void visitFunction(Function* curr);
// helpers
@@ -519,11 +524,15 @@ void FunctionValidator::visitIf(If* curr) {
void FunctionValidator::noteBreak(Name name,
Expression* value,
Expression* curr) {
- Type valueType = none;
- Index arity = 0;
if (value) {
- valueType = value->type;
- shouldBeUnequal(valueType, none, curr, "breaks must have a valid value");
+ shouldBeUnequal(value->type, none, curr, "breaks must have a valid value");
+ }
+ noteBreak(name, value ? value->type : none, curr);
+}
+
+void FunctionValidator::noteBreak(Name name, Type valueType, Expression* curr) {
+ Index arity = 0;
+ if (valueType != none) {
arity = 1;
}
auto iter = breakInfos.find(name);
@@ -1581,6 +1590,94 @@ void FunctionValidator::visitHost(Host* curr) {
}
}
+void FunctionValidator::visitTry(Try* curr) {
+ if (curr->type != unreachable) {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->body->type,
+ curr->type,
+ curr->body,
+ "try's type does not match try body's type");
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->catchBody->type,
+ curr->type,
+ curr->catchBody,
+ "try's type does not match catch's body type");
+ }
+ if (isConcreteType(curr->body->type)) {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->catchBody->type,
+ curr->body->type,
+ curr->catchBody,
+ "try's body type must match catch's body type");
+ }
+ if (isConcreteType(curr->catchBody->type)) {
+ shouldBeEqualOrFirstIsUnreachable(
+ curr->body->type,
+ curr->catchBody->type,
+ curr->body,
+ "try's body type must match catch's body type");
+ }
+}
+
+void FunctionValidator::visitThrow(Throw* curr) {
+ if (!info.validateGlobally) {
+ return;
+ }
+ shouldBeEqual(
+ curr->type, unreachable, curr, "throw's type must be unreachable");
+ auto* event = getModule()->getEventOrNull(curr->event);
+ if (!shouldBeTrue(!!event, curr, "throw's event must exist")) {
+ return;
+ }
+ if (!shouldBeTrue(curr->operands.size() == event->params.size(),
+ curr,
+ "event's param numbers must match")) {
+ return;
+ }
+ for (size_t i = 0; i < curr->operands.size(); i++) {
+ if (!shouldBeEqualOrFirstIsUnreachable(curr->operands[i]->type,
+ event->params[i],
+ curr->operands[i],
+ "event param types must match") &&
+ !info.quiet) {
+ getStream() << "(on argument " << i << ")\n";
+ }
+ }
+}
+
+void FunctionValidator::visitRethrow(Rethrow* curr) {
+ shouldBeEqual(
+ curr->type, unreachable, curr, "rethrow's type must be unreachable");
+ shouldBeEqual(curr->exnref->type,
+ exnref,
+ curr->exnref,
+ "rethrow's argument must be exnref type");
+}
+
+void FunctionValidator::visitBrOnExn(BrOnExn* curr) {
+ Event* event = getModule()->getEventOrNull(curr->event);
+ shouldBeTrue(event != nullptr, curr, "br_on_exn's event must exist");
+ shouldBeTrue(event->params == curr->eventParams,
+ curr,
+ "br_on_exn's event params and event's params are different");
+ noteBreak(curr->name, curr->getSingleSentType(), curr);
+ shouldBeTrue(curr->exnref->type == unreachable ||
+ curr->exnref->type == exnref,
+ curr,
+ "br_on_exn's argument must be unreachable or exnref type");
+ if (curr->exnref->type == unreachable) {
+ shouldBeTrue(curr->type == unreachable,
+ curr,
+ "If exnref argument's type is unreachable, br_on_exn should "
+ "be unreachable too");
+ } else {
+ shouldBeTrue(curr->type == exnref,
+ curr,
+ "br_on_exn's type should be exnref unless its exnref argument "
+ "is unreachable");
+ }
+}
+
void FunctionValidator::visitFunction(Function* curr) {
FeatureSet typeFeatures = getFeatures(curr->result);
for (auto type : curr->params) {