diff --git a/src/lib_scoru_wasm/test/ast_generators.ml b/src/lib_scoru_wasm/test/ast_generators.ml index cebf007447efa5e4449adfb02fc7c7df16901836..67e9e5bddd705e2729bdf04bcb9a2b1f2c6c2a1f 100644 --- a/src/lib_scoru_wasm/test/ast_generators.ml +++ b/src/lib_scoru_wasm/test/ast_generators.ml @@ -328,12 +328,15 @@ let table_gen = 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.Lwt.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* bs = string in - let chunks = Chunked_byte_vector.Lwt.of_string bs in + let* chunks = chunked_byte_vector_gen in return @@ Memory.of_chunks ty chunks let value_num_gen nt = @@ -378,8 +381,8 @@ let elems_gen = ref v let datas_gen = - let+ bs = string in - ref @@ Chunked_byte_vector.Lwt.of_string bs + let+ chunk = chunked_byte_vector_gen in + ref @@ chunk let blocks_table_gen = vector_gen (vector_gen instr_gen) diff --git a/src/lib_scoru_wasm/test/ast_printer.ml b/src/lib_scoru_wasm/test/ast_printer.ml index d5f431935bc9e32110091a4b2f692d5596e9e84f..acefd5a0907d5dc3989ec6feeb0ee3a11b6bba56 100644 --- a/src/lib_scoru_wasm/test/ast_printer.ml +++ b/src/lib_scoru_wasm/test/ast_printer.ml @@ -89,7 +89,7 @@ let pp_list pp out x = let pp_value_type_list = pp_list pp_value_type let pp_block_label out (Ast.Block_label l) = - Format.fprintf out "Block_label @(%ld)@]" l + Format.fprintf out "Block_label @[(%ld)@]" l let pp_opt pp out = function | Some x -> Format.fprintf out "Some @[(%a)@]" pp x @@ -340,8 +340,9 @@ let pp_instr' out instr = let pp_instr = pp_phrase pp_instr' let pp_vector pp out v = - let xs = Lwt_main.run @@ Lazy_vector.LwtInt32Vector.to_list v in - pp_list pp out xs + (* Force evaluation of the vector. *) + let _ = Lwt_main.run @@ Lazy_vector.LwtInt32Vector.to_list v in + Lazy_vector.LwtInt32Vector.pp pp out v let pp_resul_type = pp_vector pp_value_type @@ -407,14 +408,13 @@ let pp_chunk_byte_vector out chunks = let pp_table out t = let ty = Partial_table.type_of t in let c = Partial_table.content t in - let xs = Lwt_main.run @@ Partial_table.Vector.Vector.to_list c in Format.fprintf out - "@[{ty = %a;@; content = %a}@]" + "@[{ty = %a;@; content = (%a)}@]" pp_table_type ty - (pp_list pp_ref) - xs + (pp_vector pp_ref) + c let pp_mutable out = function | Types.Immutable -> Format.pp_print_string out "Immutable" diff --git a/src/lib_scoru_wasm/test/test_encoding.ml b/src/lib_scoru_wasm/test/test_encoding.ml index c0b714484ab52ff8dc5b4a0e39ed136cb9de905d..c0ec80abf954315cb58001d2dff38128b55d4788 100644 --- a/src/lib_scoru_wasm/test/test_encoding.ml +++ b/src/lib_scoru_wasm/test/test_encoding.ml @@ -278,6 +278,12 @@ let test_tuples () = ((1, 2), (3, 4)) Stdlib.( = ) in + let* () = + assert_round_trip + (tup9 ~flatten:false int int int int int int int int int) + (1, 2, 3, 4, 5, 6, 7, 8, 9) + Stdlib.( = ) + in (* Without flatten we override the element since the tree [int]s are all stored under the same key [my_int]. *) let*! t3 = encode_decode (tup3 ~flatten:true int int int) (1, 2, 3) in @@ -309,6 +315,29 @@ let test_value_option () = let* () = assert_round_trip enc None Stdlib.( = ) in return_unit +type cyclic = {name : string; self : unit -> cyclic} + +let test_with_self_ref () = + let open Merklizer in + let open Lwt_result_syntax in + let enc () = + with_self_reference (fun cycle -> + conv + (fun name -> {name; self = (fun () -> Lazy.force cycle)}) + (fun {name; _} -> name) + (value [] Data_encoding.string)) + in + (* A cycle is a value with a (lazy) self-reference. *) + let rec cycle = {name = "Cycle"; self = (fun () -> cycle)} in + (* Encode using an encoder and an empty tree. *) + let*! empty_tree = empty_tree () in + let*! tree = Merklizer.encode (enc ()) cycle empty_tree in + (* Decode using a new encoder value and the tree from above. *) + let*! ({name; self} as cycle) = Merklizer.decode (enc ()) tree in + assert (name = "Cycle") ; + assert (cycle == self ()) ; + return_unit + let tests = [ tztest "String" `Quick test_string; @@ -327,4 +356,5 @@ let tests = tztest "Tuples" `Quick test_tuples; tztest "Option" `Quick test_option; tztest "Value Option" `Quick test_value_option; + tztest "Self ref" `Quick test_with_self_ref; ] diff --git a/src/lib_scoru_wasm/test/test_scoru_wasm.ml b/src/lib_scoru_wasm/test/test_scoru_wasm.ml index 987f3dbb3a283ddd25482c8e7db6a687e0fe9770..506c77631f21175ec200b557d50e9e8f419d5d08 100644 --- a/src/lib_scoru_wasm/test/test_scoru_wasm.ml +++ b/src/lib_scoru_wasm/test/test_scoru_wasm.ml @@ -37,5 +37,6 @@ let () = ("Input", Test_input.tests); ("Encodings", Test_encoding.tests); ("AST Generators", Test_ast_generators.tests); + ("WASM Encodings", Test_wasm_encoding.tests); ] |> Lwt_main.run diff --git a/src/lib_scoru_wasm/test/test_wasm_encoding.ml b/src/lib_scoru_wasm/test/test_wasm_encoding.ml new file mode 100644 index 0000000000000000000000000000000000000000..fa32f8e7a87e86dd591bd65f420d7699f4212db2 --- /dev/null +++ b/src/lib_scoru_wasm/test/test_wasm_encoding.ml @@ -0,0 +1,144 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 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. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Tree_encoding_decoding + Invocation: dune exec src/lib_scoru_wasm/test/test_scoru_wasm.exe \ + -- test "WASM Encodings" + Subject: Encoding tests for the tezos-scoru-wasm library +*) + +open Tztest +open Tezos_webassembly_interpreter +open Tezos_scoru_wasm + +let qcheck ?count ?print gen f = + let open Lwt_result_syntax in + let test = + QCheck2.Test.make ?count ?print gen (fun x -> + Result.is_ok @@ Lwt_main.run (f x)) + in + let res = QCheck_base_runner.run_tests ~verbose:true [test] in + if res = 0 then return_unit else failwith "QCheck tests failed" + +(* Use context-binary for testing. *) +module Context = Tezos_context_memory.Context_binary + +module Tree = struct + type t = Context.t + + type tree = Context.tree + + type key = Context.key + + type value = Context.value + + include Context.Tree +end + +module Merklizer = + Tree_encoding_decoding.Make (Instance.NameMap) (Instance.Vector) + (Chunked_byte_vector.Lwt) + (Tree) +module Wasm_encoding = Wasm_encoding.Make (Merklizer) + +let empty_tree () = + let open Lwt_syntax in + let* index = Context.init "/tmp" in + let empty_store = Context.empty index in + return @@ Context.Tree.empty empty_store + +let encode_decode enc value = + let open Lwt_syntax in + let* empty_tree = empty_tree () in + let* tree = Merklizer.encode enc value empty_tree in + Merklizer.decode enc tree + +(** Test serialize/deserialize instructions. *) +let test_instr_roundtrip () = + let open Lwt_result_syntax in + qcheck Ast_generators.instr_gen (fun instr -> + let*! instr' = encode_decode Wasm_encoding.instruction_encoding instr in + assert (instr = instr') ; + return_unit) + +let assert_string_equal s1 s2 = + let open Lwt_result_syntax in + if String.equal s1 s2 then return_unit else failwith "Not equal" + +(** Test serialize/deserialize modules. *) +let test_module_roundtrip () = + let print = Format.asprintf "%a" Ast_printer.pp_module in + let open Lwt_result_syntax in + qcheck ~print (Ast_generators.module_gen ()) (fun module1 -> + (* We need to print here in order to force lazy bindings to be evaluated. *) + let module1_str = print module1 in + let*! module2 = + encode_decode Wasm_encoding.module_instance_encoding module1 + in + let module2_str = print module2 in + let*! module3 = + encode_decode Wasm_encoding.module_instance_encoding module2 + in + let module3_str = print module3 in + (* Check that modules match. *) + let* () = assert_string_equal module1_str module2_str in + assert_string_equal module2_str module3_str) + +(** Test serialize/deserialize modules and compare trees. *) +let test_module_tree () = + let print = Format.asprintf "%a" Ast_printer.pp_module in + let open Lwt_result_syntax in + qcheck ~print (Ast_generators.module_gen ()) (fun module1 -> + let*! empty_tree = empty_tree () in + (* We need to print here in order to force lazy bindings to be evaluated. *) + let _ = print module1 in + let*! tree1 = + Merklizer.encode + Wasm_encoding.module_instance_encoding + module1 + empty_tree + in + let*! module2 = + Merklizer.decode Wasm_encoding.module_instance_encoding tree1 + in + (* We need to print here in order to force lazy bindings to be evaluated. *) + let _ = print module2 in + let*! tree2 = + Merklizer.encode + Wasm_encoding.module_instance_encoding + module2 + empty_tree + in + assert (Tree.equal tree1 tree2) ; + return_unit) + +let tests = + [ + tztest "Instruction roundtrip" `Quick test_instr_roundtrip; + tztest "Module roundtrip" `Quick test_module_roundtrip; + tztest "Module trees" `Quick test_module_tree; + ] diff --git a/src/lib_scoru_wasm/tree_encoding_decoding.ml b/src/lib_scoru_wasm/tree_encoding_decoding.ml index 9b64665c7ee47c1b0ac913695b7a70e03ba8bd0e..dab460e2a86ba5d7dc5be7e0c5c4159778e20537 100644 --- a/src/lib_scoru_wasm/tree_encoding_decoding.ml +++ b/src/lib_scoru_wasm/tree_encoding_decoding.ml @@ -25,6 +25,8 @@ open Tezos_webassembly_interpreter +exception Uninitialized_self_ref + type key = string list module type S = sig @@ -105,6 +107,19 @@ module type S = sig 'h t -> ('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h) t + val tup9 : + flatten:bool -> + 'a t -> + 'b t -> + 'c t -> + 'd t -> + 'e t -> + 'f t -> + 'g t -> + 'h t -> + 'i t -> + ('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i) t + val raw : key -> bytes t val value : key -> 'a Data_encoding.t -> 'a t @@ -133,6 +148,8 @@ module type S = sig val tagged_union : 'tag t -> ('tag, 'a) case list -> 'a t val option : 'a t -> 'a option t + + val with_self_reference : ('a Lazy.t -> 'a t) -> 'a t end module Make @@ -220,6 +237,12 @@ module Make (fun (a, b, c, d, e, f, g, h) -> (a, (b, c, d, e, f, g, h))) (tup2_ a (tup7_ b c d e f g h)) + let tup9_ a b c d e f g h i = + conv + (fun (a, (b, c, d, e, f, g, h, i)) -> (a, b, c, d, e, f, g, h, i)) + (fun (a, b, c, d, e, f, g, h, i) -> (a, (b, c, d, e, f, g, h, i))) + (tup2_ a (tup8_ b c d e f g h i)) + (* This is to allow for either flat composition of tuples or where each element of the tuple is wrapped under an index node. *) let flat_or_wrap ~flatten ix enc = @@ -279,6 +302,18 @@ module Make (flat_or_wrap ~flatten 7 g) (flat_or_wrap ~flatten 8 h) + let tup9 ~flatten a b c d e f g h i = + tup9_ + (flat_or_wrap ~flatten 1 a) + (flat_or_wrap ~flatten 2 b) + (flat_or_wrap ~flatten 3 c) + (flat_or_wrap ~flatten 4 d) + (flat_or_wrap ~flatten 5 e) + (flat_or_wrap ~flatten 6 f) + (flat_or_wrap ~flatten 7 g) + (flat_or_wrap ~flatten 8 h) + (flat_or_wrap ~flatten 9 i) + let encode {encode; _} value tree = E.run encode value tree let decode {decode; _} tree = D.run decode tree @@ -387,4 +422,24 @@ module Make (function None -> Some () | _ -> None) (fun () -> None); ] + + let with_self_reference f = + (* Mutable reference to the current value. *) + let current = ref None in + (* Sets the current value. *) + let set_current value = + current := Some value ; + value + in + (* Gets the current value from the ref. This should only be called once + the encoding/decoding steps have already constructed a value and the ref + has been updated. *) + let get_current () = + match !current with + | Some value -> value + | None -> raise Uninitialized_self_ref + in + (* Intercepts the encoding and decoding steps to update the reference to the + current module. *) + conv set_current set_current (f (lazy (get_current ()))) end diff --git a/src/lib_scoru_wasm/tree_encoding_decoding.mli b/src/lib_scoru_wasm/tree_encoding_decoding.mli index d8d3a2a2528e2e4bc301e14e1905194d765d4670..f5585457b87d1124808cfed8800c4117760e7446 100644 --- a/src/lib_scoru_wasm/tree_encoding_decoding.mli +++ b/src/lib_scoru_wasm/tree_encoding_decoding.mli @@ -25,6 +25,8 @@ open Tezos_webassembly_interpreter +exception Uninitialized_self_ref + (** A key in the tree is a list of string. *) type key = string trace @@ -172,6 +174,21 @@ module type S = sig 'h t -> ('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h) t + (** [tup9 ~flatten e1 e2 e3 e4 e5 e6 e7 e8 e9] combines the given encoders + [e1 .. e9] into an encoder for a tuple of nine elements. *) + val tup9 : + flatten:bool -> + 'a t -> + 'b t -> + 'c t -> + 'd t -> + 'e t -> + 'f t -> + 'g t -> + 'h t -> + 'i t -> + ('a * 'b * 'c * 'd * 'e * 'f * 'g * 'h * 'i) t + (** [raw key] is an encoder for bytes under the given [key]. *) val raw : key -> bytes t @@ -228,6 +245,12 @@ module type S = sig (** [option enc] lifts the given encoding [enc] to one that can encode optional values. *) val option : 'a t -> 'a option t + + (** [with_self_reference f] creates an encoder that allows accessing the + encoded/decoded value itself. It's useful for encoding cyclic + data-structures. Here, [f] is a function that takes the (lazy) + self-reference as an argument and constructs an encoder. *) + val with_self_reference : ('a Lazy.t -> 'a t) -> 'a t end (** Produces an encoder/decoder module with the provided map, vector and tree diff --git a/src/lib_scoru_wasm/wasm_decodings.ml b/src/lib_scoru_wasm/wasm_decodings.ml deleted file mode 100644 index 286a73f1f0bb0dd6ab8429b557ebf0bec84203dd..0000000000000000000000000000000000000000 --- a/src/lib_scoru_wasm/wasm_decodings.ml +++ /dev/null @@ -1,577 +0,0 @@ -(*****************************************************************************) -(* *) -(* Open Source License *) -(* Copyright (c) 2022 TriliTech *) -(* *) -(* 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.Instance -open Tezos_webassembly_interpreter -open Types -open Data_encoding_utils - -module type S = sig - type tree - - type 'a t - - val run : 'a t -> tree -> 'a Lwt.t - - val module_instance_decoding : module_inst Vector.t -> module_inst t - - val module_instances_decoding : module_inst Vector.t t -end - -module Make (T : Tree.S) = struct - include Tree_decoding.Make (T) - - let list_decoding item_enc = - let open Syntax in - let* length = value ["length"] Data_encoding.int32 in - let* head = value ["head"] Data_encoding.int32 in - let* get_item = lazy_mapping (fun key -> [Int32.to_string key]) item_enc in - let produce_value key = get_item (Int32.add head key) in - let vector = Instance.Vector.create ~produce_value length in - (* TODO: #3076 - This should return a [Instance.Vector.t] instead of a list. Once the AST - has been sufficiently adapted to lazy vectors and maps, this change can - go forward. *) - of_lwt (Instance.Vector.to_list vector) - - let ref_decoding_for ref_type modules = - let open Syntax in - match ref_type with - | FuncRefType -> - let* modul_id = value ["module"] Data_encoding.int32 in - let* func_id = value ["function"] Data_encoding.int32 in - of_lwt - (let open Lwt_syntax in - let* modul = Instance.Vector.get modul_id modules in - let+ func = Instance.Vector.get func_id modul.Instance.funcs in - Instance.FuncRef func) - | ExternRefType -> - let+ value = value ["value"] Data_encoding.int32 in - Values.ExternRef value - - let ref_decoding modules = - let open Syntax in - let* ref_type = - value ["type"] Interpreter_encodings.Types.ref_type_encoding - in - ref_decoding_for ref_type modules - - let value_decoding modules = - let open Syntax in - let* value_type = - value ["type"] Interpreter_encodings.Types.value_type_encoding - in - let get_value_with enc f = - let+ value = value ["value"] enc in - f value - in - match value_type with - | NumType I32Type -> - get_value_with Data_encoding.int32 (fun x -> Values.Num (Values.I32 x)) - | NumType I64Type -> - get_value_with Data_encoding.int64 (fun x -> Values.Num (Values.I64 x)) - | VecType V128Type -> - get_value_with Data_encoding.string (fun x -> - Values.Vec (V128 (V128.of_bits x))) - | RefType ref_type -> - let+ ref_value = ref_decoding_for ref_type modules in - Values.Ref ref_value - | _ -> Stdlib.failwith "Unsupported value_type" - - let var_list_decoding = - list_decoding (value [] Interpreter_encodings.Ast.var_encoding) - - let memory_chunk_decoding = - let open Syntax in - let+ bytes = raw [] in - Chunked_byte_vector.Chunk.of_bytes bytes - - let memory_decoding = - let open Syntax in - let+ min_pages = value ["min"] Data_encoding.int32 - and+ max_pages = value ["max"] Data_encoding.int32 - and+ get_chunk = - scope - ["chunks"] - (lazy_mapping - (fun index -> [Int64.to_string index]) - memory_chunk_decoding) - in - let length = Int64.(of_int32 min_pages |> mul Memory.page_size) in - let chunks = Chunked_byte_vector.Lwt.create ~get_chunk length in - Memory.of_chunks (MemoryType {min = min_pages; max = Some max_pages}) chunks - - let table_decoding modules = - let open Syntax in - let+ min = value ["min"] Data_encoding.int32 - and+ max = value ["max"] Data_encoding.int32 - and+ get_ref = - scope - ["refs"] - (lazy_mapping - (fun index -> [Int32.to_string index]) - (ref_decoding modules)) - in - let table_type = TableType ({min; max = Some max}, FuncRefType) in - let table_entries = Table.Vector.Vector.create ~produce_value:get_ref min in - Table.of_lazy_vector table_type table_entries - - let global_decoding modules = - let open Syntax in - let+ type_ = value ["type"] Interpreter_encodings.Types.mutability_encoding - and+ value = scope ["value"] (value_decoding modules) in - let ty = GlobalType (Values.type_of_value value, type_) in - Global.alloc ty value - - let lazy_vector_decoding field_name tree_encoding = - let open Syntax in - let+ count = value ["num-" ^ field_name] Data_encoding.int32 - and+ get_instance = - scope - [field_name] - (lazy_mapping (fun index -> [Int32.to_string index]) tree_encoding) - in - Instance.Vector.create ~produce_value:get_instance count - - let memory_instance_decoding = lazy_vector_decoding "memories" memory_decoding - - let table_instance_decoding modules = - lazy_vector_decoding "tables" (table_decoding modules) - - let global_instance_decoding modules = - lazy_vector_decoding "globals" (global_decoding modules) - - let data_decoding = - let open Syntax in - let+ length = value ["length"] Data_encoding.int64 - and+ get_chunk = - lazy_mapping (fun index -> [Int64.to_string index]) memory_chunk_decoding - in - ref (Chunked_byte_vector.Lwt.create ~get_chunk length) - - let data_instance_decoding = lazy_vector_decoding "datas" data_decoding - - let block_label_decoding = - value [] Interpreter_encodings.Ast.block_label_encoding - - let instruction_decoding = - let open Ast in - let open Syntax in - let* tag = value ["tag"] Data_encoding.string in - let+ instr = - match tag with - | "Unreachable" -> return Unreachable - | "Nop" -> return Nop - | "Drop" -> return Drop - | "Select" -> - let+ param = - value - ["$1"] - Data_encoding.( - option (list Interpreter_encodings.Types.value_type_encoding)) - in - (* `Select` actually accepts only one value, but is a list for some - reason. See [Valid.check_instr] for reference or the reference - documentation. *) - Select param - | "Block" -> - let+ type_ = - value ["$1"] Interpreter_encodings.Ast.block_type_encoding - and+ instrs = scope ["$2"] block_label_decoding in - Block (type_, instrs) - | "Loop" -> - let+ type_ = - value ["$1"] Interpreter_encodings.Ast.block_type_encoding - and+ instrs = scope ["$2"] block_label_decoding in - Loop (type_, instrs) - | "If" -> - let+ type_ = - value ["$1"] Interpreter_encodings.Ast.block_type_encoding - and+ instrs_if = scope ["$2"] block_label_decoding - and+ instrs_else = scope ["$3"] block_label_decoding in - If (type_, instrs_if, instrs_else) - | "Br" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - Br var - | "BrIf" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - BrIf var - | "BrTable" -> - let+ table = scope ["$1"] var_list_decoding - and+ target = value ["$2"] Interpreter_encodings.Ast.var_encoding in - BrTable (table, target) - | "Return" -> return Return - | "Call" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - Call var - | "CallIndirect" -> - let+ var1 = value ["$1"] Interpreter_encodings.Ast.var_encoding - and+ var2 = value ["$2"] Interpreter_encodings.Ast.var_encoding in - CallIndirect (var1, var2) - | "LocalGet" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - LocalGet var - | "LocalSet" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - LocalSet var - | "LocalTee" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - LocalTee var - | "GlobalGet" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - GlobalGet var - | "GlobalSet" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - GlobalSet var - | "TableGet" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - TableGet var - | "TableSet" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - TableSet var - | "TableSize" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - TableSize var - | "TableGrow" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - TableGrow var - | "TableFill" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - TableFill var - | "TableCopy" -> - let+ var1 = value ["$1"] Interpreter_encodings.Ast.var_encoding - and+ var2 = value ["$2"] Interpreter_encodings.Ast.var_encoding in - TableCopy (var1, var2) - | "TableInit" -> - let+ var1 = value ["$1"] Interpreter_encodings.Ast.var_encoding - and+ var2 = value ["$2"] Interpreter_encodings.Ast.var_encoding in - TableInit (var1, var2) - | "ElemDrop" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - ElemDrop var - | "Load" -> - let+ loadop = - value ["$1"] Interpreter_encodings.Ast.loadop_encoding - in - Load loadop - | "Store" -> - let+ storeop = - value ["$1"] Interpreter_encodings.Ast.storeop_encoding - in - Store storeop - | "VecLoad" -> - let+ vec_loadop = - value ["$1"] Interpreter_encodings.Ast.vec_loadop_encoding - in - VecLoad vec_loadop - | "VecStore" -> - let+ vec_storeop = - value ["$1"] Interpreter_encodings.Ast.vec_storeop_encoding - in - VecStore vec_storeop - | "VecLoadLane" -> - let+ vec_laneop = - value ["$1"] Interpreter_encodings.Ast.vec_laneop_encoding - in - VecLoadLane vec_laneop - | "VecStoreLane" -> - let+ vec_laneop = - value ["$1"] Interpreter_encodings.Ast.vec_laneop_encoding - in - VecStoreLane vec_laneop - | "MemorySize" -> return MemorySize - | "MemoryGrow" -> return MemoryGrow - | "MemoryFill" -> return MemoryFill - | "MemoryCopy" -> return MemoryCopy - | "MemoryInit" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - MemoryInit var - | "DataDrop" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - DataDrop var - | "RefNull" -> - let+ ref_type = - value ["$1"] Interpreter_encodings.Types.ref_type_encoding - in - RefNull ref_type - | "RefFunc" -> - let+ var = value ["$1"] Interpreter_encodings.Ast.var_encoding in - RefFunc var - | "RefIsNull" -> return RefIsNull - | "Const" -> - let+ num = value ["$1"] Interpreter_encodings.Ast.num_encoding in - Const num - | "Test" -> - let+ param = value ["$1"] Interpreter_encodings.Ast.testop_encoding in - Test param - | "Compare" -> - let+ param = value ["$1"] Interpreter_encodings.Ast.relop_encoding in - Compare param - | "Unary" -> - let+ param = value ["$1"] Interpreter_encodings.Ast.unop_encoding in - Unary param - | "Binary" -> - let+ param = value ["$1"] Interpreter_encodings.Ast.binop_encoding in - Binary param - | "Convert" -> - let+ param = value ["$1"] Interpreter_encodings.Ast.cvtop_encoding in - Convert param - | "VecConst" -> - let+ vec = value ["$1"] Interpreter_encodings.Ast.vec_encoding in - VecConst vec - | "VecTest" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_testop_encoding - in - VecTest op - | "VecCompare" -> - let+ op = value ["$1"] Interpreter_encodings.Ast.vec_relop_encoding in - VecCompare op - | "VecUnary" -> - let+ op = value ["$1"] Interpreter_encodings.Ast.vec_unop_encoding in - VecUnary op - | "VecBinary" -> - let+ op = value ["$1"] Interpreter_encodings.Ast.vec_binop_encoding in - VecBinary op - | "VecConvert" -> - let+ op = value ["$1"] Interpreter_encodings.Ast.vec_cvtop_encoding in - VecConvert op - | "VecShift" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_shiftop_encoding - in - VecShift op - | "VecBitmask" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_bitmaskop_encoding - in - VecBitmask op - | "VecTestBits" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_vtestop_encoding - in - VecTestBits op - | "VecUnaryBits" -> - let+ op = value ["$1"] Interpreter_encodings.Ast.vec_vunop_encoding in - VecUnaryBits op - | "VecBinaryBits" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_vbinop_encoding - in - VecBinaryBits op - | "VecTernaryBits" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_vternop_encoding - in - VecTernaryBits op - | "VecSplat" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_splatop_encoding - in - VecSplat op - | "VecExtract" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_extractop_encoding - in - VecExtract op - | "VecReplace" -> - let+ op = - value ["$1"] Interpreter_encodings.Ast.vec_replaceop_encoding - in - VecReplace op - | _ -> Stdlib.failwith (Printf.sprintf "Unknown instruction tag %s" tag) - in - Source.(instr @@ no_region) - - let func_type_decoding () = - let open Syntax in - let* params = - lazy_vector_decoding - "type_params" - (value [] Interpreter_encodings.Types.value_type_encoding) - in - let+ result = - lazy_vector_decoding - "type_result" - (value [] Interpreter_encodings.Types.value_type_encoding) - in - Types.FuncType (params, result) - - let function_decoding current_module = - let open Syntax in - let* is_host_func = - value - ["kind"] - (Data_encoding.string_enum [("host", true); ("native", false)]) - in - if is_host_func then - let* global_name = value ["global_name"] Data_encoding.string in - let+ func_type = scope ["func_type"] (func_type_decoding ()) in - Func.HostFunc (func_type, global_name) - else - let* type_ = func_type_decoding () in - let* ftype = value ["ftype"] Interpreter_encodings.Ast.var_encoding in - let* locals = - lazy_vector_decoding - "locals" - (value [] Interpreter_encodings.Types.value_type_encoding) - in - let+ body = block_label_decoding in - let func = Ast.{ftype; locals; body} in - Func.AstFunc (type_, current_module, Source.(func @@ no_region)) - - let function_instance_decoding current_module = - lazy_vector_decoding "functions" (function_decoding current_module) - - let type_instance_decoding = - lazy_vector_decoding "types" (func_type_decoding ()) - - let elem_decoding funcs = - let open Syntax in - let+ length = value ["length"] Data_encoding.int32 - and+ get_ref = - lazy_mapping - (fun index -> [Int32.to_string index]) - (value [] Data_encoding.int32) - in - let produce_value index = - let open Lwt_syntax in - let* func_id = get_ref index in - let+ func = Instance.Vector.get func_id funcs in - Instance.FuncRef func - in - ref (Instance.Vector.create ~produce_value length) - - let elem_instance_decoding funcs = - lazy_vector_decoding "elements" (elem_decoding funcs) - - type export = - | ExportFunc of int32 - | ExportTable of int32 - | ExportMemory of int32 - | ExportGlobal of int32 - - let export_decoding funcs tables memories globals = - let open Syntax in - let* export = - value - [] - Data_encoding.( - union_incr - [ - case_incr - "ExportFunc" - int32 - (function ExportFunc x -> Some x | _ -> None) - (fun x -> ExportFunc x); - case_incr - "ExportTable" - int32 - (function ExportTable x -> Some x | _ -> None) - (fun x -> ExportTable x); - case_incr - "ExportMemory" - int32 - (function ExportMemory x -> Some x | _ -> None) - (fun x -> ExportMemory x); - case_incr - "ExportGlobal" - int32 - (function ExportGlobal x -> Some x | _ -> None) - (fun x -> ExportGlobal x); - ]) - in - match export with - | ExportFunc index -> - let+ value = of_lwt (Instance.Vector.get index funcs) in - Instance.ExternFunc value - | ExportTable index -> - let+ value = of_lwt (Instance.Vector.get index tables) in - Instance.ExternTable value - | ExportMemory index -> - let+ value = of_lwt (Instance.Vector.get index memories) in - Instance.ExternMemory value - | ExportGlobal index -> - let+ value = of_lwt (Instance.Vector.get index globals) in - Instance.ExternGlobal value - - let export_instance_decoding funcs tables memories globals = - let open Syntax in - let+ get_export = - lazy_mapping - (fun name -> Format.[asprintf "%a" (pp_print_list pp_print_int) name]) - (export_decoding funcs tables memories globals) - in - Instance.NameMap.create ~produce_value:get_export () - - let instruction_instance_decoding = - lazy_vector_decoding "instructions" instruction_decoding - - let block_instance_decoding = - lazy_vector_decoding "blocks" instruction_instance_decoding - - let module_instance_decoding modules = - let open Syntax in - let current_module = ref Instance.empty_module_inst in - let* memories = memory_instance_decoding in - let* tables = table_instance_decoding modules in - let* globals = global_instance_decoding modules in - let* datas = data_instance_decoding in - let* types = type_instance_decoding in - let* funcs = function_instance_decoding current_module in - let* elems = elem_instance_decoding funcs in - let* exports = export_instance_decoding funcs tables memories globals in - let+ blocks = block_instance_decoding in - let modul = - Instance. - {memories; tables; globals; datas; types; funcs; elems; exports; blocks} - in - current_module := modul ; - modul - - let module_instances_decoding = - let open Syntax in - let self = ref (fun () -> Stdlib.failwith "Uninitialized!") in - let* count = value ["count"] Data_encoding.int32 in - let modules = - (* This is a proxy for the mapping returned by the function contained in - [self]. *) - Instance.Vector.create - ~produce_value:(fun index -> Lazy_map.LwtInt32Map.get index (!self ())) - count - in - let+ get_module_inst = - lazy_mapping - (fun index -> [Int32.to_string index]) - (module_instance_decoding modules) - in - let self_modules = - Lazy_map.LwtInt32Map.create ~produce_value:get_module_inst () - in - (* Update the self pointer to effectively close the recursion - [self_modules -> get_module_inst -> modules -> self -> self_modules]. *) - (self := fun () -> self_modules) ; - modules -end diff --git a/src/lib_scoru_wasm/wasm_decodings.mli b/src/lib_scoru_wasm/wasm_decodings.mli deleted file mode 100644 index b5c24441631addd8bf722872f053d894a8f1a23b..0000000000000000000000000000000000000000 --- a/src/lib_scoru_wasm/wasm_decodings.mli +++ /dev/null @@ -1,48 +0,0 @@ -(*****************************************************************************) -(* *) -(* Open Source License *) -(* Copyright (c) 2022 TriliTech *) -(* *) -(* 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.Instance - -(** A module type representing WASM specific decodings. *) -module type S = sig - (** Represents decoders. *) - type 'a t - - (** Represents values encoded as trees. *) - type tree - - (** [run decoder tree] runs the tree decoder against the tree. *) - val run : 'a t -> tree -> 'a Lwt.t - - (** [module_instance_encoding modules] allows you to decode a module instance. - It requires a vector of previously decoded modules for references. *) - val module_instance_decoding : module_inst Vector.t -> module_inst t - - (** [module_instances_decoding] decodes module instances. *) - val module_instances_decoding : module_inst Vector.t t -end - -(** Creates a WASM decoding module given a {!Tree.S} implementation. *) -module Make (T : Tree.S) : S with type tree = T.tree diff --git a/src/lib_scoru_wasm/wasm_encoding.ml b/src/lib_scoru_wasm/wasm_encoding.ml new file mode 100644 index 0000000000000000000000000000000000000000..1009ca6b6880dd5aa69ec4ea911c808085451d3a --- /dev/null +++ b/src/lib_scoru_wasm/wasm_encoding.ml @@ -0,0 +1,706 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 TriliTech *) +(* *) +(* 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 + +exception Uninitialized_current_module + +module Make + (Tree_encoding_decoding : Tree_encoding_decoding.S + with type vector_key = int32 + and type 'a vector = 'a Instance.Vector.t + and type 'a map = 'a Instance.NameMap.t + and type chunked_byte_vector = + Chunked_byte_vector.Lwt.t) = +struct + module V = Instance.Vector + module M = Instance.NameMap + module C = Chunked_byte_vector.Lwt + include Tree_encoding_decoding + + (** Utility function*) + let string_tag = value [] Data_encoding.string + + let list_encoding item_enc = + let vector = lazy_vector (value [] Data_encoding.int32) item_enc in + (* TODO: #3076 + This should return a [Instance.Vector.t] instead of a list. Once the AST + has been sufficiently adapted to lazy vectors and maps, this change can + go forward. *) + conv_lwt V.to_list (fun list -> Lwt.return (V.of_list list)) vector + + let lazy_vector_encoding field_name enc = + scope [field_name] (lazy_vector (value [] Data_encoding.int32) enc) + + let function_type_encoding = + conv + (fun (params, result) -> Types.FuncType (params, result)) + (function Types.FuncType (params, result) -> (params, result)) + (tup2 + ~flatten:false + (lazy_vector_encoding + "type-params" + (value [] Interpreter_encodings.Types.value_type_encoding)) + (lazy_vector_encoding + "type-result" + (value [] Interpreter_encodings.Types.value_type_encoding))) + + let var_list_encoding = + list_encoding (value [] Interpreter_encodings.Ast.var_encoding) + + let block_label_encoding = + value [] Interpreter_encodings.Ast.block_label_encoding + + let instruction_encoding = + let unit_encoding = value [] Data_encoding.unit in + let open Ast in + conv + (fun instr -> Source.{at = no_region; it = instr}) + (fun Source.{at = _; it} -> it) + (tagged_union + string_tag + [ + case + "Unreachable" + unit_encoding + (function Unreachable -> Some () | _ -> None) + (fun () -> Unreachable); + case + "Nop" + unit_encoding + (function Nop -> Some () | _ -> None) + (fun () -> Nop); + case + "Drop" + unit_encoding + (function Drop -> Some () | _ -> None) + (fun () -> Drop); + case + "Select" + (value + ["$1"] + (* `Select` actually accepts only one value, but is a list for some + reason. See [Valid.check_instr] for reference or the reference + documentation. *) + Data_encoding.( + option (list Interpreter_encodings.Types.value_type_encoding))) + (function Select p -> Some p | _ -> None) + (fun p -> Select p); + case + "Block" + (tup2 + ~flatten:false + (value ["$1"] Interpreter_encodings.Ast.block_type_encoding) + (scope ["$2"] block_label_encoding)) + (function + | Block (type_, instr) -> Some (type_, instr) | _ -> None) + (fun (type_, instr) -> Block (type_, instr)); + case + "Loop" + (tup2 + ~flatten:false + (value ["$1"] Interpreter_encodings.Ast.block_type_encoding) + (scope ["$2"] block_label_encoding)) + (function Loop (type_, instr) -> Some (type_, instr) | _ -> None) + (fun (type_, instr) -> Loop (type_, instr)); + case + "If" + (tup3 + ~flatten:false + (value ["$1"] Interpreter_encodings.Ast.block_type_encoding) + (scope ["$2"] block_label_encoding) + (scope ["$3"] block_label_encoding)) + (function + | If (type_, instr_if, instrs_else) -> + Some (type_, instr_if, instrs_else) + | _ -> None) + (fun (type_, instrs_if, instrs_else) -> + If (type_, instrs_if, instrs_else)); + case + "Br" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function Br var -> Some var | _ -> None) + (fun var -> Br var); + case + "BrIf" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function BrIf var -> Some var | _ -> None) + (fun var -> BrIf var); + case + "BrTable" + (tup2 + ~flatten:false + (scope ["$1"] var_list_encoding) + (value ["$2"] Interpreter_encodings.Ast.var_encoding)) + (function + | BrTable (table, target) -> Some (table, target) | _ -> None) + (fun (table, target) -> BrTable (table, target)); + case + "Return" + unit_encoding + (function Return -> Some () | _ -> None) + (fun () -> Return); + case + "Call" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function Call var -> Some var | _ -> None) + (fun var -> Call var); + case + "CallIndirect" + (tup2 + ~flatten:false + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (value ["$2"] Interpreter_encodings.Ast.var_encoding)) + (function + | CallIndirect (var1, var2) -> Some (var1, var2) | _ -> None) + (fun (var1, var2) -> CallIndirect (var1, var2)); + case + "LocalGet" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function LocalGet var -> Some var | _ -> None) + (fun var -> LocalGet var); + case + "LocalSet" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function LocalSet var -> Some var | _ -> None) + (fun var -> LocalSet var); + case + "LocalTee" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function LocalTee var -> Some var | _ -> None) + (fun var -> LocalTee var); + case + "GlobalGet" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function GlobalGet var -> Some var | _ -> None) + (fun var -> GlobalGet var); + case + "GlobalSet" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function GlobalSet var -> Some var | _ -> None) + (fun var -> GlobalSet var); + case + "TableGet" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function TableGet var -> Some var | _ -> None) + (fun var -> TableGet var); + case + "TableSet" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function TableSet var -> Some var | _ -> None) + (fun var -> TableSet var); + case + "TableSize" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function TableSize var -> Some var | _ -> None) + (fun var -> TableSize var); + case + "TableGrow" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function TableGrow var -> Some var | _ -> None) + (fun var -> TableGrow var); + case + "TableFill" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function TableFill var -> Some var | _ -> None) + (fun var -> TableFill var); + case + "TableCopy" + (tup2 + ~flatten:false + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (value ["$2"] Interpreter_encodings.Ast.var_encoding)) + (function + | TableCopy (var1, var2) -> Some (var1, var2) | _ -> None) + (fun (var1, var2) -> TableCopy (var1, var2)); + case + "TableInit" + (tup2 + ~flatten:false + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (value ["$2"] Interpreter_encodings.Ast.var_encoding)) + (function + | TableInit (var1, var2) -> Some (var1, var2) | _ -> None) + (fun (var1, var2) -> TableInit (var1, var2)); + case + "ElemDrop" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function ElemDrop var -> Some var | _ -> None) + (fun var -> ElemDrop var); + case + "Load" + (value ["$1"] Interpreter_encodings.Ast.loadop_encoding) + (function Load loadop -> Some loadop | _ -> None) + (fun loadop -> Load loadop); + case + "Store" + (value ["$1"] Interpreter_encodings.Ast.storeop_encoding) + (function Store loadop -> Some loadop | _ -> None) + (fun loadop -> Store loadop); + case + "VecLoad" + (value ["$1"] Interpreter_encodings.Ast.vec_loadop_encoding) + (function VecLoad vec_loadop -> Some vec_loadop | _ -> None) + (fun vec_loadop -> VecLoad vec_loadop); + case + "VecStore" + (value ["$1"] Interpreter_encodings.Ast.vec_storeop_encoding) + (function VecStore vec_loadop -> Some vec_loadop | _ -> None) + (fun vec_storeop -> VecStore vec_storeop); + case + "VecLoadLane" + (value ["$1"] Interpreter_encodings.Ast.vec_laneop_encoding) + (function VecLoadLane vec_laneop -> Some vec_laneop | _ -> None) + (fun vec_laneop -> VecLoadLane vec_laneop); + case + "VecStoreLane" + (value ["$1"] Interpreter_encodings.Ast.vec_laneop_encoding) + (function VecStoreLane vec_laneop -> Some vec_laneop | _ -> None) + (fun vec_laneop -> VecStoreLane vec_laneop); + case + "MemorySize" + unit_encoding + (function MemorySize -> Some () | _ -> None) + (fun () -> MemorySize); + case + "MemoryGrow" + unit_encoding + (function MemoryGrow -> Some () | _ -> None) + (fun () -> MemoryGrow); + case + "MemoryFill" + unit_encoding + (function MemoryFill -> Some () | _ -> None) + (fun () -> MemoryFill); + case + "MemoryCopy" + unit_encoding + (function MemoryCopy -> Some () | _ -> None) + (fun () -> MemoryCopy); + case + "MemoryInit" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function MemoryInit var -> Some var | _ -> None) + (fun var -> MemoryInit var); + case + "DataDrop" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function DataDrop var -> Some var | _ -> None) + (fun var -> DataDrop var); + case + "RefNull" + (value ["$1"] Interpreter_encodings.Types.ref_type_encoding) + (function RefNull ref_type -> Some ref_type | _ -> None) + (fun ref_type -> RefNull ref_type); + case + "RefFunc" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function RefFunc var -> Some var | _ -> None) + (fun var -> RefFunc var); + case + "RefFunc" + (value ["$1"] Interpreter_encodings.Ast.var_encoding) + (function RefFunc var -> Some var | _ -> None) + (fun var -> RefFunc var); + case + "RefIsNull" + unit_encoding + (function RefIsNull -> Some () | _ -> None) + (fun () -> RefIsNull); + case + "Const" + (value ["$1"] Interpreter_encodings.Ast.num_encoding) + (function Const var -> Some var | _ -> None) + (fun var -> Const var); + case + "Test" + (value ["$1"] Interpreter_encodings.Ast.testop_encoding) + (function Test var -> Some var | _ -> None) + (fun var -> Test var); + case + "Compare" + (value ["$1"] Interpreter_encodings.Ast.relop_encoding) + (function Compare var -> Some var | _ -> None) + (fun var -> Compare var); + case + "Unary" + (value ["$1"] Interpreter_encodings.Ast.unop_encoding) + (function Unary var -> Some var | _ -> None) + (fun var -> Unary var); + case + "Binary" + (value ["$1"] Interpreter_encodings.Ast.binop_encoding) + (function Binary var -> Some var | _ -> None) + (fun var -> Binary var); + case + "Convert" + (value ["$1"] Interpreter_encodings.Ast.cvtop_encoding) + (function Convert var -> Some var | _ -> None) + (fun var -> Convert var); + case + "VecConst" + (value ["$1"] Interpreter_encodings.Ast.vec_encoding) + (function VecConst vec -> Some vec | _ -> None) + (fun vec -> VecConst vec); + case + "VecTest" + (value ["$1"] Interpreter_encodings.Ast.vec_testop_encoding) + (function VecTest op -> Some op | _ -> None) + (fun op -> VecTest op); + case + "VecCompare" + (value ["$1"] Interpreter_encodings.Ast.vec_relop_encoding) + (function VecCompare op -> Some op | _ -> None) + (fun op -> VecCompare op); + case + "VecUnary" + (value ["$1"] Interpreter_encodings.Ast.vec_unop_encoding) + (function VecUnary op -> Some op | _ -> None) + (fun op -> VecUnary op); + case + "VecBinary" + (value ["$1"] Interpreter_encodings.Ast.vec_binop_encoding) + (function VecBinary op -> Some op | _ -> None) + (fun op -> VecBinary op); + case + "VecConvert" + (value ["$1"] Interpreter_encodings.Ast.vec_cvtop_encoding) + (function VecConvert op -> Some op | _ -> None) + (fun op -> VecConvert op); + case + "VecShift" + (value ["$1"] Interpreter_encodings.Ast.vec_shiftop_encoding) + (function VecShift op -> Some op | _ -> None) + (fun op -> VecShift op); + case + "VecBitmask" + (value ["$1"] Interpreter_encodings.Ast.vec_bitmaskop_encoding) + (function VecBitmask op -> Some op | _ -> None) + (fun op -> VecBitmask op); + case + "VecTestBits" + (value ["$1"] Interpreter_encodings.Ast.vec_vtestop_encoding) + (function VecTestBits op -> Some op | _ -> None) + (fun op -> VecTestBits op); + case + "VecUnaryBits" + (value ["$1"] Interpreter_encodings.Ast.vec_vunop_encoding) + (function VecUnaryBits op -> Some op | _ -> None) + (fun op -> VecUnaryBits op); + case + "VecBinaryBits" + (value ["$1"] Interpreter_encodings.Ast.vec_vbinop_encoding) + (function VecBinaryBits op -> Some op | _ -> None) + (fun op -> VecBinaryBits op); + case + "VecTernaryBits" + (value ["$1"] Interpreter_encodings.Ast.vec_vternop_encoding) + (function VecTernaryBits op -> Some op | _ -> None) + (fun op -> VecTernaryBits op); + case + "VecSplat" + (value ["$1"] Interpreter_encodings.Ast.vec_splatop_encoding) + (function VecSplat op -> Some op | _ -> None) + (fun op -> VecSplat op); + case + "VecExtract" + (value ["$1"] Interpreter_encodings.Ast.vec_extractop_encoding) + (function VecExtract op -> Some op | _ -> None) + (fun op -> VecExtract op); + case + "VecReplace" + (value ["$1"] Interpreter_encodings.Ast.vec_replaceop_encoding) + (function VecReplace op -> Some op | _ -> None) + (fun op -> VecReplace op); + ]) + + let func_type_encoding = + conv + (fun (type_params, type_result) -> + Types.FuncType (type_params, type_result)) + (fun (Types.FuncType (type_params, type_result)) -> + (type_params, type_result)) + (tup2 + ~flatten:false + (lazy_vector_encoding + "type_params" + (value [] Interpreter_encodings.Types.value_type_encoding)) + (lazy_vector_encoding + "type_result" + (value [] Interpreter_encodings.Types.value_type_encoding))) + + let function_encoding ~current_module = + tagged_union + string_tag + [ + case + "Host" + (tup2 + ~flatten:false + func_type_encoding + (value ["name"] Data_encoding.string)) + (function + | Func.HostFunc (func_type, name) -> Some (func_type, name) + | _ -> None) + (fun (func_type, name) -> Func.HostFunc (func_type, name)); + case + "Native" + (tup4 + ~flatten:false + function_type_encoding + (value ["ftype"] Interpreter_encodings.Ast.var_encoding) + (lazy_vector_encoding + "locals" + (value [] Interpreter_encodings.Types.value_type_encoding)) + block_label_encoding) + (function + | Func.AstFunc + (type_, _current_module, {at = _; it = {ftype; locals; body}}) + -> + (* Note that we do not encode [_current_module] to avoid + infinite recursion. Instead we use the given self-reference + [current_module] on decoding. *) + Some (type_, ftype, locals, body) + | _ -> None) + (fun (type_, ftype, locals, body) -> + let func = + Source.{at = no_region; it = {Ast.ftype; locals; body}} + in + Func.AstFunc (type_, Lazy.force current_module, func)); + ] + + let value_ref_encoding ~current_module = + tagged_union + string_tag + [ + case + "FuncRef" + (function_encoding ~current_module) + (fun val_ref -> + match val_ref with + | Instance.FuncRef func_inst -> Some func_inst + | _ -> None) + (fun func -> Instance.FuncRef func); + case + "ExternRef" + (value [] Data_encoding.int32) + (function Values.ExternRef v -> Some v | _ -> None) + (fun v -> Values.ExternRef v); + case + "NullRef" + (value [] Interpreter_encodings.Types.ref_type_encoding) + (function Values.NullRef v -> Some v | _ -> None) + (fun v -> Values.NullRef v); + ] + + let value_encoding ~current_module = + tagged_union + string_tag + [ + case + "NumType" + (value [] Interpreter_encodings.Values.num_encoding) + (function Values.Num n -> Some n | _ -> None) + (fun n -> Values.Num n); + case + "VecType V128Type" + (value [] Interpreter_encodings.Values.vec_encoding) + (function Values.Vec v -> Some v | _ -> None) + (fun v -> Values.Vec v); + case + "RefType" + (value_ref_encoding ~current_module) + (function Values.Ref r -> Some r | _ -> None) + (fun r -> Values.Ref r); + ] + + let memory_encoding = + conv + (fun (min, max, chunks) -> + Memory.of_chunks (MemoryType {min; max}) chunks) + (fun memory_inst -> + let (MemoryType {min; max}) = Memory.type_of memory_inst in + let content = Memory.content memory_inst in + (min, max, content)) + (tup3 + ~flatten:false + (value ["min"] Data_encoding.int32) + (value_option ["max"] Data_encoding.int32) + (scope ["chunks"] chunked_byte_vector)) + + let table_encoding ~current_module = + conv + (fun (min, max, vector, ref_type) -> + let table_type = Types.TableType ({min; max}, ref_type) in + Table.of_lazy_vector table_type vector) + (fun table -> + let (Types.TableType ({min; max}, ref_type)) = Table.type_of table in + (min, max, Table.content table, ref_type)) + (tup4 + ~flatten:false + (value ["min"] Data_encoding.int32) + (value_option ["max"] Data_encoding.int32) + (lazy_vector_encoding "refs" (value_ref_encoding ~current_module)) + (value ["ref-type"] Interpreter_encodings.Types.ref_type_encoding)) + + let global_encoding ~current_module = + conv + (fun (type_, value) -> + let ty = Types.GlobalType (Values.type_of_value value, type_) in + Global.alloc ty value) + (fun global -> + let (Types.GlobalType (_, mutability)) = Global.type_of global in + let value = Global.load global in + (mutability, value)) + (tup2 + ~flatten:false + (value ["type"] Interpreter_encodings.Types.mutability_encoding) + (scope ["value"] (value_encoding ~current_module))) + + let memory_instance_encoding = lazy_vector_encoding "memories" memory_encoding + + let table_vector_encoding ~current_module = + lazy_vector_encoding "tables" (table_encoding ~current_module) + + let global_vector_encoding ~current_module = + lazy_vector_encoding "globals" (global_encoding ~current_module) + + let chunked_byte_vector_ref_encoding = + conv (fun x -> ref x) (fun r -> !r) chunked_byte_vector + + let function_vector_encoding ~current_module = + lazy_vector_encoding "functions" (function_encoding ~current_module) + + let function_type_vector_encoding = + lazy_vector_encoding "types" function_type_encoding + + let value_ref_vector_encoding ~current_module = + lazy_vector_encoding "refs" (value_ref_encoding ~current_module) + + let extern_map_encoding ~current_module = + lazy_mapping + (tagged_union + string_tag + [ + case + "ExternFunc" + (function_encoding ~current_module) + (function Instance.ExternFunc x -> Some x | _ -> None) + (fun x -> Instance.ExternFunc x); + case + "ExternTable" + (table_encoding ~current_module) + (function Instance.ExternTable x -> Some x | _ -> None) + (fun x -> Instance.ExternTable x); + case + "ExternMemory" + memory_encoding + (function Instance.ExternMemory x -> Some x | _ -> None) + (fun x -> Instance.ExternMemory x); + case + "ExternGlobal" + (global_encoding ~current_module) + (function Instance.ExternGlobal x -> Some x | _ -> None) + (fun x -> Instance.ExternGlobal x); + ]) + + let value_ref_vector_vector_encoding ~current_module = + lazy_vector_encoding + "elements" + (conv + (fun x -> ref x) + (fun r -> !r) + (value_ref_vector_encoding ~current_module)) + + let data_instance_encoding = + lazy_vector_encoding "datas" chunked_byte_vector_ref_encoding + + let block_table_encoding = + lazy_vector_encoding + "block-table" + (lazy_vector_encoding "instructions" instruction_encoding) + + let module_instance_encoding = + let open Lwt_syntax in + let gen_encoding current_module = + let current_module = Lazy.map (fun x -> ref x) current_module in + conv_lwt + (fun ( types, + funcs, + tables, + memories, + globals, + exports, + elems, + datas, + blocks ) -> + let open Lwt_syntax in + return + { + Instance.types; + funcs; + tables; + memories; + globals; + exports; + elems; + datas; + blocks; + }) + (fun { + Instance.types; + funcs; + tables; + memories; + globals; + exports; + elems; + datas; + blocks; + } -> + return + ( types, + funcs, + tables, + memories, + globals, + exports, + elems, + datas, + blocks )) + (tup9 + ~flatten:false + function_type_vector_encoding + (function_vector_encoding ~current_module) + (table_vector_encoding ~current_module) + memory_instance_encoding + (global_vector_encoding ~current_module) + (extern_map_encoding ~current_module) + (value_ref_vector_vector_encoding ~current_module) + data_instance_encoding + block_table_encoding) + in + scope ["module"] @@ with_self_reference gen_encoding +end diff --git a/src/lib_scoru_wasm/wasm_encoding.mli b/src/lib_scoru_wasm/wasm_encoding.mli new file mode 100644 index 0000000000000000000000000000000000000000..a9fdcba5579bad67ae241a3af8abf113e4ef72f0 --- /dev/null +++ b/src/lib_scoru_wasm/wasm_encoding.mli @@ -0,0 +1,94 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 TriliTech *) +(* *) +(* 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 + +exception Uninitialized_current_module + +module Make + (M : Tree_encoding_decoding.S + with type vector_key = int32 + and type 'a vector = 'a Instance.Vector.t + and type 'a map = 'a Instance.NameMap.t + and type chunked_byte_vector = Chunked_byte_vector.Lwt.t) : sig + type tree = M.tree + + type 'a t = 'a M.t + + val var_list_encoding : Ast.var list t + + val instruction_encoding : Ast.instr t + + val function_encoding : + current_module:Instance.module_inst ref Lazy.t -> Instance.func_inst t + + val value_ref_encoding : + current_module:Instance.module_inst ref Lazy.t -> Values.ref_ t + + val value_encoding : + current_module:Instance.module_inst ref Lazy.t -> Values.value t + + val memory_encoding : Partial_memory.memory t + + val table_encoding : + current_module:Instance.module_inst ref Lazy.t -> Partial_table.table t + + val global_encoding : + current_module:Instance.module_inst ref Lazy.t -> Global.global t + + val memory_instance_encoding : Partial_memory.memory Instance.Vector.t t + + val table_vector_encoding : + current_module:Instance.module_inst ref Lazy.t -> + Partial_table.table Instance.Vector.t t + + val global_vector_encoding : + current_module:Instance.module_inst ref Lazy.t -> + Global.global Instance.Vector.t t + + val chunked_byte_vector_ref_encoding : Chunked_byte_vector.Lwt.t ref t + + val function_vector_encoding : + current_module:Instance.module_inst ref Lazy.t -> + Instance.func_inst Instance.Vector.t t + + val function_type_vector_encoding : Types.func_type Instance.Vector.t t + + val value_ref_vector_encoding : + current_module:Instance.module_inst ref Lazy.t -> + Values.ref_ Instance.Vector.t t + + val extern_map_encoding : + current_module:Instance.module_inst ref Lazy.t -> + Instance.extern Instance.NameMap.t t + + val value_ref_vector_vector_encoding : + current_module:Instance.module_inst ref Lazy.t -> + Values.ref_ Instance.Vector.t ref Instance.Vector.t t + + val block_table_encoding : Ast.block_table t + + val module_instance_encoding : Instance.module_inst t +end diff --git a/src/lib_scoru_wasm/wasm_pvm.ml b/src/lib_scoru_wasm/wasm_pvm.ml index 1cc067a8b9c8da22739bdfe6de520f5ad6a171e2..a2fcc33ef149f3c14e8d369dd1228dd4fa4ce575 100644 --- a/src/lib_scoru_wasm/wasm_pvm.ml +++ b/src/lib_scoru_wasm/wasm_pvm.ml @@ -30,8 +30,6 @@ *) -open Tezos_webassembly_interpreter - module Make (T : Tree.S) : Gather_floppies.S with type tree = T.tree = struct include Gather_floppies.Make @@ -39,13 +37,14 @@ module Make (T : Tree.S) : Gather_floppies.S with type tree = T.tree = struct (struct type tree = T.tree - module Decodings = Wasm_decodings.Make (T) + module Wasm = Tezos_webassembly_interpreter module EncDec = Tree_encoding_decoding.Make - (Lazy_map.LwtInt32Map) - (Lazy_vector.LwtInt32Vector) - (Chunked_byte_vector.Lwt) + (Wasm.Instance.NameMap) + (Wasm.Instance.Vector) + (Wasm.Chunked_byte_vector.Lwt) (T) + module Wasm_encoding = Wasm_encoding.Make (EncDec) let compute_step = Lwt.return @@ -121,11 +120,5 @@ module Make (T : Tree.S) : Gather_floppies.S with type tree = T.tree = struct in let* tree = EncDec.encode status_encoding false tree in EncDec.encode (inp_encoding level id) message tree - - let _module_instance_of_tree modules = - Decodings.run (Decodings.module_instance_decoding modules) - - let _module_instances_of_tree = - Decodings.run Decodings.module_instances_decoding end) end