Raw File
ast_generators.ml
(*****************************************************************************)
(*                                                                           *)
(* Open Source License                                                       *)
(* Copyright (c) 2022 Trili Tech  <contact@trili.tech>                       *)
(*                                                                           *)
(* Permission is hereby granted, free of charge, to any person obtaining a   *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense,  *)
(* and/or sell copies of the Software, and to permit persons to whom the     *)
(* Software is furnished to do so, subject to the following conditions:      *)
(*                                                                           *)
(* The above copyright notice and this permission notice shall be included   *)
(* in all copies or substantial portions of the Software.                    *)
(*                                                                           *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL   *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING   *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER       *)
(* DEALINGS IN THE SOFTWARE.                                                 *)
(*                                                                           *)
(*****************************************************************************)

open Tezos_webassembly_interpreter
open Lazy_containers
open QCheck2.Gen
module Vector = Lazy_containers.Lazy_vector.Int32Vector

let small_vector_gen gen = Vector.of_list <$> small_list gen

let no_region it = Source.{it; at = no_region}

let var_gen =
  let+ n = int32 in
  no_region n

(* The types [F32Type] and [F64Type] are not currently supported. *)
let num_type_gen =
  let open Types in
  oneofl [I32Type; I64Type]

let ref_type_gen =
  let open Types in
  oneofl [FuncRefType; ExternRefType]

let value_type_gen =
  let open Types in
  oneof
    [
      map (fun nt -> NumType nt) num_type_gen;
      return (VecType V128Type);
      map (fun rt -> RefType rt) ref_type_gen;
    ]

let value_types_gen = small_list value_type_gen

let block_label_gen =
  let+ l = int32 in
  Ast.Block_label l

let data_label_gen =
  let+ l = int32 in
  Ast.Data_label l

let block_type_gen =
  let open Ast in
  oneof
    [
      map (fun v -> VarBlockType v) var_gen;
      map (fun v -> ValBlockType v) (opt value_type_gen);
    ]

let memop_gen ty_gen pack_gen =
  let+ align = int_range 0 255
  and+ ty = ty_gen
  and+ pack = pack_gen
  and+ offset = int32 in
  {Ast.ty; align; offset; pack}

let pack_size_gen = oneofl [Types.Pack8; Pack16; Pack32; Pack64]

let extension_gen = oneofl [Types.SX; ZX]

let load_op_gen =
  memop_gen num_type_gen (opt (pair pack_size_gen extension_gen))

let store_op_gen = memop_gen num_type_gen (opt pack_size_gen)

let pack_shape_gen = oneofl [Types.Pack8x8; Pack16x4; Pack32x2]

let vec_extension_gen =
  let open Types in
  oneof
    [
      map (fun (ps, e) -> ExtLane (ps, e)) (pair pack_shape_gen extension_gen);
      return ExtSplat;
      return ExtZero;
    ]

let vec_type_gen = return Types.V128Type

let vec_load_op_gen =
  memop_gen vec_type_gen (opt @@ pair pack_size_gen vec_extension_gen)

let vec_store_op_gen = memop_gen vec_type_gen unit

let vec_laneop_gen =
  let+ memop = memop_gen vec_type_gen pack_size_gen and+ n = int_range 0 255 in
  (memop, n)

(** The values [F32] and [F64] are not currently supported. *)
let value_op_gen int32_gen int64_gen =
  let open Values in
  oneof [map (fun x -> I32 x) int32_gen; map (fun x -> I64 x) int64_gen]

let num_gen =
  let+ num = value_op_gen int32 int64 in
  no_region num

let int_relop_gen =
  let open Ast.IntOp in
  oneofl [Eq; Ne; LtS; LtU; LeS; LeU; GtS; GtU; GeS; GeU]

let relop_gen = value_op_gen int_relop_gen int_relop_gen

