1;;! component_model_async = true
2;;! reference_types = true
3;;! multi_memory = true
4;;! gc_types = true
5
6;; This test has two components $C and $D, where $D imports and calls $C.transform
7;;  $C.transform takes and returns a stream<u8>
8;;  Before $C.transform blocks the first time, it supplies a 12-byte read buffer
9;;  When $D.run regains control after $C.transform blocks, it can perform multiple
10;;   successful writes until it fully uses up the 12-byte buffer.
11;;   ... and that's where I am so far ...
12;;
13;; (Copied from
14;; https://github.com/WebAssembly/component-model/blob/add-tests/test/concurrency/partial-stream-copies.wast)
15(component
16  (component $C
17    (core module $Memory (memory (export "mem") 1))
18    (core instance $memory (instantiate $Memory))
19    (core module $CM
20      (import "" "mem" (memory 1))
21      (import "" "task.return" (func $task.return (param i32)))
22      (import "" "waitable.join" (func $waitable.join (param i32 i32)))
23      (import "" "waitable-set.new" (func $waitable-set.new (result i32)))
24      (import "" "stream.new" (func $stream.new (result i64)))
25      (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
26      (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
27      (import "" "stream.drop-readable" (func $stream.drop-readable (param i32)))
28      (import "" "stream.drop-writable" (func $stream.drop-writable (param i32)))
29
30      ;; $ws is waited on by 'transform'
31      (global $ws (mut i32) (i32.const 0))
32      (func $start (global.set $ws (call $waitable-set.new)))
33      (start $start)
34
35      ;; $insr/$outsw are read/written by 'transform'
36      (global $insr (mut i32) (i32.const 0))
37      (global $inbufp (mut i32) (i32.const 0x10))
38      (global $outsw (mut i32) (i32.const 0))
39      (global $outbufp (mut i32) (i32.const 0x20))
40
41      (func $transform (export "transform") (param i32) (result i32)
42        (local $ret i32) (local $ret64 i64) (local $outsr i32)
43
44        ;; check the incoming readable stream end
45        (global.set $insr (local.get 0))
46        (if (i32.ne (i32.const 2) (global.get $insr))
47          (then unreachable))
48
49        ;; create a new stream r/w pair $outsr/$outsw
50        (local.set $ret64 (call $stream.new))
51        (local.set $outsr (i32.wrap_i64 (local.get $ret64)))
52        (if (i32.ne (i32.const 3) (local.get $outsr))
53          (then unreachable))
54        (global.set $outsw (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32))))
55        (if (i32.ne (i32.const 4) (global.get $outsw))
56          (then unreachable))
57
58        ;; start async read on $insr which will block
59        (local.set $ret (call $stream.read (global.get $insr) (global.get $inbufp) (i32.const 12)))
60        (if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret))
61          (then unreachable))
62
63        ;; return the readable end of the outgoing stream to the caller
64        (call $task.return (local.get $outsr))
65
66        ;; wait for the stream.read/write to complete
67        (call $waitable.join (global.get $insr) (global.get $ws))
68        (call $waitable.join (global.get $outsw) (global.get $ws))
69        (i32.or (i32.const 2 (; WAIT ;)) (i32.shl (global.get $ws) (i32.const 4)))
70      )
71      (func $transform_cb (export "transform_cb") (param $event_code i32) (param $index i32) (param $payload i32) (result i32)
72        (local $ret i32) (local $ret64 i64)
73
74        ;; confirm the read succeeded fully
75        (if (i32.ne (local.get $event_code) (i32.const 2 (; STREAM_READ ;)))
76          (then unreachable))
77        (if (i32.ne (local.get $index) (global.get $insr))
78          (then unreachable))
79        (if (i32.ne (local.get $payload) (i32.const 0xc0 (; COMPLETED=0 | (12 << 4) ;)))
80          (then unreachable))
81        (if (i32.ne (i32.const 0x89abcdef) (i32.load offset=0 (global.get $inbufp)))
82          (then unreachable))
83        (if (i32.ne (i32.const 0x01234567) (i32.load offset=4 (global.get $inbufp)))
84          (then unreachable))
85        (if (i32.ne (i32.const 0x89abcdef) (i32.load offset=8 (global.get $inbufp)))
86          (then unreachable))
87
88        ;; multiple read calls succeed until 12-byte buffer is consumed
89        (local.set $ret (call $stream.read (global.get $insr) (global.get $inbufp) (i32.const 4)))
90        (if (i32.ne (i32.const 0x40) (local.get $ret))
91          (then unreachable))
92        (if (i32.ne (i32.const 0x76543210) (i32.load (global.get $inbufp)))
93          (then unreachable))
94        (local.set $ret (call $stream.read (global.get $insr) (global.get $inbufp) (i32.const 2)))
95        (if (i32.ne (i32.const 0x20) (local.get $ret))
96          (then unreachable))
97        (if (i32.ne (i32.const 0xba98) (i32.load16_u (global.get $inbufp)))
98          (then unreachable))
99        (local.set $ret (call $stream.read (global.get $insr) (global.get $inbufp) (i32.const 8)))
100        (if (i32.ne (i32.const 0x60) (local.get $ret))
101          (then unreachable))
102        (if (i32.ne (i32.const 0x3210fedc) (i32.load (global.get $inbufp)))
103          (then unreachable))
104        (if (i32.ne (i32.const 0x7654) (i32.load16_u offset=4 (global.get $inbufp)))
105          (then unreachable))
106
107        (call $stream.drop-readable (global.get $insr))
108        (call $stream.drop-writable (global.get $outsw))
109        (return (i32.const 0 (; EXIT ;)))
110      )
111    )
112    (type $ST (stream u8))
113    (canon task.return (result $ST) (memory $memory "mem") (core func $task.return))
114    (canon waitable.join (core func $waitable.join))
115    (canon waitable-set.new (core func $waitable-set.new))
116    (canon stream.new $ST (core func $stream.new))
117    (canon stream.read $ST async (memory $memory "mem") (core func $stream.read))
118    (canon stream.write $ST async (memory $memory "mem") (core func $stream.write))
119    (canon stream.drop-readable $ST (core func $stream.drop-readable))
120    (canon stream.drop-writable $ST (core func $stream.drop-writable))
121    (core instance $cm (instantiate $CM (with "" (instance
122      (export "mem" (memory $memory "mem"))
123      (export "task.return" (func $task.return))
124      (export "waitable.join" (func $waitable.join))
125      (export "waitable-set.new" (func $waitable-set.new))
126      (export "stream.new" (func $stream.new))
127      (export "stream.read" (func $stream.read))
128      (export "stream.write" (func $stream.write))
129      (export "stream.drop-readable" (func $stream.drop-readable))
130      (export "stream.drop-writable" (func $stream.drop-writable))
131    ))))
132    (func (export "transform") (param "in" (stream u8)) (result (stream u8)) (canon lift
133      (core func $cm "transform")
134      async (memory $memory "mem") (callback (func $cm "transform_cb"))
135    ))
136  )
137
138  (component $D
139    (import "transform" (func $transform (param "in" (stream u8)) (result (stream u8))))
140
141    (core module $Memory (memory (export "mem") 1))
142    (core instance $memory (instantiate $Memory))
143    (core module $DM
144      (import "" "mem" (memory 1))
145      (import "" "waitable.join" (func $waitable.join (param i32 i32)))
146      (import "" "waitable-set.new" (func $waitable-set.new (result i32)))
147      (import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))
148      (import "" "stream.new" (func $stream.new (result i64)))
149      (import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
150      (import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
151      (import "" "stream.drop-readable" (func $stream.drop-readable (param i32)))
152      (import "" "stream.drop-writable" (func $stream.drop-writable (param i32)))
153      (import "" "transform" (func $transform (param i32 i32) (result i32)))
154
155      (func $run (export "run") (result i32)
156        (local $ret i32) (local $ret64 i64) (local $retp i32)
157        (local $insr i32) (local $insw i32) (local $outsr i32)
158        (local $subtask i32)
159        (local $ws i32)
160
161        ;; create a new stream r/w pair $insr/$insw
162        (local.set $ret64 (call $stream.new))
163        (local.set $insr (i32.wrap_i64 (local.get $ret64)))
164        (if (i32.ne (i32.const 1) (local.get $insr))
165          (then unreachable))
166        (local.set $insw (i32.wrap_i64 (i64.shr_u (local.get $ret64) (i64.const 32))))
167        (if (i32.ne (i32.const 2) (local.get $insw))
168          (then unreachable))
169
170        ;; call 'transform' which will return a readable stream $outsr eagerly
171        (local.set $retp (i32.const 8))
172        (local.set $ret (call $transform (local.get $insr) (local.get $retp)))
173        (if (i32.ne (i32.const 2 (; RETURNED=2 | (0<<4) ;)) (local.get $ret))
174          (then unreachable))
175        (local.set $outsr (i32.load (local.get $retp)))
176        (if (i32.ne (i32.const 1) (local.get $outsr))
177          (then unreachable))
178
179        ;; multiple write calls succeed until 12-byte buffer is filled
180        (i64.store (i32.const 16) (i64.const 0x0123456789abcdef))
181        (local.set $ret (call $stream.write (local.get $insw) (i32.const 16) (i32.const 8)))
182        (if (i32.ne (i32.const 0x80) (local.get $ret))
183          (then unreachable))
184        (local.set $ret (call $stream.write (local.get $insw) (i32.const 16) (i32.const 8)))
185        (if (i32.ne (i32.const 0x40) (local.get $ret))
186          (then unreachable))
187
188        ;; start a blocking write with a 12-byte buffer
189        (i64.store (i32.const 16) (i64.const 0xfedcba9876543210))
190        (i32.store (i32.const 24) (i32.const 0x76543210))
191        (local.set $ret (call $stream.write (local.get $insw) (i32.const 16) (i32.const 12)))
192        (if (i32.ne (i32.const -1 (; BLOCKED ;)) (local.get $ret))
193          (then unreachable))
194
195        ;; wait for transform to read our write and drop all the streams
196        (local.set $ws (call $waitable-set.new))
197        (call $waitable.join (local.get $insw) (local.get $ws))
198        (local.set $ret (call $waitable-set.wait (local.get $ws) (i32.const 0)))
199
200        ;; confirm the write and the dropped stream
201        (if (i32.ne (i32.const 3 (; STREAM_WRITE ;)) (local.get $ret))
202          (then unreachable))
203        (if (i32.ne (local.get $insw) (i32.load (i32.const 0)))
204          (then unreachable))
205        (if (i32.ne (i32.const 0xc1 (; DROPPED=1 | (12 << 4) ;) (; TODO: currently returns 0xc0 ;)) (i32.load (i32.const 4)))
206          (then unreachable))
207
208        (call $stream.drop-writable (local.get $insw))
209        (call $stream.drop-readable (local.get $outsr))
210
211        ;; return 42 to the top-level test harness
212        (i32.const 42)
213      )
214    )
215    (type $ST (stream u8))
216    (canon waitable.join (core func $waitable.join))
217    (canon waitable-set.new (core func $waitable-set.new))
218    (canon waitable-set.wait (memory $memory "mem") (core func $waitable-set.wait))
219    (canon stream.new $ST (core func $stream.new))
220    (canon stream.read $ST async (memory $memory "mem") (core func $stream.read))
221    (canon stream.write $ST async (memory $memory "mem") (core func $stream.write))
222    (canon stream.drop-readable $ST (core func $stream.drop-readable))
223    (canon stream.drop-writable $ST (core func $stream.drop-writable))
224    (canon lower (func $transform) async (memory $memory "mem") (core func $transform'))
225    (core instance $dm (instantiate $DM (with "" (instance
226      (export "mem" (memory $memory "mem"))
227      (export "waitable.join" (func $waitable.join))
228      (export "waitable-set.new" (func $waitable-set.new))
229      (export "waitable-set.wait" (func $waitable-set.wait))
230      (export "stream.new" (func $stream.new))
231      (export "stream.read" (func $stream.read))
232      (export "stream.write" (func $stream.write))
233      (export "stream.drop-readable" (func $stream.drop-readable))
234      (export "stream.drop-writable" (func $stream.drop-writable))
235      (export "transform" (func $transform'))
236    ))))
237    (func (export "run") async (result u32) (canon lift (core func $dm "run")))
238  )
239
240  (instance $c (instantiate $C))
241  (instance $d (instantiate $D (with "transform" (func $c "transform"))))
242  (func (export "run") (alias export $d "run"))
243)
244(assert_return (invoke "run") (u32.const 42))
245