(module
 (type $struct (sub (struct (mut i32))))
 (type $extendedstruct (sub $struct (struct (mut i32) f64)))
 (type $bytes (array (mut i8)))

 (type $void_func (func))
 (type $int_func (func (result i32)))

 (import "fuzzing-support" "log-i32" (func $log (param i32)))

 (func $structs (export "structs")
  (local $x (ref null $struct))
  (local $y (ref null $struct))
  (local.set $x
   (struct.new_default $struct)
  )
  ;; The value is initialized to 0
  ;; Note: We cannot optimize these to constants without either immutability or
  ;; some kind of escape analysis (to verify that the GC data referred to is not
  ;; written to elsewhere).
  (call $log
   (struct.get $struct 0 (local.get $x))
  )
  ;; Assigning a value works
  (struct.set $struct 0
   (local.get $x)
   (i32.const 42)
  )
  (call $log
   (struct.get $struct 0 (local.get $x))
  )
  ;; References are references, so writing to one's value affects the other's
  (local.set $y (local.get $x))
  (struct.set $struct 0
   (local.get $y)
   (i32.const 100)
  )
  (call $log
   (struct.get $struct 0 (local.get $x))
  )
  (call $log
   (struct.get $struct 0 (local.get $y))
  )
 )
 (func $arrays (export "arrays")
  (local $x (ref null $bytes))
  (local.set $x
   (array.new $bytes
    (i32.const 42) ;; value to splat into the array
    (i32.const 50) ;; size
   )
  )
  ;; The length should be 50
  (call $log
   (array.len (local.get $x))
  )
  ;; The value should be 42
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 10))
  )
  ;; Write a value that will be truncated into an i8
  (array.set $bytes (local.get $x) (i32.const 10) (i32.const 0xff80))
  ;; The value should be 0x80 (-128 or 128 depending on signed/unsigned)
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 10))
  )
  (call $log
   (array.get_s $bytes (local.get $x) (i32.const 10))
  )
  ;; Other items than the one at index 10 are unaffected.
  (call $log
   (array.get_s $bytes (local.get $x) (i32.const 20))
  )
 )
 (func $br_on_cast (export "br_on_cast")
  (local $any anyref)
  ;; create a simple $struct, store it in an anyref
  (local.set $any
   (struct.new_default $struct)
  )
  (drop
   (block $block (result (ref $struct))
    (drop
     (block $extendedblock (result (ref $extendedstruct))
      (drop
       ;; second, try to cast our simple $struct to what it is, which will work
       (br_on_cast $block anyref (ref $struct)
        ;; first, try to cast our simple $struct to an extended, which will fail
        (br_on_cast $extendedblock anyref (ref $extendedstruct)
         (local.get $any)
        )
       )
      )
      (call $log (i32.const -1)) ;; we should never get here
      (return)
     )
    )
    (call $log (i32.const -2)) ;; we should never get here either
    (return)
   )
  )
  (call $log (i32.const 3)) ;; we should get here
 )
 (func $br_on_failed_cast-1 (export "br_on_failed_cast-1")
  (local $any anyref)
  ;; create a simple $struct, store it in an anyref
  (local.set $any
   (struct.new_default $struct)
  )
  (drop
   (block $any (result (ref null any))
    (call $log (i32.const 1))
    (drop
     ;; try to cast our simple $struct to an extended, which will fail, and
     ;; so we will branch, skipping the next logging.
     (br_on_cast_fail $any anyref (ref $extendedstruct)
      (local.get $any)
     )
    )
    (call $log (i32.const 999)) ;; we should skip this
    (ref.null any)
   )
  )
 )
 (func $br_on_failed_cast-2 (export "br_on_failed_cast-2")
  (local $any anyref)
  ;; create an $extendedstruct, store it in an anyref
  (local.set $any
   (struct.new_default $extendedstruct)
  )
  (drop
   (block $any (result (ref null any))
    (call $log (i32.const 1))
    (drop
     ;; try to cast our simple $struct to an extended, which will succeed, and
     ;; so we will continue to the next logging.
     (br_on_cast_fail $any anyref (ref $extendedstruct)
      (local.get $any)
     )
    )
    (call $log (i32.const 999))
    (ref.null any)
   )
  )
 )
 (func $cast-null-anyref-to-gc (export "cast-null-anyref-to-gc")
  ;; a null anyref is a literal which is not even of GC data, as it's not an
  ;; array or a struct, so our casting code should not assume it is. it is ok
  ;; to try to cast it, and the result should be 0.
  (call $log
   (ref.test (ref $struct)
    (ref.null any)
   )
  )
 )
 (func $get_struct (result structref)
  (struct.new_default $struct)
 )
 (func $br-on_non_null (export "br-on_non_null")
  (drop
   (block $non-null (result (ref any))
    (br_on_non_null $non-null (ref.i31 (i32.const 0)))
    ;; $x refers to an i31, which is not null, so we will branch, and not
    ;; log
    (call $log (i32.const 1))
    (unreachable)
   )
  )
 )
 (func $br-on_non_null-2 (export "br-on_non_null-2")
  (drop
   (block $non-null (result (ref any))
    (br_on_non_null $non-null (ref.null any))
    ;; $x is null, and so we will not branch, and log and then trap
    (call $log (i32.const 1))
    (unreachable)
   )
  )
 )
 (func $ref-as-func-of-func (export "ref-as-func-of-func")
  (drop
   (ref.cast (ref func)
    (ref.func $structs)
   )
  )
 )
 (func $a-void-func
  (call $log (i32.const 1337))
 )
 (func $cast-on-func (export "cast-on-func")
  (call $log (i32.const 0))
  ;; a valid cast
  (call_ref $void_func
   (ref.cast (ref $void_func) (ref.func $a-void-func))
  )
  (call $log (i32.const 1))
  ;; an invalid cast
  (drop (call_ref $int_func
   (ref.cast (ref $int_func) (ref.func $a-void-func))
  ))
  ;; will never be reached
  (call $log (i32.const 2))
 )
 (func $array-alloc-failure (export "array-alloc-failure")
  (drop
   (array.new_default $bytes
    (i32.const -1) ;; un-allocatable size (4GB * sizeof(Literal))
   )
  )
 )
 (func $init-array-packed (export "init-array-packed") (result i32)
  (local $x (ref null $bytes))
  (local.set $x
   (array.new $bytes
    (i32.const -43) ;; initialize the i8 values with a negative i32
    (i32.const 50)
   )
  )
  ;; read the value, which should be -43 & 255 ==> 213
  (array.get_u $bytes
   (local.get $x)
   (i32.const 10)
  )
 )
 (func $call-target (param $0 eqref)
  (nop)
 )
 (func $array-copy (export "array-copy")
  (local $x (ref null $bytes))
  (local $y (ref null $bytes))
  ;; Create an array of 10's, of size 100.
  (local.set $x
   (array.new $bytes
    (i32.const 10)
    (i32.const 100)
   )
  )
  ;; Create an array of zeros of size 200, and also set one index there.
  (local.set $y
   (array.new_default $bytes
    (i32.const 200)
   )
  )
  (array.set $bytes
   (local.get $y)
   (i32.const 42)
   (i32.const 99)
  )
  ;; Log out a value from $x before.
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 10))
  )
  (array.copy $bytes $bytes
   (local.get $x)
   (i32.const 10)
   (local.get $y)
   (i32.const 42)
   (i32.const 2)
  )
  ;; Log out some value from $x after. Indexes 10 and 11 should be modified.
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 9))
  )
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 10))
  )
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 11))
  )
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 12))
  )
 )
 (func $array.new_fixed (export "array.new_fixed")
  (local $x (ref null $bytes))
  (local.set $x
   (array.new_fixed $bytes 2
    (i32.const 42) ;; first value
    (i32.const 50) ;; second value
   )
  )
  ;; The length should be 2
  (call $log
   (array.len (local.get $x))
  )
  ;; The first value should be 42
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 0))
  )
  ;; The second value should be 50
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 1))
  )
 )
 (func $array.new_fixed-packed (export "array.new_fixed-packed")
  (local $x (ref null $bytes))
  (local.set $x
   (array.new_fixed $bytes 1
    (i32.const -11512)
   )
  )
  ;; The value should be be -11512 & 255 => 8
  (call $log
   (array.get_u $bytes (local.get $x) (i32.const 0))
  )
 )
 (func $static-casts (export "static-casts")
  ;; Casting null returns null.
  (call $log (ref.is_null
   (ref.cast (ref null $struct) (ref.null $struct))
  ))
  ;; Testing null returns 0.
  (call $log
   (ref.test (ref $struct) (ref.null $struct))
  )
  ;; Testing something completely wrong (struct vs array) returns 0.
  (call $log
   (ref.test (ref $struct)
    (array.new $bytes
     (i32.const 20)
     (i32.const 10)
    )
   )
  )
  ;; Testing a thing with the same type returns 1.
  (call $log
   (ref.test (ref $struct)
    (struct.new_default $struct)
   )
  )
  ;; A bad downcast returns 0: we create a struct, which is not a extendedstruct.
  (call $log
   (ref.test (ref $extendedstruct)
    (struct.new_default $struct)
   )
  )
  ;; Casting to a supertype works.
  (call $log
   (ref.test (ref $struct)
    (struct.new_default $extendedstruct)
   )
  )
 )
 (func $static-br_on_cast (export "static-br_on_cast")
  (local $any anyref)
  ;; create a simple $struct, store it in an anyref
  (local.set $any
   (struct.new_default $struct)
  )
  (drop
   (block $block (result (ref $struct))
    (drop
     (block $extendedblock (result (ref $extendedstruct))
      (drop
       ;; second, try to cast our simple $struct to what it is, which will work
       (br_on_cast $block anyref (ref $struct)
        ;; first, try to cast our simple $struct to an extended, which will fail
        (br_on_cast $extendedblock anyref (ref $extendedstruct)
         (local.get $any)
        )
       )
      )
      (call $log (i32.const -1)) ;; we should never get here
      (return)
     )
    )
    (call $log (i32.const -2)) ;; we should never get here either
    (return)
   )
  )
  (call $log (i32.const 3)) ;; we should get here
 )
 (func $static-br_on_cast_fail (export "static-br_on_cast_fail")
  (local $any anyref)
  ;; create a simple $struct, store it in an anyref
  (local.set $any
   (struct.new_default $struct)
  )
  (drop
   (block $failblock (result anyref)
    (drop
      ;; try to cast our simple $struct to an extended, which will fail
     (br_on_cast_fail $failblock anyref (ref $extendedstruct)
      (local.get $any)
     )
    )
    (call $log (i32.const -1)) ;; we should never get here
    (return)
   )
  )
  (call $log (i32.const -2)) ;; we should get here.
  (return)
 )
)
(module
 (type $"[mut:i8]" (array (mut i8)))
 (func $foo (export "foo") (result i32)
  ;; before opts this will trap on failing to allocate -1 >>> 0 bytes. after
  ;; opts the unused value is removed so there is no trap, and a value is
  ;; returned, which should not confuse the fuzzer.
  (drop
   (array.new_default $"[mut:i8]"
    (i32.const -1)
   )
  )
  (i32.const 0)
 )
)