let int_unop_gen =
  let open Ast.IntOp in
  oneof
    [
      return Clz;
      return Ctz;
      return Popcnt;
      map (fun ps -> ExtendS ps) pack_size_gen;
    ]

let int_binop_gen =
  let open Ast.IntOp in
  oneofl
    [
      Add;
      Sub;
      Mul;
      DivS;
      DivU;
      RemS;
      RemU;
      And;
      Or;
      Xor;
      Shl;
      ShrS;
      ShrU;
      Rotl;
      Rotr;
    ]

let int_cvtop =
  let open Ast.IntOp in
  oneofl
    [
      ExtendSI32;
      ExtendUI32;
      WrapI64;
      TruncSF32;
      TruncUF32;
      TruncSF64;
      TruncUF64;
      TruncSatSF32;
      TruncSatUF32;
      TruncSatSF64;
      TruncSatUF64;
      ReinterpretFloat;
    ]

let cvtop_gen = value_op_gen int_cvtop int_cvtop

let unop_gen = value_op_gen int_unop_gen int_unop_gen

let binop_gen = value_op_gen int_binop_gen int_binop_gen

let vec_gen =
  let* bits = string_size (return 16) in
  let value = V128.of_bits bits in
  return (no_region @@ Values.V128 value)

(*
  Generate instructions. The following are missing:
  - [VecTest]
  - [VecCompare]
  - [VecUnary]
  - [VecBinary]
  - [VecConvert]
  - [VecShift]
  - [VecBitmask]
  - [VecTestBits]
  - [VecUnaryBits]
  - [VecBinaryBits]
  - [VecTernaryBits]
  - [VecSplat]
  - [VecExtract]
  - [VecReplace]
  *)
let instr_gen =
  let open Ast in
  let with_var f = map f var_gen in
  let with_block_type_and_label f =
    let+ bt = block_type_gen and+ l = block_label_gen in
    f (bt, l)
  in
  let+ x =
    oneof
      [
        (* Simple *)
        return Unreachable;
        return Nop;
        return Drop;
        return Return;
        return MemorySize;
        return MemoryGrow;
        return MemoryFill;
        return MemoryCopy;
        return RefIsNull;
        (* Unary with variable payload. *)
        with_var (fun v -> Br v);
        with_var (fun var -> Br var);
        with_var (fun var -> BrIf var);
        with_var (fun var -> Call var);
        with_var (fun var -> LocalGet var);
        with_var (fun var -> LocalSet var);
        with_var (fun var -> LocalTee var);
        with_var (fun var -> GlobalGet var);
        with_var (fun var -> GlobalSet var);
        with_var (fun var -> TableGet var);
        with_var (fun var -> TableSet var);
        with_var (fun var -> TableSize var);
        with_var (fun var -> TableGrow var);
        with_var (fun var -> TableFill var);
        with_var (fun var -> ElemDrop var);
        with_var (fun var -> MemoryInit var);
        with_var (fun var -> DataDrop var);
        with_var (fun var -> RefFunc var);
        (* More complex *)
        map (fun vt -> Select vt) (opt value_types_gen);
        with_block_type_and_label (fun (bt, l) -> Block (bt, l));
        with_block_type_and_label (fun (bt, l) -> Loop (bt, l));
        map
          (fun (t, l2, l3) -> If (t, l2, l3))
          (triple block_type_gen block_label_gen block_label_gen);
        map (fun (vl, v) -> BrTable (vl, v)) (pair (small_list var_gen) var_gen);
        map (fun (v1, v2) -> CallIndirect (v1, v2)) (pair var_gen var_gen);
        map (fun o -> Load o) load_op_gen;
        map (fun o -> Store o) store_op_gen;
        map (fun o -> VecLoad o) vec_load_op_gen;
        map (fun o -> VecStore o) vec_store_op_gen;
        map (fun o -> VecLoadLane o) vec_laneop_gen;
        map (fun o -> VecStoreLane o) vec_laneop_gen;
        map (fun rt -> RefNull rt) ref_type_gen;
        map (fun n -> Const n) num_gen;
        map (fun o -> Compare o) relop_gen;
        map (fun o -> Unary o) unop_gen;
        map (fun o -> Binary o) binop_gen;
        map (fun o -> Convert o) cvtop_gen;
        map (fun o -> VecConst o) vec_gen;
      ]
  in
  no_region x

