wasmtime-cli 45.0.0

Command-line interface for Wasmtime
Documentation
;;! 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")