-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[interpreter] Parse and convert EH opcodes #160
[interpreter] Parse and convert EH opcodes #160
Conversation
interpreter/syntax/ast.ml
Outdated
handler (* handle exception *) | ||
| Throw of var (* throw exception *) | ||
| Rethrow of var (* rethrow exception *) | ||
and handler = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not worth introducing an auxiliary type here (and auxiliary functions for it in various places). Simply make it two separate instructions, TryCatch and TryDelegate. That they share an opcode/keyword is best considered a detail of the binary & textual representation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
interpreter/binary/decode.ml
Outdated
| _ -> | ||
let pos = pos s in | ||
let e' = instr s in | ||
instr_block' s (Source.(e' @@ region s pos pos) :: es) | ||
|
||
and handler s = | ||
let rec catch_list () = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: avoid nesting functions unless they close over local variables in a non-trivial fashion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
interpreter/syntax/free.ml
Outdated
|
||
and block (es : instr list) = | ||
let free = list instr es in {free with labels = shift free.labels} | ||
|
||
and handler = function | ||
| Catch (cs, catch_all) -> | ||
let rec catch_list = function |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same nit here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/core/exceptions.wast
Outdated
@@ -0,0 +1,429 @@ | |||
;; Test exception handling support |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you split this up into files focussing on individual instructions?
throw.wast
rethrow.wast
try_catch.wast
try_delegate.wast
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
b0d52fb
to
5e7081d
Compare
I was able to run these tests for SpiderMonkey as well using its test harness for spec tests. After some modifications to account for recent changes (e.g., bare One issue I ran into, however, is that the core tests are using |
interpreter/binary/decode.ml
Outdated
let ca = if peek s = Some 0x19 then begin | ||
ignore (u8 s); | ||
Some (instr_block s) | ||
end else | ||
None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let ca = if peek s = Some 0x19 then begin | |
ignore (u8 s); | |
Some (instr_block s) | |
end else | |
None | |
let ca = | |
if peek s = Some 0x19 then begin | |
ignore (u8 s); | |
Some (instr_block s) | |
end else | |
None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
interpreter/binary/decode.ml
Outdated
end else | ||
begin match op s with | ||
| 0x0b -> try_catch bt es [] None | ||
| 0x18 -> try_delegate bt es (at var s) | ||
| b -> illegal s pos b | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
end else | |
begin match op s with | |
| 0x0b -> try_catch bt es [] None | |
| 0x18 -> try_delegate bt es (at var s) | |
| b -> illegal s pos b | |
end | |
end else begin | |
match op s with | |
| 0x0b -> try_catch bt es [] None | |
| 0x18 -> try_delegate bt es (at var s) | |
| b -> illegal s pos b | |
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
5e7081d
to
6ad5c54
Compare
I think that makes sense and I just updated the PR with that suggestion. The JS implementation of the rule uses a simple try/catch to accept any exception. I was actually planning to add such a rule later that uses the JS API to also check the type and content of the exception, but for now I think it is sufficient to add the weak form to solve the issue you pointed out. |
Thanks! I just tried the new commit with |
Ah, wanted to comment on the assertion question as well, but forgot. I agree, we should have a dedicated assertion for that. But can we just call it |
6ad5c54
to
1f62ccf
Compare
Done, both look fine to me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I haven't read the ML implementation part yet but the test cases here match my understanding.
test/core/rethrow.wast
Outdated
(do (throw $e0)) | ||
(catch $e0 | ||
(if (i32.eq (local.get 0) (i32.const 0)) (then (rethrow 1))) | ||
(if (i32.eq (local.get 0) (i32.const 1)) (then (rethrow 2))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(if (i32.eq (local.get 0) (i32.const 1)) (then (rethrow 2))) | |
(if (i32.eq (local.get 0) (i32.const 1)) (then (rethrow 2))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/core/rethrow.wast
Outdated
(do (if (i32.eqz (local.get 0)) (then (rethrow 2))) (i32.const 42)) | ||
(catch $e0 (i32.const 23)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(do (if (i32.eqz (local.get 0)) (then (rethrow 2))) (i32.const 42)) | |
(catch $e0 (i32.const 23)) | |
(do (if (i32.eqz (local.get 0)) (then (rethrow 2))) (i32.const 42)) | |
(catch $e0 (i32.const 23)) |
Tab characters are mixed in there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
test/core/try_delegate.wast
Outdated
|
||
(assert_return (invoke "delegate-merge" (i32.const 1) (i32.const 0)) (i32.const 2)) | ||
(assert_exception (invoke "delegate-merge" (i32.const 2) (i32.const 0))) | ||
(assert_return (invoke "delegate-merge" (i32.const 1)) (i32.const 2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it OK to call a function having two parameters with just one parameter? Then are the remaining params are set to 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be ok but in this case that was a mistake, thanks for noticing! Fixed.
(module | ||
(event $imported-e0 (import "test" "e0")) | ||
(func $imported-throw (import "test" "throw")) | ||
(event $e0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming that we are changing this to (tag ...)
(not necessarily in this PR), do we need a text syntax to represent the attribute (exception)? Currently we are tentatively using
(event $e0 (attr 0))
in Binaryen, but not sure if this is a good one. (I uploaded a PR to change this in Binaryen to (tag ..)
in WebAssembly/binaryen#3937, but this attr
thing is a separate thing anyway)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As argued elsewhere, I don't think it is necessary to manifest the (so far vacuous) attribute in the text format for the time being. It's not even necessary to put it in the semantics. All we need right now is a reserved zero byte in the binary format, until we know better what possible distinctions are needed in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if we eventually need a different tag kind and go with @aheejin's syntax, exception tags would be a special case as shown below?
(tag) ;; an exception tag
(tag (attr 0)) ;; an exception tag
(tag (attr 1)) ;; some other tag
I am fine with that, just want to clarify.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it would default to the current meaning.
FWIW, it might not even be necessary that we distinguish tags by role. That is, it seems fine to use any tag as an exception, as long as it has a suitable type (with empty result). Provided there is no source of confusion between tags used in different roles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. I'll remove the attr
attribute from the Binaryen text format then.
Add support for EH opcodes in the parser, AST, encoder, decoder and formatter, and add spec tests. This can already be used to convert the tests to JS, but not run them with the interpreter yet since validation and execution are still missing.
1f62ccf
to
45b454d
Compare
This attribute is always 0 and reserved for future use. In Binayren's unofficial text format we were writing this field as `(attr 0)`, but we have recently come to the conclusion that this is not necessary. Relevant discussion: WebAssembly/exception-handling#160 (comment)
This attribute is always 0 and reserved for future use. In Binayren's unofficial text format we were writing this field as `(attr 0)`, but we have recently come to the conclusion that this is not necessary. Relevant discussion: WebAssembly/exception-handling#160 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @aheejin that the change from event
to tag
could be done in a different PR, so everything LGTM. Sorry for the delayed review!
The assert_uncaught_exception assertion used for exception handling was renamed to assert_exception in the final version of WebAssembly/exception-handling#160 but was out-of-date in the wast crate.
The assert_uncaught_exception assertion used for exception handling was renamed to assert_exception in the final version of WebAssembly/exception-handling#160 but was out-of-date in the wast crate.
Please take a look. This adds support for EH opcodes in the parser, AST, encoder, decoder and formatter, and adds core spec tests. This follows the latest discussions that seem to have reached agreement: unwind is not supported (#156), and try blocks may have no handlers attached to them (#157).
This can already be used to convert the tests to JS, so this should be a useful starting point to ensure consistency across JS engines. The interpreter itself is still missing validation and execution.