let vector_gen gen =
  let* len = int_range 0 10 in
  let* seeds = small_list int in
  return
    (Lazy_vector.Int32Vector.create
       ~produce_value:(fun ix ->
         let rand =
           Random.State.make @@ Array.of_list (Int32.to_int ix :: seeds)
         in
         Lwt.return @@ generate1 ~rand gen)
       (Int32.of_int len))

let vector_z_gen gen =
  let* len = int_range 0 10 in
  let* seeds = small_list int in
  return
    (Lazy_vector.ZVector.create
       ~produce_value:(fun ix ->
         let rand = Random.State.make @@ Array.of_list (Z.to_int ix :: seeds) in
         Lwt.return @@ generate1 ~rand gen)
       (Z.of_int len))

let result_type_gen = vector_gen value_type_gen

let func_type_gen =
  let+ pt = result_type_gen and+ rt = result_type_gen in
  Types.FuncType (pt, rt)

let block_label_gen =
  let+ n = int32 in
  Ast.Block_label n

let ast_func_gen =
  let* ftype = var_gen in
  let* locals = vector_gen value_type_gen in
  let* body = block_label_gen in
  return @@ no_region {Ast.ftype; locals; body}

let func_gen current_module =
  let ast_func () =
    let* func_type = func_type_gen in
    let* func = ast_func_gen in
    return @@ Func.AstFunc (func_type, current_module, func)
  in
  oneof
    [
      delay ast_func;
      map
        (fun (ft, n) -> Func.HostFunc (ft, Printf.sprintf "host_func_%d" n))
        (pair func_type_gen small_nat);
    ]

let ref_type_gen = oneofl [Types.FuncRefType; Types.ExternRefType]

let ref_gen =
  oneof
    [
      map (fun rt -> Values.NullRef rt) ref_type_gen;
      map (fun n -> Values.ExternRef n) int32;
    ]

let table_gen =
  let* len = frequency [(10, int_range 1 10); (1, int_range 100 200)] in
  let* max = opt @@ map Int32.of_int @@ int_range 1 len in
  let limit = {Types.min = 0l; max} in
  let* ref_type = ref_type_gen in
  let ty = Types.TableType (limit, ref_type) in
  let* seeds = small_list (int_range 0 10_000) in
  let table_entries =
    Table.Vector.Vector.create
      ~produce_value:(fun ix ->
        let rand =
          Random.State.make @@ Array.of_list (Int32.to_int ix :: seeds)
        in
        Lwt.return @@ generate1 ~rand ref_gen)
      (Int32.of_int len)
  in
  return @@ Table.of_lazy_vector ty table_entries

let chunked_byte_vector_gen =
  let* bs = small_string ~gen:char in
  return @@ Chunked_byte_vector.of_string bs

let memory_gen =
  let* len = frequency [(10, int_range 1 10); (1, int_range 100 200)] in
  let* max = opt @@ map Int32.of_int @@ int_range 1 len in
  let ty = Types.MemoryType {Types.min = 0l; max} in
  let* chunks = chunked_byte_vector_gen in
  return @@ Memory.of_chunks ty chunks

let value_num_gen nt =
  match nt with
  | Types.I32Type -> map (fun x -> Values.I32 x) int32
  | Types.I64Type -> map (fun x -> Values.I64 x) int64
  | _ -> Stdlib.failwith "Float type not supported"

