1;;! multi_memory = true
2
3;; Returning an unaligned utf16 string is invalid
4(component definition $A
5  (core module $m
6    (memory (export "m") 1)
7    (func (export "f") (result i32)
8      (i32.store (i32.const 4) (i32.const 1))
9      (i32.store (i32.const 8) (i32.const 0))
10      i32.const 4
11    )
12  )
13  (core instance $m (instantiate $m))
14  (func (export "f1") (result string)
15    (canon lift (core func $m "f") (memory $m "m") string-encoding=utf16))
16  (func (export "f2") (result string)
17    (canon lift (core func $m "f") (memory $m "m") string-encoding=latin1+utf16))
18
19)
20(component instance $A $A)
21(assert_trap (invoke "f1") "string pointer not aligned to 2")
22(component instance $A $A)
23(assert_trap (invoke "f2") "string pointer not aligned to 2")
24
25;; utf8 -> utf16 -- when shrinking memory it must be aligned
26(component
27  (component $c
28    (core module $m
29      (func (export "") (param i32 i32) unreachable)
30      (func (export "realloc") (param $old_ptr i32) (param $old_size i32)
31                               (param $align i32) (param $new_size i32) (result i32)
32        (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
33        (if (result i32) (i32.eqz (local.get $old_ptr))
34          (then (i32.const 2)) ;; first allocation aligned
35          (else (i32.const 3)) ;; second allocation unaligned
36        )
37      )
38      (memory (export "memory") 1)
39    )
40    (core instance $m (instantiate $m))
41    (func (export "a") (param "a" string)
42      (canon lift
43        (core func $m "")
44        (realloc (func $m "realloc"))
45        (memory $m "memory")
46        string-encoding=utf16)
47    )
48  )
49
50  (component $c2
51    (import "a" (func $f (param "a" string)))
52    (core module $libc
53      (memory (export "memory") 1)
54      ;; "àà" is  2 UTF-16 code units (4 bytes), and 4 bytes in UTF-8
55      ;; Pessimistic alloc = 4 * 2 = 8 bytes, shrinks to 4 bytes after.
56      (data (memory 0) (i32.const 0) "àà")
57    )
58    (core instance $libc (instantiate $libc))
59    (core func $f (canon lower (func $f) string-encoding=utf8 (memory $libc "memory")))
60    (core module $m
61      (import "" "" (func $f (param i32 i32)))
62      (func (export "f") (call $f (i32.const 0) (i32.const 4)))
63    )
64    (core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
65    (func (export "f") (canon lift (core func $m "f")))
66  )
67
68  (instance $c (instantiate $c))
69  (instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
70  (export "f" (func $c2 "f"))
71)
72
73(assert_trap (invoke "f") "unaligned pointer")
74
75;; utf16 -> latin1+utf16 -- when shrinking memory it must be aligned
76(component
77  (component $c
78    (core module $m
79      (func (export "") (param i32 i32))
80      (func (export "realloc") (param $old_ptr i32) (param $old_size i32)
81                               (param $align i32) (param $new_size i32) (result i32)
82        (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
83        (if (result i32) (i32.eqz (local.get $old_ptr))
84          (then (i32.const 2)) ;; first allocation aligned
85          (else (i32.const 3)) ;; second allocation unaligned
86        )
87      )
88      (memory (export "memory") 1)
89    )
90    (core instance $m (instantiate $m))
91    (func (export "a") (param "a" string)
92      (canon lift
93        (core func $m "")
94        (realloc (func $m "realloc"))
95        (memory $m "memory")
96        string-encoding=latin1+utf16)
97    )
98  )
99
100  (component $c2
101    (import "a" (func $f (param "a" string)))
102    (core module $libc
103      (memory (export "memory") 1)
104      ;; "AΣ" in UTF-16: 0x41 0x00 0xA3 0x03 (Σ = U+03A3, not Latin-1)
105      ;; Forces transcoding to take the UTF-16 grow path.
106      (data (memory 0) (i32.const 0) "\41\00\a3\03")
107    )
108    (core instance $libc (instantiate $libc))
109    (core func $f (canon lower (func $f) string-encoding=utf16 (memory $libc "memory")))
110    (core module $m
111      (import "" "" (func $f (param i32 i32)))
112      (func (export "f") (call $f (i32.const 0) (i32.const 2)))
113    )
114    (core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
115    (func (export "f") (canon lift (core func $m "f")))
116  )
117
118  (instance $c (instantiate $c))
119  (instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
120  (export "f" (func $c2 "f"))
121)
122(assert_trap (invoke "f") "unaligned pointer")
123
124;; latin1+utf16 -> latin1+utf16 -- auto-downsize
125(component
126  (component $c
127    (core module $m
128      (func (export "") (param i32 i32) unreachable)
129      (func (export "realloc") (param $old_ptr i32) (param $old_size i32)
130                               (param $align i32) (param $new_size i32) (result i32)
131        (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
132        (if (result i32) (i32.eqz (local.get $old_ptr))
133          (then (i32.const 2)) ;; first allocation aligned
134          (else (i32.const 3)) ;; second allocation unaligned
135        )
136      )
137      (memory (export "memory") 1)
138    )
139    (core instance $m (instantiate $m))
140    (func (export "a") (param "a" string)
141      (canon lift
142        (core func $m "")
143        (realloc (func $m "realloc"))
144        (memory $m "memory")
145        string-encoding=latin1+utf16)
146    )
147  )
148
149  (component $c2
150    (import "a" (func $f (param "a" string)))
151    (core module $libc
152      (memory (export "memory") 1)
153      ;; "AA" in UTF-16: 0x41 0x00 0x41 0x00
154      (data (memory 0) (i32.const 0) "\41\00\41\00")
155    )
156    (core instance $libc (instantiate $libc))
157    (core func $f (canon lower (func $f) string-encoding=latin1+utf16 (memory $libc "memory")))
158    (core module $m
159      (import "" "" (func $f (param i32 i32)))
160      ;; the length here contains `UTF16_TAG` and it's additionally 1 code
161      ;; unit. This is a utf-16 encoded string but during transcoding it'll
162      ;; get shrunk to latin 1
163      (func (export "f") (call $f (i32.const 0) (i32.const 0x8000_0002)))
164    )
165    (core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
166    (func (export "f") (canon lift (core func $m "f")))
167  )
168
169  (instance $c (instantiate $c))
170  (instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
171  (export "f" (func $c2 "f"))
172)
173(assert_trap (invoke "f") "unaligned pointer")
174
175;; utf8 -> latin1+utf16 -- initial encode finishes but needs downsizing
176(component
177  (component $c
178    (core module $m
179      (func (export "") (param i32 i32) unreachable)
180      (func (export "realloc") (param $old_ptr i32) (param $old_size i32)
181                               (param $align i32) (param $new_size i32) (result i32)
182        (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
183        (if (result i32) (i32.eqz (local.get $old_ptr))
184          (then (i32.const 2)) ;; first allocation aligned
185          (else (i32.const 3)) ;; second allocation unaligned
186        )
187      )
188      (memory (export "memory") 1)
189    )
190    (core instance $m (instantiate $m))
191    (func (export "a") (param "a" string)
192      (canon lift
193        (core func $m "")
194        (realloc (func $m "realloc"))
195        (memory $m "memory")
196        string-encoding=latin1+utf16)
197    )
198  )
199
200  (component $c2
201    (import "a" (func $f (param "a" string)))
202    (core module $libc
203      (memory (export "memory") 1)
204      ;; "Ë" in UTF-8 is "\xc3\xab", which is 2 bytes, but in latin1+utf16 it's
205      ;; 1 byte (0xCB). The initial allocation of 2 bytes completes the entire
206      ;; transcode but the final allocation needs to be shrunk to 1 byte.
207      (data (memory 0) (i32.const 0) "Ë")
208    )
209    (core instance $libc (instantiate $libc))
210    (core func $f (canon lower (func $f) (memory $libc "memory")))
211    (core module $m
212      (import "" "" (func $f (param i32 i32)))
213      (func (export "f") (call $f (i32.const 0) (i32.const 2)))
214    )
215    (core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
216    (func (export "f") (canon lift (core func $m "f")))
217  )
218
219  (instance $c (instantiate $c))
220  (instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
221  (export "f" (func $c2 "f"))
222)
223(assert_trap (invoke "f") "unaligned pointer")
224
225;; utf8 -> latin1+utf16
226;;  - first realloc fails to hold latin1
227;;  - second realloc is too big
228;;  - third realloc shrinks
229(component
230  (component $c
231    (core module $m
232      (global $cnt (mut i32) (i32.const 0))
233      (func (export "") (param i32 i32)
234        unreachable
235      )
236      (func (export "realloc") (param $old_ptr i32) (param $old_size i32)
237                               (param $align i32) (param $new_size i32) (result i32)
238        (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
239        (global.set $cnt (i32.add (global.get $cnt) (i32.const 1)))
240
241        ;; first allocation is aligned
242        (if (i32.eq (global.get $cnt) (i32.const 1))
243          (then
244            (if (i32.ne (local.get $old_ptr) (i32.const 0)) (then unreachable))
245            (if (i32.ne (local.get $old_size) (i32.const 0)) (then unreachable))
246            (if (i32.ne (local.get $new_size) (i32.const 5)) (then unreachable))
247            (return (i32.const 2)))
248        )
249        ;; second allocation is aligned
250        (if (i32.eq (global.get $cnt) (i32.const 2))
251          (then
252            (if (i32.ne (local.get $old_ptr) (i32.const 2)) (then unreachable))
253            (if (i32.ne (local.get $old_size) (i32.const 5)) (then unreachable))
254            (if (i32.ne (local.get $new_size) (i32.const 10)) (then unreachable))
255            (return (i32.const 4)))
256        )
257        ;; third allocation is unaligned
258        (if (i32.eq (global.get $cnt) (i32.const 3))
259          (then
260            (if (i32.ne (local.get $old_ptr) (i32.const 4)) (then unreachable))
261            (if (i32.ne (local.get $old_size) (i32.const 10)) (then unreachable))
262            (if (i32.ne (local.get $new_size) (i32.const 4)) (then unreachable))
263            (return (i32.const 3)))
264        )
265
266        unreachable
267      )
268      (memory (export "memory") 1)
269    )
270    (core instance $m (instantiate $m))
271    (func (export "a") (param "a" string)
272      (canon lift
273        (core func $m "")
274        (realloc (func $m "realloc"))
275        (memory $m "memory")
276        string-encoding=latin1+utf16)
277    )
278  )
279
280  (component $c2
281    (import "a" (func $f (param "a" string)))
282    (core module $libc
283      (memory (export "memory") 1)
284      ;; "Ë┛" in UTF-8 is "\xc3\xab\xe2\x8c\x9b", 5 bytes.
285      ;; * First, a 5-byte allocation is made to see if it fits in latin 1.
286      ;; * This fails since "┛" does not fit in latin1. The second allocation
287      ;;   is over-large at 10 bytes (twice the original length).
288      ;; * The string encoded in UTF-16 is "\xcb\x00\x1b%", which is 4 bytes.
289      ;; * The 10-byte allocation is shrunk to 4 bytes, which is what this
290      ;;   test is looking for (proper alignment in the 3rd realloc).
291      (data (memory 0) (i32.const 0) "Ë┛")
292    )
293    (core instance $libc (instantiate $libc))
294    (core func $f (canon lower (func $f) (memory $libc "memory")))
295    (core module $m
296      (import "" "" (func $f (param i32 i32)))
297      (func (export "f") (call $f (i32.const 0) (i32.const 5)))
298    )
299    (core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
300    (func (export "f") (canon lift (core func $m "f")))
301  )
302
303  (instance $c (instantiate $c))
304  (instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
305  (export "f" (func $c2 "f"))
306)
307(assert_trap (invoke "f") "unaligned pointer")
308
309;; utf8 -> latin1+utf16
310;;  - first realloc fails to hold latin1
311;;  - second realloc is out of bounds
312(component
313  (component $c
314    (core module $m
315      (global $cnt (mut i32) (i32.const 0))
316      (func (export "") (param i32 i32)
317        unreachable
318      )
319      (func (export "realloc") (param $old_ptr i32) (param $old_size i32)
320                               (param $align i32) (param $new_size i32) (result i32)
321        (if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
322        (global.set $cnt (i32.add (global.get $cnt) (i32.const 1)))
323
324        ;; first allocation is aligned
325        (if (i32.eq (global.get $cnt) (i32.const 1))
326          (then
327            (if (i32.ne (local.get $old_ptr) (i32.const 0)) (then unreachable))
328            (if (i32.ne (local.get $old_size) (i32.const 0)) (then unreachable))
329            (if (i32.ne (local.get $new_size) (i32.const 5)) (then unreachable))
330            (return (i32.const 2)))
331        )
332        ;; second allocation is out of bounds
333        (if (i32.eq (global.get $cnt) (i32.const 2))
334          (then
335            (if (i32.ne (local.get $old_ptr) (i32.const 2)) (then unreachable))
336            (if (i32.ne (local.get $old_size) (i32.const 5)) (then unreachable))
337            (if (i32.ne (local.get $new_size) (i32.const 10)) (then unreachable))
338            (return (i32.const -2)))
339        )
340
341        unreachable
342      )
343      (memory (export "memory") 1)
344    )
345    (core instance $m (instantiate $m))
346    (func (export "a") (param "a" string)
347      (canon lift
348        (core func $m "")
349        (realloc (func $m "realloc"))
350        (memory $m "memory")
351        string-encoding=latin1+utf16)
352    )
353  )
354
355  (component $c2
356    (import "a" (func $f (param "a" string)))
357    (core module $libc
358      (memory (export "memory") 1)
359      ;; "Ë┛" in UTF-8 is "\xc3\xab\xe2\x8c\x9b", 5 bytes.
360      ;; * First, a 5-byte allocation is made to see if it fits in latin 1.
361      ;; * This fails since "┛" does not fit in latin1. The second allocation
362      ;;   is then out of bounds and should trap
363      (data (memory 0) (i32.const 0) "Ë┛")
364    )
365    (core instance $libc (instantiate $libc))
366    (core func $f (canon lower (func $f) (memory $libc "memory")))
367    (core module $m
368      (import "" "" (func $f (param i32 i32)))
369      (func (export "f") (call $f (i32.const 0) (i32.const 5)))
370    )
371    (core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
372    (func (export "f") (canon lift (core func $m "f")))
373  )
374
375  (instance $c (instantiate $c))
376  (instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
377  (export "f" (func $c2 "f"))
378)
379(assert_trap (invoke "f") "string content out-of-bounds")
380