;;! multi_memory = true
;; Returning an unaligned utf16 string is invalid
(component definition $A
(core module $m
(memory (export "m") 1)
(func (export "f") (result i32)
(i32.store (i32.const 4) (i32.const 1))
(i32.store (i32.const 8) (i32.const 0))
i32.const 4
)
)
(core instance $m (instantiate $m))
(func (export "f1") (result string)
(canon lift (core func $m "f") (memory $m "m") string-encoding=utf16))
(func (export "f2") (result string)
(canon lift (core func $m "f") (memory $m "m") string-encoding=latin1+utf16))
)
(component instance $A $A)
(assert_trap (invoke "f1") "string pointer not aligned to 2")
(component instance $A $A)
(assert_trap (invoke "f2") "string pointer not aligned to 2")
;; utf8 -> utf16 -- when shrinking memory it must be aligned
(component
(component $c
(core module $m
(func (export "") (param i32 i32) unreachable)
(func (export "realloc") (param $old_ptr i32) (param $old_size i32)
(param $align i32) (param $new_size i32) (result i32)
(if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
(if (result i32) (i32.eqz (local.get $old_ptr))
(then (i32.const 2)) ;; first allocation aligned
(else (i32.const 3)) ;; second allocation unaligned
)
)
(memory (export "memory") 1)
)
(core instance $m (instantiate $m))
(func (export "a") (param "a" string)
(canon lift
(core func $m "")
(realloc (func $m "realloc"))
(memory $m "memory")
string-encoding=utf16)
)
)
(component $c2
(import "a" (func $f (param "a" string)))
(core module $libc
(memory (export "memory") 1)
;; "àà" is 2 UTF-16 code units (4 bytes), and 4 bytes in UTF-8
;; Pessimistic alloc = 4 * 2 = 8 bytes, shrinks to 4 bytes after.
(data (memory 0) (i32.const 0) "àà")
)
(core instance $libc (instantiate $libc))
(core func $f (canon lower (func $f) string-encoding=utf8 (memory $libc "memory")))
(core module $m
(import "" "" (func $f (param i32 i32)))
(func (export "f") (call $f (i32.const 0) (i32.const 4)))
)
(core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
(func (export "f") (canon lift (core func $m "f")))
)
(instance $c (instantiate $c))
(instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
(export "f" (func $c2 "f"))
)
(assert_trap (invoke "f") "unaligned pointer")
;; utf16 -> latin1+utf16 -- when shrinking memory it must be aligned
(component
(component $c
(core module $m
(func (export "") (param i32 i32))
(func (export "realloc") (param $old_ptr i32) (param $old_size i32)
(param $align i32) (param $new_size i32) (result i32)
(if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
(if (result i32) (i32.eqz (local.get $old_ptr))
(then (i32.const 2)) ;; first allocation aligned
(else (i32.const 3)) ;; second allocation unaligned
)
)
(memory (export "memory") 1)
)
(core instance $m (instantiate $m))
(func (export "a") (param "a" string)
(canon lift
(core func $m "")
(realloc (func $m "realloc"))
(memory $m "memory")
string-encoding=latin1+utf16)
)
)
(component $c2
(import "a" (func $f (param "a" string)))
(core module $libc
(memory (export "memory") 1)
;; "AΣ" in UTF-16: 0x41 0x00 0xA3 0x03 (Σ = U+03A3, not Latin-1)
;; Forces transcoding to take the UTF-16 grow path.
(data (memory 0) (i32.const 0) "\41\00\a3\03")
)
(core instance $libc (instantiate $libc))
(core func $f (canon lower (func $f) string-encoding=utf16 (memory $libc "memory")))
(core module $m
(import "" "" (func $f (param i32 i32)))
(func (export "f") (call $f (i32.const 0) (i32.const 2)))
)
(core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
(func (export "f") (canon lift (core func $m "f")))
)
(instance $c (instantiate $c))
(instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
(export "f" (func $c2 "f"))
)
(assert_trap (invoke "f") "unaligned pointer")
;; latin1+utf16 -> latin1+utf16 -- auto-downsize
(component
(component $c
(core module $m
(func (export "") (param i32 i32) unreachable)
(func (export "realloc") (param $old_ptr i32) (param $old_size i32)
(param $align i32) (param $new_size i32) (result i32)
(if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
(if (result i32) (i32.eqz (local.get $old_ptr))
(then (i32.const 2)) ;; first allocation aligned
(else (i32.const 3)) ;; second allocation unaligned
)
)
(memory (export "memory") 1)
)
(core instance $m (instantiate $m))
(func (export "a") (param "a" string)
(canon lift
(core func $m "")
(realloc (func $m "realloc"))
(memory $m "memory")
string-encoding=latin1+utf16)
)
)
(component $c2
(import "a" (func $f (param "a" string)))
(core module $libc
(memory (export "memory") 1)
;; "AA" in UTF-16: 0x41 0x00 0x41 0x00
(data (memory 0) (i32.const 0) "\41\00\41\00")
)
(core instance $libc (instantiate $libc))
(core func $f (canon lower (func $f) string-encoding=latin1+utf16 (memory $libc "memory")))
(core module $m
(import "" "" (func $f (param i32 i32)))
;; the length here contains `UTF16_TAG` and it's additionally 1 code
;; unit. This is a utf-16 encoded string but during transcoding it'll
;; get shrunk to latin 1
(func (export "f") (call $f (i32.const 0) (i32.const 0x8000_0002)))
)
(core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
(func (export "f") (canon lift (core func $m "f")))
)
(instance $c (instantiate $c))
(instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
(export "f" (func $c2 "f"))
)
(assert_trap (invoke "f") "unaligned pointer")
;; utf8 -> latin1+utf16 -- initial encode finishes but needs downsizing
(component
(component $c
(core module $m
(func (export "") (param i32 i32) unreachable)
(func (export "realloc") (param $old_ptr i32) (param $old_size i32)
(param $align i32) (param $new_size i32) (result i32)
(if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
(if (result i32) (i32.eqz (local.get $old_ptr))
(then (i32.const 2)) ;; first allocation aligned
(else (i32.const 3)) ;; second allocation unaligned
)
)
(memory (export "memory") 1)
)
(core instance $m (instantiate $m))
(func (export "a") (param "a" string)
(canon lift
(core func $m "")
(realloc (func $m "realloc"))
(memory $m "memory")
string-encoding=latin1+utf16)
)
)
(component $c2
(import "a" (func $f (param "a" string)))
(core module $libc
(memory (export "memory") 1)
;; "Ë" in UTF-8 is "\xc3\xab", which is 2 bytes, but in latin1+utf16 it's
;; 1 byte (0xCB). The initial allocation of 2 bytes completes the entire
;; transcode but the final allocation needs to be shrunk to 1 byte.
(data (memory 0) (i32.const 0) "Ë")
)
(core instance $libc (instantiate $libc))
(core func $f (canon lower (func $f) (memory $libc "memory")))
(core module $m
(import "" "" (func $f (param i32 i32)))
(func (export "f") (call $f (i32.const 0) (i32.const 2)))
)
(core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
(func (export "f") (canon lift (core func $m "f")))
)
(instance $c (instantiate $c))
(instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
(export "f" (func $c2 "f"))
)
(assert_trap (invoke "f") "unaligned pointer")
;; utf8 -> latin1+utf16
;; - first realloc fails to hold latin1
;; - second realloc is too big
;; - third realloc shrinks
(component
(component $c
(core module $m
(global $cnt (mut i32) (i32.const 0))
(func (export "") (param i32 i32)
unreachable
)
(func (export "realloc") (param $old_ptr i32) (param $old_size i32)
(param $align i32) (param $new_size i32) (result i32)
(if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
(global.set $cnt (i32.add (global.get $cnt) (i32.const 1)))
;; first allocation is aligned
(if (i32.eq (global.get $cnt) (i32.const 1))
(then
(if (i32.ne (local.get $old_ptr) (i32.const 0)) (then unreachable))
(if (i32.ne (local.get $old_size) (i32.const 0)) (then unreachable))
(if (i32.ne (local.get $new_size) (i32.const 5)) (then unreachable))
(return (i32.const 2)))
)
;; second allocation is aligned
(if (i32.eq (global.get $cnt) (i32.const 2))
(then
(if (i32.ne (local.get $old_ptr) (i32.const 2)) (then unreachable))
(if (i32.ne (local.get $old_size) (i32.const 5)) (then unreachable))
(if (i32.ne (local.get $new_size) (i32.const 10)) (then unreachable))
(return (i32.const 4)))
)
;; third allocation is unaligned
(if (i32.eq (global.get $cnt) (i32.const 3))
(then
(if (i32.ne (local.get $old_ptr) (i32.const 4)) (then unreachable))
(if (i32.ne (local.get $old_size) (i32.const 10)) (then unreachable))
(if (i32.ne (local.get $new_size) (i32.const 4)) (then unreachable))
(return (i32.const 3)))
)
unreachable
)
(memory (export "memory") 1)
)
(core instance $m (instantiate $m))
(func (export "a") (param "a" string)
(canon lift
(core func $m "")
(realloc (func $m "realloc"))
(memory $m "memory")
string-encoding=latin1+utf16)
)
)
(component $c2
(import "a" (func $f (param "a" string)))
(core module $libc
(memory (export "memory") 1)
;; "Ë┛" in UTF-8 is "\xc3\xab\xe2\x8c\x9b", 5 bytes.
;; * First, a 5-byte allocation is made to see if it fits in latin 1.
;; * This fails since "┛" does not fit in latin1. The second allocation
;; is over-large at 10 bytes (twice the original length).
;; * The string encoded in UTF-16 is "\xcb\x00\x1b%", which is 4 bytes.
;; * The 10-byte allocation is shrunk to 4 bytes, which is what this
;; test is looking for (proper alignment in the 3rd realloc).
(data (memory 0) (i32.const 0) "Ë┛")
)
(core instance $libc (instantiate $libc))
(core func $f (canon lower (func $f) (memory $libc "memory")))
(core module $m
(import "" "" (func $f (param i32 i32)))
(func (export "f") (call $f (i32.const 0) (i32.const 5)))
)
(core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
(func (export "f") (canon lift (core func $m "f")))
)
(instance $c (instantiate $c))
(instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
(export "f" (func $c2 "f"))
)
(assert_trap (invoke "f") "unaligned pointer")
;; utf8 -> latin1+utf16
;; - first realloc fails to hold latin1
;; - second realloc is out of bounds
(component
(component $c
(core module $m
(global $cnt (mut i32) (i32.const 0))
(func (export "") (param i32 i32)
unreachable
)
(func (export "realloc") (param $old_ptr i32) (param $old_size i32)
(param $align i32) (param $new_size i32) (result i32)
(if (i32.ne (local.get $align) (i32.const 2)) (then unreachable))
(global.set $cnt (i32.add (global.get $cnt) (i32.const 1)))
;; first allocation is aligned
(if (i32.eq (global.get $cnt) (i32.const 1))
(then
(if (i32.ne (local.get $old_ptr) (i32.const 0)) (then unreachable))
(if (i32.ne (local.get $old_size) (i32.const 0)) (then unreachable))
(if (i32.ne (local.get $new_size) (i32.const 5)) (then unreachable))
(return (i32.const 2)))
)
;; second allocation is out of bounds
(if (i32.eq (global.get $cnt) (i32.const 2))
(then
(if (i32.ne (local.get $old_ptr) (i32.const 2)) (then unreachable))
(if (i32.ne (local.get $old_size) (i32.const 5)) (then unreachable))
(if (i32.ne (local.get $new_size) (i32.const 10)) (then unreachable))
(return (i32.const -2)))
)
unreachable
)
(memory (export "memory") 1)
)
(core instance $m (instantiate $m))
(func (export "a") (param "a" string)
(canon lift
(core func $m "")
(realloc (func $m "realloc"))
(memory $m "memory")
string-encoding=latin1+utf16)
)
)
(component $c2
(import "a" (func $f (param "a" string)))
(core module $libc
(memory (export "memory") 1)
;; "Ë┛" in UTF-8 is "\xc3\xab\xe2\x8c\x9b", 5 bytes.
;; * First, a 5-byte allocation is made to see if it fits in latin 1.
;; * This fails since "┛" does not fit in latin1. The second allocation
;; is then out of bounds and should trap
(data (memory 0) (i32.const 0) "Ë┛")
)
(core instance $libc (instantiate $libc))
(core func $f (canon lower (func $f) (memory $libc "memory")))
(core module $m
(import "" "" (func $f (param i32 i32)))
(func (export "f") (call $f (i32.const 0) (i32.const 5)))
)
(core instance $m (instantiate $m (with "" (instance (export "" (func $f))))))
(func (export "f") (canon lift (core func $m "f")))
)
(instance $c (instantiate $c))
(instance $c2 (instantiate $c2 (with "a" (func $c "a"))))
(export "f" (func $c2 "f"))
)
(assert_trap (invoke "f") "string content out-of-bounds")