let typed_value_gen ty =
  match ty with
  | Types.NumType nt -> map (fun n -> Values.Num n) (value_num_gen nt)
  | Types.RefType rt -> return @@ Values.Ref (Values.NullRef rt)
  | Types.VecType _ -> map (fun v -> Values.Vec v.Source.it) vec_gen

let value_gen =
  let* value_type = value_type_gen in
  typed_value_gen value_type

let global_gen =
  let* value_type = value_type_gen in
  let* value = typed_value_gen value_type in
  let* mt = oneofl [Types.Immutable; Types.Mutable] in
  let ty = Types.GlobalType (value_type, mt) in
  return @@ Global.alloc ty value

let extern_gen current_module =
  oneof
    [
      map (fun f -> Instance.ExternFunc f) @@ func_gen current_module;
      map (fun t -> Instance.ExternTable t) table_gen;
      map (fun m -> Instance.ExternMemory m) memory_gen;
      map (fun g -> Instance.ExternGlobal g) global_gen;
    ]

let map_gen gen =
  let* seeds = small_list int in
  return
  @@ Instance.NameMap.create
       ~produce_value:(fun key ->
         let rand = Random.State.make @@ Array.of_list (key @ seeds) in
         Lwt.return @@ generate1 ~rand gen)
       ()

let elems_gen =
  let+ v = vector_gen ref_gen in
  ref v

let datas_gen =
  let+ lbl = data_label_gen in
  ref @@ lbl

let blocks_table_gen = vector_gen (vector_gen instr_gen)

let datas_table_gen = vector_gen chunked_byte_vector_gen

let allocations_gen =
  let* blocks = blocks_table_gen in
  let+ datas = datas_table_gen in
  Ast.{blocks; datas}

let limit_gen gen =
  let* min = gen in
  let* max = opt gen in
  return {Types.min; Types.max}

let table_type_gen =
  let* limit = limit_gen int32 in
  let* ref_type = ref_type_gen in
  return @@ Types.TableType (limit, ref_type)

let memory_type_gen =
  let+ limit = limit_gen int32 in
  Types.MemoryType limit

let global_type_gen =
  let* vt = value_type_gen in
  let* mt = oneofl [Types.Immutable; Types.Mutable] in
  return @@ Types.GlobalType (vt, mt)

let import_desc_gen =
  let+ idesc =
    oneof
      [
        map (fun v -> Ast.FuncImport v) var_gen;
        map (fun tt -> Ast.TableImport tt) table_type_gen;
        map (fun mt -> Ast.MemoryImport mt) memory_type_gen;
        map (fun gt -> Ast.GlobalImport gt) global_type_gen;
      ]
  in
  no_region idesc

let export_desc_gen =
  let+ edesc =
    oneof
      [
        map (fun v -> Ast.FuncExport v) var_gen;
        map (fun v -> Ast.TableExport v) var_gen;
        map (fun v -> Ast.MemoryExport v) var_gen;
        map (fun v -> Ast.GlobalExport v) var_gen;
      ]
  in
  no_region edesc

let const_gen = map no_region block_label_gen

let segment_mode_gen =
  let passive = return Ast.Passive in
  let active =
    let* index = var_gen in
    let+ offset = block_label_gen in
    Ast.Active {index; offset = no_region offset}
  in
  let declarative = return Ast.Declarative in
  let+ mode = oneof [passive; active; declarative] in
  no_region mode

let start_gen =
  let+ sfunc = var_gen in
  no_region Ast.{sfunc}

let module_key_and_instance_gen ?module_reg () =
  let module_reg =
    match module_reg with
    | None -> Instance.ModuleMap.create ()
    | Some module_reg -> module_reg
  in
  let* module_name = string_printable in
  let module_key = Instance.Module_key module_name in
  let* types = vector_gen func_type_gen in
  let* funcs = vector_gen @@ func_gen module_key in
  let* tables = vector_gen table_gen in
  let* memories = vector_gen memory_gen in
  let* globals = vector_gen global_gen in
  let* exports = map_gen (extern_gen module_key) in
  let* elems = vector_gen elems_gen in
  let* datas = vector_gen datas_gen in
  let* allocations = allocations_gen in
  let module_ =
    {
      Instance.types;
      funcs;
      tables;
      memories;
      globals;
      exports;
      elems;
      datas;
      allocations;
    }
  in
  Instance.update_module_ref module_reg module_key module_ ;
  return (module_key, module_)

