summaryrefslogtreecommitdiff
path: root/src/wasm/wasm-validator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm/wasm-validator.cpp')
-rw-r--r--src/wasm/wasm-validator.cpp68
1 files changed, 67 insertions, 1 deletions
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 276b9602f..beb994390 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -236,6 +236,7 @@ struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
};
std::unordered_map<Name, BreakInfo> breakInfos;
+ std::unordered_set<Name> delegateTargetNames;
std::set<Type> returnTypes; // types used in returns
@@ -276,11 +277,52 @@ public:
void visitLoop(Loop* curr);
void visitIf(If* curr);
+ static void visitPreTry(FunctionValidator* self, Expression** currp) {
+ auto* curr = (*currp)->cast<Try>();
+ if (curr->name.is()) {
+ self->delegateTargetNames.insert(curr->name);
+ }
+ }
+
+ // We remove try's label before proceeding to verify catch bodies because the
+ // following is a validation failure:
+ // (try $l0
+ // (do ... )
+ // (catch $e
+ // (try
+ // (do ...)
+ // (delegate $l0) ;; validation failure
+ // )
+ // )
+ // )
+ // Unlike branches, if delegate's target 'catch' is located above the
+ // delegate, it is a validation failure.
+ static void visitPreCatch(FunctionValidator* self, Expression** currp) {
+ auto* curr = (*currp)->cast<Try>();
+ if (curr->name.is()) {
+ self->delegateTargetNames.erase(curr->name);
+ }
+ }
+
// override scan to add a pre and a post check task to all nodes
static void scan(FunctionValidator* self, Expression** currp) {
+ auto* curr = *currp;
+ // Treat 'Try' specially because we need to run visitPreCatch between the
+ // try body and catch bodies
+ if (curr->is<Try>()) {
+ self->pushTask(doVisitTry, currp);
+ auto& list = curr->cast<Try>()->catchBodies;
+ for (int i = int(list.size()) - 1; i >= 0; i--) {
+ self->pushTask(scan, &list[i]);
+ }
+ self->pushTask(visitPreCatch, currp);
+ self->pushTask(scan, &curr->cast<Try>()->body);
+ self->pushTask(visitPreTry, currp);
+ return;
+ }
+
PostWalker<FunctionValidator>::scan(self, currp);
- auto* curr = *currp;
if (curr->is<Block>()) {
self->pushTask(visitPreBlock, currp);
}
@@ -334,6 +376,7 @@ public:
void visitRefIs(RefIs* curr);
void visitRefFunc(RefFunc* curr);
void visitRefEq(RefEq* curr);
+ void noteDelegate(Name name, Expression* curr);
void visitTry(Try* curr);
void visitThrow(Throw* curr);
void visitRethrow(Rethrow* curr);
@@ -767,6 +810,7 @@ void FunctionValidator::noteBreak(Name name, Type valueType, Expression* curr) {
}
}
}
+
void FunctionValidator::visitBreak(Break* curr) {
noteBreak(curr->name, curr->value, curr);
if (curr->value) {
@@ -2029,6 +2073,14 @@ void FunctionValidator::visitRefEq(RefEq* curr) {
"ref.eq's right argument should be a subtype of eqref");
}
+void FunctionValidator::noteDelegate(Name name, Expression* curr) {
+ if (name != DELEGATE_CALLER_TARGET) {
+ shouldBeTrue(delegateTargetNames.find(name) != delegateTargetNames.end(),
+ curr,
+ "all delegate targets must be valid");
+ }
+}
+
void FunctionValidator::visitTry(Try* curr) {
shouldBeTrue(getModule()->features.hasExceptionHandling(),
curr,
@@ -2061,6 +2113,17 @@ void FunctionValidator::visitTry(Try* curr) {
shouldBeTrue(curr->catchBodies.size() - curr->catchEvents.size() <= 1,
curr,
"the number of catch blocks and events do not match");
+
+ shouldBeFalse(curr->isCatch() && curr->isDelegate(),
+ curr,
+ "try cannot have both catch and delegate at the same time");
+ shouldBeTrue(curr->isCatch() || curr->isDelegate(),
+ curr,
+ "try should have either catches or a delegate");
+
+ if (curr->isDelegate()) {
+ noteDelegate(curr->delegateTarget, curr);
+ }
}
void FunctionValidator::visitThrow(Throw* curr) {
@@ -2470,6 +2533,9 @@ void FunctionValidator::visitFunction(Function* curr) {
shouldBeTrue(
breakInfos.empty(), curr->body, "all named break targets must exist");
+ shouldBeTrue(delegateTargetNames.empty(),
+ curr->body,
+ "all named delegate targets must exist");
returnTypes.clear();
labelNames.clear();
// validate optional local names