| ;; Tests for resume_throw |
| |
| ;; Test resume_throw on a continuation that is never resumed. |
| (module |
| (tag $exn) |
| |
| (type $f (func)) |
| (type $k (cont $f)) |
| |
| (func $never (unreachable)) |
| (elem declare func $never) |
| |
| (func (export "throw_never") |
| (block $h |
| (try_table (catch $exn $h) |
| (cont.new $k (ref.func $never)) |
| (resume_throw $k $exn) |
| (unreachable) |
| ) |
| ) |
| ) |
| ) |
| (assert_return (invoke "throw_never")) |
| |
| ;; Test resume_throw with a value type argument. |
| (module |
| (tag $exn_i32 (param i32)) |
| |
| (type $f (func)) |
| (type $k (cont $f)) |
| |
| (func $never (unreachable)) |
| (elem declare func $never) |
| |
| (func (export "throw_never_i32") (result i32) |
| (block $h (result i32) |
| (try_table (result i32) (catch $exn_i32 $h) |
| (i32.const 42) |
| (cont.new $k (ref.func $never)) |
| (resume_throw $k $exn_i32) |
| (unreachable) |
| ) |
| ) |
| ) |
| ) |
| (assert_return (invoke "throw_never_i32") (i32.const 42)) |
| |
| ;; Test resume_throw with a reference type argument. |
| (module |
| (tag $exn_ref (param externref)) |
| |
| (type $f (func)) |
| (type $k (cont $f)) |
| |
| (func $never (unreachable)) |
| (elem declare func $never) |
| |
| (func (export "throw_never_ref") (param $val externref) (result externref) |
| (block $h (result externref) |
| (try_table (result externref) (catch $exn_ref $h) |
| (local.get $val) |
| (cont.new $k (ref.func $never)) |
| (resume_throw $k $exn_ref) |
| (unreachable) |
| ) |
| ) |
| ) |
| ) |
| (assert_return (invoke "throw_never_ref" (ref.extern 1)) (ref.extern 1)) |
| |
| ;; Test resume_throw where the continuation handles the exception. |
| (module |
| (tag $exn) |
| (tag $e1) |
| |
| (type $f (func)) |
| (type $k (cont $f)) |
| |
| (func $handler |
| (block $h |
| (try_table (catch $exn $h) |
| (suspend $e1) |
| ) |
| ) |
| ) |
| (elem declare func $handler) |
| |
| (func (export "throw_handled") |
| (block $h (result (ref $k)) |
| (resume $k (on $e1 $h) (cont.new $k (ref.func $handler))) |
| (unreachable) |
| ) |
| (resume_throw $k $exn) |
| ) |
| ) |
| (assert_return (invoke "throw_handled")) |
| |
| ;; Test resume_throw where the continuation does not handle the exception. |
| (module |
| (tag $exn) |
| (tag $e1) |
| |
| (type $f (func)) |
| (type $k (cont $f)) |
| |
| (func $no_handler |
| (suspend $e1) |
| ) |
| (elem declare func $no_handler) |
| |
| (func (export "throw_unhandled") |
| (block $h (result (ref $k)) |
| (resume $k (on $e1 $h) (cont.new $k (ref.func $no_handler))) |
| (unreachable) |
| ) |
| (resume_throw $k $exn) |
| ) |
| ) |
| (assert_exception (invoke "throw_unhandled")) |
| |
| ;; Test resume_throw on a consumed continuation. |
| (module |
| (tag $exn) |
| |
| (type $f (func)) |
| (type $k (cont $f)) |
| |
| (func $f1) |
| (elem declare func $f1) |
| |
| (func (export "throw_consumed") |
| (local $k_ref (ref $k)) |
| (local.set $k_ref (cont.new $k (ref.func $f1))) |
| (resume $k (local.get $k_ref)) ;; consume it |
| (resume_throw $k $exn (local.get $k_ref)) ;; should trap |
| ) |
| ) |
| (assert_trap (invoke "throw_consumed") "continuation already consumed") |
| |
| ;; Test resume_throw on a null continuation reference. |
| (module |
| (tag $exn) |
| (type $f (func)) |
| (type $k (cont $f)) |
| (func (export "throw_null") |
| (resume_throw $k $exn (ref.null $k)) |
| ) |
| ) |
| (assert_trap (invoke "throw_null") "null continuation reference") |
| |
| ;; Test resume_throw_ref where the continuation handles the exception. |
| (module |
| (tag $e0 (param i32)) |
| (tag $yield) |
| |
| (type $f (func (result i32))) |
| (type $k (cont $f)) |
| |
| (func (export "throw_handled_ref") (result i32) |
| (local $k_ref (ref $k)) |
| (local.set $k_ref (cont.new $k (ref.func $yield42))) |
| |
| (block $y (result (ref $k)) |
| (resume $k (on $yield $y) |
| (local.get $k_ref)) |
| (unreachable)) |
| (local.set $k_ref) |
| |
| (block $h (result i32 exnref) |
| (try_table (catch_ref $e0 $h) |
| (i32.const 42) |
| (throw $e0)) |
| (unreachable) |
| ) |
| |
| (resume_throw_ref $k (local.get $k_ref)) |
| (return) |
| ) |
| |
| (func $yield42 (result i32) |
| (block $h (result i32) |
| (try_table (result i32) (catch $e0 $h) |
| (suspend $yield) |
| (unreachable) |
| ) |
| ) |
| ) |
| (elem declare func $yield42) |
| ) |
| (assert_return (invoke "throw_handled_ref") (i32.const 42)) |
| |
| |
| ;; Test resume_throw_ref where the continuation does not handle the exception. |
| (module |
| (tag $e0) |
| |
| (type $f (func)) |
| (type $k (cont $f)) |
| |
| (func $no_handler |
| (unreachable) ;; We only throw into this function |
| ) |
| (elem declare func $no_handler) |
| |
| (func (export "throw_unhandled_ref") |
| (block $h (result exnref) |
| (try_table (catch_ref $e0 $h) (throw $e0)) |
| (unreachable) |
| ) |
| (resume_throw_ref $k (cont.new $k (ref.func $no_handler))) |
| ) |
| ) |
| (assert_exception (invoke "throw_unhandled_ref")) |
| |
| ;; Test resume_throw_ref on a consumed continuation. |
| (module |
| (tag $e0) |
| |
| (type $f (func)) |
| (type $k (cont $f)) |
| |
| (func $f1) |
| (elem declare func $f1) |
| |
| (func (export "throw_consumed_ref") |
| (local $k_ref (ref $k)) |
| (local.set $k_ref (cont.new $k (ref.func $f1))) |
| (resume $k (local.get $k_ref)) ;; consume it |
| |
| (block $h (result exnref) |
| (try_table (result exnref) (catch_ref $e0 $h) |
| (throw $e0) |
| ) |
| ) |
| (local.get $k_ref) |
| |
| (resume_throw_ref $k) ;; should trap |
| ) |
| ) |
| (assert_trap (invoke "throw_consumed_ref") "continuation already consumed") |
| |
| ;; Test resume_throw_ref on a null continuation reference. |
| (module |
| (tag $e0) |
| (type $f (func)) |
| (type $k (cont $f)) |
| (func (export "throw_null_ref") |
| (block $h (result exnref) |
| (try_table (catch_ref $e0 $h) |
| (throw $e0)) |
| (unreachable) |
| ) |
| (resume_throw_ref $k (ref.null $k)) |
| ) |
| ) |
| (assert_trap (invoke "throw_null_ref") "null continuation reference") |
| |
| ;; ---- Validation ---- |
| |
| ;; TODO(Binaryen): validate here, even though the continuation is null and we |
| ;; don't have the info in the IR. |
| ;;(assert_invalid |
| ;; (module |
| ;; (type $ft (func)) |
| ;; (type $ct (cont $ft)) |
| ;; (tag $exn (param i32)) |
| ;; (func |
| ;; (i64.const 0) |
| ;; (resume_throw $ct $exn (ref.null $ct)) ;; null continuation |
| ;; (unreachable))) |
| ;; "type mismatch") |
| |
| (assert_invalid |
| (module |
| (type $ft (func)) |
| (type $ct (cont $ft)) |
| (tag $exn) |
| (func |
| (ref.null $ct) |
| (i32.const 0) |
| (resume_throw $ct $exn) ;; exception tag does not take paramter |
| (unreachable))) |
| "type mismatch") |
| |
| (assert_invalid |
| (module |
| (type $ft (func)) |
| (type $ct (cont $ft)) |
| (tag $exn (param i32)) |
| (func |
| (resume_throw $ct $exn (ref.null $ct)) ;; missing exception payload |
| (unreachable))) |
| "type mismatch") |
| |
| |
| (assert_invalid |
| (module |
| (type $ft (func)) |
| (type $ct (cont $ft)) |
| (tag $exn (param externref)) |
| (func |
| (i64.const 0) |
| (resume_throw_ref $ct (ref.null $ct)) ;; expecting an exception ref |
| (unreachable))) |
| "type mismatch") |
| |
| (assert_invalid |
| (module |
| (type $ft (func)) |
| (type $ct (cont $ft)) |
| (func |
| (resume_throw_ref $ct (ref.null $ct)) ;; expecting an exception ref |
| (unreachable))) |
| "type mismatch") |