let module_gen ?module_reg () =
  map snd (module_key_and_instance_gen ?module_reg ())

let frame_gen ~module_reg =
  let* inst, _ = module_key_and_instance_gen ~module_reg () in
  let+ locals = small_vector_gen (map ref value_gen) in
  Eval.{inst; locals}

let rec admin_instr'_gen ~module_reg =
  let open Eval in
  let from_block_gen =
    let* block = block_label_gen in
    let+ index = int32 in
    From_block (block, index)
  in
  let plain_gen =
    let+ instr = instr_gen in
    Plain instr.it
  in
  let refer_gen =
    let+ ref_ = ref_gen in
    Refer ref_
  in
  let invoke_gen =
    let* inst, _ = module_key_and_instance_gen ~module_reg () in
    let+ func = func_gen inst in
    Invoke func
  in
  let trapping_gen =
    let+ msg = string_printable in
    Trapping msg
  in
  let returning_gen =
    let+ values = small_vector_gen value_gen in
    Returning values
  in
  let breaking_gen =
    let* index = int32 in
    let+ values = small_vector_gen value_gen in
    Breaking (index, values)
  in
  oneof
    [
      from_block_gen;
      plain_gen;
      refer_gen;
      invoke_gen;
      trapping_gen;
      returning_gen;
      breaking_gen;
    ]

