1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
(* SPDX-License-Identifier: AGPL-3.0-or-later *)
(* Copyright © 2021-2026 OCamlPro *)
(* Written by the Owi programmers *)

open Syntax
module I = Abstract_interpreter_control_flow

let unsafe = false

type host_externref = int

let ty : host_externref Type.Id.t = Type.Id.make ()

let do_action env = function
  | Wast.Invoke (module_name, func_name, args) -> begin
    Log.info (fun m ->
      m "invoke %a %s %a..."
        (Fmt.option ~none:Fmt.nop Fmt.string)
        module_name func_name Wast.pp_consts args );
    let* f = Env.Abstract.get_exported_func ~env ~module_name ~func_name in
    let ctx = Env.Abstract.get_context ~env in
    let stack =
      List.rev_map (Abstract_value.of_script_const ctx ~ty) args
      |> List.mapi (fun i v -> (i, v))
    in
    let locals = Abstract_locals.of_list stack in
    I.exec_vfunc_from_outside ~ctx ~locals ~env f
    end
  | Get (_module_name, _name) ->
    Log.info (fun m -> m "get...");
    assert false
(* let* global = Link.get_global_from_module env mod_id name in *)
(* let v = Abstract_value.of_concrete ctx global.value in *)
(* Ok [ v ] *)

let run_one ~no_exhaustion:_ (state : Env.Abstract.t Result.t) cmd =
  let* env = state in
  match cmd with
  | Wast.Text_module (false, m) ->
    let* modul, env =
      Compile.Text.until_abstract_link env ~unsafe ~name:None m
    in
    let _state = I.modul_with_ctx ~env ~modul in
    (* TODO: set context in env? Or isn't it necessary as it's supposed to be mutable? *)
    Ok env
  | Assert (Assert_return (action, res)) ->
    let* state = do_action env action in
    let stack = List.rev state.stack in
    if
      List.compare_lengths res stack <> 0
      || not
           (List.for_all2
              (Abstract_value.equal_script_result
                 (Env.Abstract.get_context ~env)
                 ~ty )
              res stack )
    then begin
      (* Log.err (fun m -> *)
      (*   m "got:      %a@.expected: %a" Stack.pp stack Wast.pp_results res ); *)
      Error `Bad_result
    end
    else Ok env
  | _ -> assert false

let run ~no_exhaustion script =
  let context = Abstract_domain.root_context () in
  let env = Env.Abstract.empty ~context in
  let* state =
    Env.Abstract.link_extern_module ~env ~name:"spectest_extern"
      Spectest.abstract_extern_m
  in
  let script = Spectest.m :: Register ("spectest", Some "spectest") :: script in

  List.fold_left
    (fun acc cmd -> run_one ~no_exhaustion acc cmd)
    (Ok state) script

let exec ~(no_exhaustion : bool) (script : Wast.script) =
  let res = run ~no_exhaustion script in
  (* match Symex.Monad.run to_run (Thread.init ()) with *)
  match res with
  | Error _e -> Error (`Msg "script failed!")
  | Ok _ -> Ok ()