/* Copyright 2021 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // ./test/core/stack-switching/validation.wast // ./test/core/stack-switching/validation.wast:8 let $0 = instantiate(`(module (type \$ft1 (func)) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i32) (result i32))) (type \$ct2 (cont \$ft2)) (func \$test (param \$p1 (ref cont)) (param \$p2 (ref nocont)) (param \$p3 (ref \$ct1)) (local \$x1 (ref cont)) (local \$x2 (ref nocont)) (local \$x3 (ref \$ct1)) (local \$x4 (ref \$ct2)) (local \$x5 (ref null \$ct1)) ;; nocont <: cont (local.set \$x1 (local.get \$p2)) ;; nocont <: \$ct1 (local.set \$x3 (local.get \$p2)) ;; \$ct1 <: \$cont (local.set \$x3 (local.get \$p3)) ;; (ref \$ct1) <: (ref null \$cont) (local.set \$x5 (local.get \$p3)) ) )`); // ./test/core/stack-switching/validation.wast:40 assert_invalid( () => instantiate(`(module (type \$ft1 (func)) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i32) (result i32))) (type \$ct2 (cont \$ft2)) (func \$test (param \$p1 (ref cont)) (param \$p2 (ref nocont)) (param \$p3 (ref \$ct1)) (local \$x1 (ref cont)) (local \$x2 (ref nocont)) (local \$x3 (ref \$ct1)) (local \$x4 (ref \$ct2)) (local \$x5 (ref null \$ct1)) ;; cont instantiate(`(module (type \$ft1 (func)) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i32) (result i32))) (type \$ct2 (cont \$ft2)) (func \$test (param \$p1 (ref cont)) (param \$p2 (ref nocont)) (param \$p3 (ref \$ct1)) (local \$x1 (ref cont)) (local \$x2 (ref nocont)) (local \$x3 (ref \$ct1)) (local \$x4 (ref \$ct2)) (local \$x5 (ref null \$ct1)) ;; \$ct1 instantiate(`(module (type \$ft1 (func)) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i32) (result i32))) (type \$ct2 (cont \$ft2)) (func \$test (param \$p1 (ref cont)) (param \$p2 (ref nocont)) (param \$p3 (ref \$ct1)) (local \$x1 (ref cont)) (local \$x2 (ref nocont)) (local \$x3 (ref \$ct1)) (local \$x4 (ref \$ct2)) (local \$x5 (ref null \$ct1)) ;; \$cont instantiate(`(module (type \$ft1 (func)) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i32) (result i32))) (type \$ct2 (cont \$ft2)) (func \$test (param \$p1 (ref cont)) (param \$p2 (ref nocont)) (param \$p3 (ref \$ct1)) (param \$p4 (ref \$ct2)) (param \$p5 (ref null \$ct1)) (local \$x1 (ref cont)) (local \$x2 (ref nocont)) (local \$x3 (ref \$ct1)) (local \$x4 (ref \$ct2)) (local \$x5 (ref null \$ct1)) ;; (ref null \$ct1) instantiate(`(module (type \$ft1 (func)) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i32) (result i32))) (type \$ct2 (cont \$ft2)) (func \$test (param \$p1 (ref cont)) (param \$p2 (ref nocont)) (param \$p3 (ref \$ct1)) (param \$p4 (ref \$ct2)) (param \$p5 (ref null \$ct1)) (local \$x1 (ref cont)) (local \$x2 (ref nocont)) (local \$x3 (ref \$ct1)) (local \$x4 (ref \$ct2)) (local \$x5 (ref null \$ct1)) ;; (ref \$ct1) instantiate(`(module (type \$ft0 (func (result i32))) (type \$ct0 (cont \$ft0)) (func \$error (param \$p_ft0 (ref \$ft0)) ;; error: non-continuation type on cont.bind (local.get \$p_ft0) (cont.bind \$ft0 \$ft0) (drop) ) )`), `non-continuation type`, ); // ./test/core/stack-switching/validation.wast:244 assert_invalid( () => instantiate(`(module (type \$ft0 (func (result i32))) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (func \$error (param \$p_ct1 (ref \$ct1)) ;; cont.bind type mismatch: passing wrong kind of continuation. ;; This is actually just checking type-matching, not cont.bind specific. (local.get \$p_ct1) (cont.bind \$ct0 \$ct0) (drop) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:265 assert_invalid( () => instantiate(`(module (type \$ft0 (func (result i32))) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i64 i32) (result i32))) (type \$ct2 (cont \$ft2)) (type \$ft1_alt (func (param i64) (result i32))) (type \$ct1_alt (cont \$ft1_alt)) (func \$error (param \$p_ct2 (ref \$ct2)) ;; error: two continuation types not agreeing on arg types (i64.const 123) (local.get \$p_ct2) (cont.bind \$ct2 \$ct1_alt) (drop) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:293 assert_invalid( () => instantiate(`(module (type \$ft0 (func (result i32))) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i64 i32) (result i32))) (type \$ct2 (cont \$ft2)) (type \$ft1_alt (func (param i32) (result i64))) (type \$ct1_alt (cont \$ft1_alt)) (func \$error (param \$p_ct2 (ref \$ct2)) ;; error: two continuation types not agreeing on return types (i64.const 123) (local.get \$p_ct2) (cont.bind \$ct2 \$ct1_alt) (drop) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:321 assert_invalid( () => instantiate(`(module (type \$ft0 (func (result i32))) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (func \$error (param \$p_ct0 (ref \$ct0)) ;; error: trying to go from 0 to 1 args (local.get \$p_ct0) (cont.bind \$ct0 \$ct1) (drop) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:341 assert_invalid( () => instantiate(`(module (type \$ft0 (func (result i32))) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (func \$error (param \$p_ct1 (ref \$ct1)) ;; error: Insufficient arguments:: ;; This is really testing the general application of arguments to instructions, ;; but trying to trick parsers of the folded form (cont.bind \$ct1 \$ct0 (local.get \$p_ct1)) (drop) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:363 assert_invalid( () => instantiate(`(module (type \$ft0 (func (result i32))) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (func \$error (param \$p_ct1 (ref \$ct1)) ;; error: Too many arguments:: ;; This is really testing the general application of arguments to instructions, ;; but trying to trick parsers of the folded form (cont.bind \$ct1 \$ct0 (i32.const 123) (i32.const 123) (local.get \$p_ct1)) (drop) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:388 let $2 = instantiate(`(module (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i32 i32) (result i32))) (type \$ct2 (cont \$ft2)) (func \$f1 (export "f1") (param i32) (result i32) (i32.const 123) ) (func \$drop_ct1 (param \$c (ref \$ct1))) ;; simple smoke test (func \$test_good1 (param \$x (ref \$ft1)) (result (ref \$ct1)) (local.get \$x) (cont.new \$ct1) ) ;; cont.new takes a nullable function (func \$test_good2 (param \$x (ref null \$ft1)) (result (ref \$ct1)) (local.get \$x) (cont.new \$ct1) ) )`); // ./test/core/stack-switching/validation.wast:416 assert_invalid( () => instantiate(`(module (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (func \$bad (param \$x (ref null \$ct1)) (result (ref \$ct1)) (local.get \$x) (cont.new \$ct1) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:432 assert_invalid( () => instantiate(`(module (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i32 i32) (result i32))) (type \$ct2 (cont \$ft2)) (func \$drop_ct1 (param \$c (ref \$ct1))) (func \$bad (param \$x (ref null \$ft2)) (result (ref \$ct1)) (local.get \$x) (cont.new \$ct1) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:453 assert_invalid( () => instantiate(`(module (type \$ft1 (func (param i32) (result i32))) (type \$ct1 (cont \$ft1)) (func \$bad (param \$x (ref null \$ct1)) (local.get \$x) (cont.new \$ft1) (drop) ) )`), `non-continuation type 0`, ); // ./test/core/stack-switching/validation.wast:473 let $3 = instantiate(`(module (type \$ft0 (func)) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param f32) (result f64))) (type \$ct1 (cont \$ft1)) (type \$ft2 (func (param i64) (result f64))) (type \$ct2 (cont \$ft2)) (type \$ft3 (func (param i32) (result f64))) (type \$ct3 (cont \$ft3)) (tag \$t0) (tag \$t1) (tag \$t2 (param i32) (result i64)) (tag \$t3 (param i64) (result i32)) ;; Multiple tags, all types handled correctly (func \$test0 (param \$x (ref \$ct1)) (result f64) (block \$handler0 (result i32 (ref \$ct2)) (block \$handler1 (result i64 (ref \$ct3)) (f32.const 1.23) (local.get \$x) (resume \$ct1 (on \$t2 \$handler0) (on \$t3 \$handler1)) (return) ) (unreachable) ) (unreachable) ) ;; Same as \$test0, but we provide two handlers for the same tag (func \$test1 (param \$x (ref \$ct1)) (result f64) (block \$handler0 (result i32 (ref \$ct2)) (block \$handler1 (result i32 (ref \$ct2)) (f32.const 1.23) (local.get \$x) (resume \$ct1 (on \$t2 \$handler0) (on \$t2 \$handler1)) (return) ) (unreachable) ) (unreachable) ) ;; resume takes a nullable reference (func \$test2 (param \$x (ref null \$ct0)) (local.get \$x) (resume \$ct0) ) ;; handler blocks take the continuation as nullable ref (func \$test3 (param \$x (ref \$ct0)) (block \$handler (result (ref null \$ct0)) (local.get \$x) (resume \$ct0 (on \$t0 \$handler)) (return) ) (unreachable) ) ;; Nothing wrong with using the same handler block for multiple tags (func \$test4 (param \$x (ref \$ct0)) (block \$handler (result (ref null \$ct0)) (local.get \$x) (resume \$ct0 (on \$t0 \$handler) (on \$t1 \$handler)) (return) ) (unreachable) ) ;; handler block can have params (func \$test5 (param \$x (ref \$ct0)) (local.get \$x) (block \$handler (param (ref \$ct0)) (result (ref \$ct0)) (resume \$ct0 (on \$t0 \$handler)) (return) ) (unreachable) ) )`); // ./test/core/stack-switching/validation.wast:557 assert_invalid( () => instantiate(`(module (type \$ft0 (func)) (type \$ct0 (cont \$ft0)) ;;(tag \$t0) (func \$error (param \$x (ref \$ct0)) (block \$handler (result (ref null \$ct0)) (local.get \$x) (resume \$ct0 (on 0 \$handler)) (return) ) (unreachable) ) )`), `unknown tag`, ); // ./test/core/stack-switching/validation.wast:576 assert_invalid( () => instantiate(`(module (type \$ft0 (func)) (type \$ct0 (cont \$ft0)) (func \$error (param \$x (ref \$ct0)) (local.get \$x) (resume \$ft0) ) )`), `non-continuation type`, ); // ./test/core/stack-switching/validation.wast:590 assert_invalid( () => instantiate(`(module (type \$ft0 (func)) (type \$ct0 (cont \$ft0)) (tag \$t0) (func \$test1 (param \$x (ref \$ct0)) (block \$handler (result (ref \$ft0)) (local.get \$x) (resume \$ct0 (on \$t0 \$handler)) (return) ) (unreachable) ) )`), `non-continuation type`, ); // ./test/core/stack-switching/validation.wast:608 assert_invalid( () => instantiate(`(module (type \$ft (func (param i32))) (type \$ct (cont \$ft)) (func \$error (param \$p (ref \$ct)) ;; error: Too many arguments:: ;; This is really testing the general application of arguments to instructions, ;; but trying to trick parsers of the folded form (resume \$ct (i32.const 123) (i32.const 123) (local.get \$p)) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:624 assert_invalid( () => instantiate(`(module (type \$ft (func (param i32))) (type \$ct (cont \$ft)) (func \$error (param \$p (ref \$ct)) ;; error: Too few arguments:: ;; This is really testing the general application of arguments to instructions, ;; but trying to trick parsers of the folded form (resume \$ct (local.get \$p)) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:640 assert_invalid( () => instantiate(`(module (type \$ft0 (func (param i32))) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param i64))) (type \$ct1 (cont \$ft1)) (func \$error (param \$p (ref \$ct1)) ;; error: Mismatch between annotation on resume and actual argument ;; This is really testing the general application of arguments to instructions. (resume \$ct0 (i32.const 123) (local.get \$p)) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:658 assert_invalid( () => instantiate(`(module (type \$ft0 (func)) (type \$ct0 (cont \$ft0)) (tag \$t (param i32)) (func \$error (param \$p (ref \$ct0)) (block \$handler (result (ref \$ct0)) ;; error: handler block has insufficient number of results (local.get \$p) (resume \$ct0 (on \$t \$handler)) (return) ) (unreachable) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:679 assert_invalid( () => instantiate(`(module (type \$ft0 (func)) (type \$ct0 (cont \$ft0)) (tag \$t (param i32)) (func \$error (param \$p (ref \$ct0)) (block \$handler (result i32 i32 (ref \$ct0)) ;; error: handler block has too many results (local.get \$p) (resume \$ct0 (on \$t \$handler)) (return) ) (unreachable) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:700 assert_invalid( () => instantiate(`(module (type \$ft0 (func)) (type \$ct0 (cont \$ft0)) (tag \$t (param i32)) (func \$error (param \$p (ref \$ct0)) (block \$handler (result i64 (ref \$ct0)) ;; error: type mismatch in non-continuation handler result type (local.get \$p) (resume \$ct0 (on \$t \$handler)) (return) ) (unreachable) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:721 assert_invalid( () => instantiate(`(module (type \$ft0 (func)) (type \$ct0 (cont \$ft0)) (type \$ft1 (func (param i32))) (type \$ct1 (cont \$ft1)) (tag \$t (param i32)) (func \$error (param \$p (ref \$ct0)) (block \$handler (result i32 (ref \$ct1)) ;; error: type mismatch in continuation handler result type (local.get \$p) (resume \$ct0 (on \$t \$handler)) (return) ) (unreachable) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:749 let $4 = instantiate(`(module (tag \$t (param i64 i32) (result i32 i64)) (func \$test (result i32 i64) (i64.const 123) (i32.const 123) (suspend \$t) ) )`); // ./test/core/stack-switching/validation.wast:760 assert_invalid( () => instantiate(`(module (tag \$t (param i64 i32) (result i32 i64)) (func \$test (result i32 i64) ;; error: Insufficient arguments:: ;; This is really testing the general application of arguments to instructions, ;; but trying to trick parsers of the folded form (suspend \$t (i64.const 123)) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:775 assert_invalid( () => instantiate(`(module (tag \$t (param i32) (result i32 i64)) (func \$test (result i32 i64) ;; error: Too many arguments: ;; This is really testing the general application of arguments to instructions, ;; but trying to trick parsers of the folded form (suspend \$t (i64.const 123) (i32.const 123)) ) )`), `type mismatch`, ); // ./test/core/stack-switching/validation.wast:790 assert_invalid( () => instantiate(`(module (func \$test ;; error: Tag does not exist (suspend 0) ) )`), `unknown tag`, ); // ./test/core/stack-switching/validation.wast:803 assert_invalid( () => instantiate(`(module (func (drop (ref.test contref (unreachable)))) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:809 assert_invalid( () => instantiate(`(module (func (drop (ref.test nullcontref (unreachable)))) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:815 assert_invalid( () => instantiate(`(module (type \$f (func)) (type \$c (cont \$f)) (func (drop (ref.test (ref \$c) (unreachable)))) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:824 assert_invalid( () => instantiate(`(module (func (drop (ref.cast contref (unreachable)))) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:830 assert_invalid( () => instantiate(`(module (func (drop (ref.cast nullcontref (unreachable)))) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:836 assert_invalid( () => instantiate(`(module (type \$f (func)) (type \$c (cont \$f)) (func (drop (ref.cast (ref \$c) (unreachable)))) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:845 assert_invalid( () => instantiate(`(module (func (block (result contref) (br_on_cast 0 contref contref (unreachable))) (drop) ) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:854 assert_invalid( () => instantiate(`(module (func (block (result contref) (br_on_cast 0 nullcontref nullcontref (unreachable))) (drop) ) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:863 assert_invalid( () => instantiate(`(module (type \$f (func)) (type \$c (cont \$f)) (func (block (result contref) (br_on_cast 0 (ref \$c) (ref \$c) (unreachable))) (drop) ) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:875 assert_invalid( () => instantiate(`(module (func (block (result contref) (br_on_cast_fail 0 contref contref (unreachable))) (drop) ) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:884 assert_invalid( () => instantiate(`(module (func (block (result contref) (br_on_cast_fail 0 nullcontref nullcontref (unreachable))) (drop) ) )`), `invalid cast`, ); // ./test/core/stack-switching/validation.wast:893 assert_invalid( () => instantiate(`(module (type \$f (func)) (type \$c (cont \$f)) (func (block (result contref) (br_on_cast_fail 0 (ref \$c) (ref \$c) (unreachable))) (drop) ) )`), `invalid cast`, );