and admin_instr_gen ~module_reg =
  map Source.(at no_region) (admin_instr'_gen ~module_reg)

let input_buffer_gen =
  let gen_message =
    let* rtype = int32 in
    let* raw_level = int32 in
    let* message_counter = map Z.of_int small_nat in
    let+ payload = map Bytes.of_string (small_string ~gen:char) in
    Input_buffer.{rtype; raw_level; message_counter; payload}
  in
  let* messages = vector_z_gen gen_message in
  let+ num_elements = small_nat in
  {
    Input_buffer.content = Lazy_vector.Mutable.ZVector.of_immutable messages;
    num_elements = Z.of_int num_elements;
  }

let output_info_gen =
  let* level = small_int in
  let outbox_level = Int32.of_int level in
  let* message_index = map Z.of_int small_nat in
  return Output_buffer.{outbox_level; message_index}

let output_buffer_gen =
  let* l = small_list int in
  let s =
    List.map
      (fun _ ->
        generate1
        @@ map
             (fun a ->
               Output_buffer.Index_Vector.(of_immutable @@ Vector.of_list a))
             (list (map Bytes.of_string string)))
      l
  in
  return Output_buffer.Level_Vector.(of_immutable @@ Vector.of_list s)

let label_gen ~module_reg =
  let* label_arity = option (Int32.of_int <$> small_nat) in
  let* label_frame_specs = frame_gen ~module_reg in
  let* label_break = option instr_gen in
  let* es = small_vector_gen (admin_instr_gen ~module_reg) in
  let+ vs = small_vector_gen value_gen in
  Eval.{label_arity; label_frame_specs; label_break; label_code = (vs, es)}

let label_stack_gen ~module_reg =
  let* label = label_gen ~module_reg in
  let+ stack = small_vector_gen (label_gen ~module_reg) in
  Eval.Label_stack (label, stack)

let label_result_gen =
  let+ values = small_vector_gen value_gen in
  Eval.Label_result values

let label_trapped_gen =
  let+ msg = small_string ~gen:char in
  Eval.Label_trapped (no_region @@ msg)

type packed_label_kont = Packed_lk : 'a Eval.label_kont -> packed_label_kont

type packed_frame_stack =
  | Packed_fs : 'a Eval.frame_stack -> packed_frame_stack

let packed_label_kont_gen ~module_reg =
  let pack x = Packed_lk x in
  oneof
    [
      pack <$> label_stack_gen ~module_reg;
      pack <$> label_result_gen;
      pack <$> label_trapped_gen;
    ]

let ongoing_frame_stack_gen ~module_reg =
  let* frame_arity = option (Int32.of_int <$> small_nat) in
  let* frame_specs = frame_gen ~module_reg in
  let+ frame_label_kont = label_stack_gen ~module_reg in
  Eval.{frame_arity; frame_specs; frame_label_kont}

let packed_frame_stack_gen ~module_reg =
  let* frame_arity = option (Int32.of_int <$> small_nat) in
  let* frame_specs = frame_gen ~module_reg in
  let+ (Packed_lk frame_label_kont) = packed_label_kont_gen ~module_reg in
  Packed_fs {frame_arity; frame_specs; frame_label_kont}

let map_kont_gen gen gen' =
  let* origin = small_vector_gen gen in
  let* destination = small_vector_gen gen' in
  let+ offset = Int32.of_int <$> small_nat in
  Eval.{origin; destination; offset}

let concat_kont_gen gen =
  let* lv = small_vector_gen gen in
  let* rv = small_vector_gen gen in
  let* res = small_vector_gen gen in
  let+ offset = Int32.of_int <$> small_nat in
  Eval.{lv; rv; res; offset}

let inv_start_gen ~module_reg =
  let* module_name = string_printable in
  let module_key = Instance.Module_key module_name in
  let* func = func_gen module_key in
  let* es = small_vector_gen (admin_instr_gen ~module_reg) in
  let+ vs = small_vector_gen value_gen in
  Eval.Inv_start {func; code = (vs, es)}

let inv_prepare_locals_gen ~module_reg =
  let* arity = Int32.of_int <$> small_nat in
  let* args = small_vector_gen value_gen in
  let* vs = small_vector_gen value_gen in
  let* instructions = small_vector_gen (admin_instr_gen ~module_reg) in
  let* module_name = string_printable in
  let inst = Instance.Module_key module_name in
  let* func = ast_func_gen in
  let+ locals_kont = map_kont_gen value_type_gen (ref <$> value_gen) in
  Eval.Inv_prepare_locals
    {arity; args; vs; instructions; inst; func; locals_kont}

let inv_prepare_args_gen ~module_reg =
  let* arity = Int32.of_int <$> small_nat in
  let* vs = small_vector_gen value_gen in
  let* instructions = small_vector_gen (admin_instr_gen ~module_reg) in
  let* module_name = string_printable in
  let inst = Instance.Module_key module_name in
  let* func = ast_func_gen in
  let* locals = small_vector_gen (ref <$> value_gen) in
  let+ args_kont = map_kont_gen value_gen (ref <$> value_gen) in
  Eval.Inv_prepare_args {arity; vs; instructions; inst; func; locals; args_kont}

let inv_concat_gen ~module_reg =
  let* arity = Int32.of_int <$> small_nat in
  let* vs = small_vector_gen value_gen in
  let* instructions = small_vector_gen (admin_instr_gen ~module_reg) in
  let* module_name = string_printable in
  let inst = Instance.Module_key module_name in
  let* func = ast_func_gen in
  let+ concat_kont = concat_kont_gen (ref <$> value_gen) in
  Eval.Inv_concat {arity; vs; instructions; inst; func; concat_kont}

let inv_stop_gen ~module_reg =
  let* vs = small_vector_gen value_gen in
  let* es = small_vector_gen (admin_instr_gen ~module_reg) in
  let+ fresh_frame = option (ongoing_frame_stack_gen ~module_reg) in
  Eval.Inv_stop {code = (vs, es); fresh_frame}

let invoke_step_gen ~module_reg =
  oneof
    [
      inv_start_gen ~module_reg;
      inv_prepare_locals_gen ~module_reg;
      inv_prepare_args_gen ~module_reg;
      inv_concat_gen ~module_reg;
      inv_stop_gen ~module_reg;
    ]

let ls_start_gen ~module_reg =
  let+ label = label_stack_gen ~module_reg in
  Eval.LS_Start label

let ls_craft_frame_gen ~module_reg =
  let* kont = label_stack_gen ~module_reg in
  let+ invoke_step = invoke_step_gen ~module_reg in
  Eval.LS_Craft_frame (kont, invoke_step)

let ls_push_frame_gen ~module_reg =
  let* kont = label_stack_gen ~module_reg in
  let+ frame = ongoing_frame_stack_gen ~module_reg in
  Eval.LS_Push_frame (kont, frame)

let ls_consolidate_top_gen ~module_reg =
  let* label = label_gen ~module_reg in
  let* kont = concat_kont_gen value_gen in
  let* es = small_vector_gen (admin_instr_gen ~module_reg) in
  let+ stack = small_vector_gen (label_gen ~module_reg) in
  Eval.LS_Consolidate_top (label, kont, es, stack)

let ls_modify_top_gen ~module_reg =
  let+ (Packed_lk kont) = packed_label_kont_gen ~module_reg in
  Eval.LS_Modify_top kont

let label_step_kont_gen ~module_reg =
  oneof
    [
      ls_start_gen ~module_reg;
      ls_craft_frame_gen ~module_reg;
      ls_push_frame_gen ~module_reg;
      ls_consolidate_top_gen ~module_reg;
      ls_modify_top_gen ~module_reg;
    ]

let sk_start_gen ~module_reg =
  let* (Packed_fs top) = packed_frame_stack_gen ~module_reg in
  let+ stack = small_vector_gen (ongoing_frame_stack_gen ~module_reg) in
  Eval.SK_Start (top, stack)

let sk_next_gen ~module_reg =
  let* (Packed_fs top) = packed_frame_stack_gen ~module_reg in
  let* stack = small_vector_gen (ongoing_frame_stack_gen ~module_reg) in
  let+ label_kont = label_step_kont_gen ~module_reg in
  Eval.SK_Next (top, stack, label_kont)

let sk_consolidate_label_result_gen ~module_reg =
  let* frame = ongoing_frame_stack_gen ~module_reg in
  let* stack = small_vector_gen (ongoing_frame_stack_gen ~module_reg) in
  let* label = label_gen ~module_reg in
  let* kont = concat_kont_gen value_gen in
  let* es = small_vector_gen (admin_instr_gen ~module_reg) in
  let+ lstack = small_vector_gen (label_gen ~module_reg) in
  Eval.SK_Consolidate_label_result (frame, stack, label, kont, es, lstack)

let sk_result_gen =
  let+ values = small_vector_gen value_gen in
  Eval.SK_Result values

let sk_trapped_gen =
  let+ msg = small_string ~gen:char in
  Eval.SK_Trapped (no_region @@ msg)

let step_kont_gen ~module_reg =
  oneof
    [
      sk_start_gen ~module_reg;
      sk_next_gen ~module_reg;
      sk_result_gen;
      sk_trapped_gen;
    ]

let config_gen ~host_funcs ~module_reg =
  let* input = input_buffer_gen in
  let _input_list =
    Lwt_main.run @@ Lazy_vector.ZVector.to_list
    @@ Lazy_vector.Mutable.ZVector.snapshot input.content
  in
  let* output = output_buffer_gen in
  let* stack_size_limit = small_int in
  let+ step_kont = step_kont_gen ~module_reg in
  Eval.{input; output; step_kont; host_funcs; stack_size_limit}
back to top