diff --git a/lib/orb.ex b/lib/orb.ex index 0a7dcb2..f8f55ce 100644 --- a/lib/orb.ex +++ b/lib/orb.ex @@ -1012,6 +1012,7 @@ defmodule Orb do end def to_wat(term) when is_struct(term) do + # TODO: should we leave this as iodata? Or allow an option to be passed in? Orb.ToWat.to_wat(term, "") |> IO.chardata_to_string() end @@ -1032,6 +1033,7 @@ defmodule Orb do # <<0x0A, 0x06, 0x01, 0x04, 0x00, 0x41, 0x2A, 0x0B>> context = Orb.ToWasm.Context.new() + # TODO: should we leave this as iodata? Or allow an option to be passed in? Orb.ToWasm.to_wasm(term, context) |> IO.iodata_to_binary() end diff --git a/lib/orb/dsl/dsl.ex b/lib/orb/dsl/dsl.ex index 7985021..5ee6f3e 100644 --- a/lib/orb/dsl/dsl.ex +++ b/lib/orb/dsl/dsl.ex @@ -467,7 +467,8 @@ defmodule Orb.DSL do Declare a loop. """ defmacro loop(identifier, options \\ [], do: block) do - identifier = __expand_identifier(identifier, __CALLER__) + env = __CALLER__ + identifier = __expand_identifier(identifier, env) result_type = Keyword.get(options, :result, nil) |> Macro.expand_literals(__CALLER__) @@ -481,11 +482,11 @@ defmodule Orb.DSL do block_items = Macro.prewalk(block_items, fn {{:., _, [{:__aliases__, _, [identifier]}, :continue]}, _, []} -> - quote do: %Orb.Loop.Branch{identifier: unquote(identifier)} + quote do: %Orb.Loop.Branch{identifier: unquote(__expand_identifier(identifier, env))} {{:., _, [{:__aliases__, _, [identifier]}, :continue]}, _, [[if: condition]]} -> quote do: %Orb.Loop.Branch{ - identifier: unquote(identifier), + identifier: unquote(__expand_identifier(identifier, env)), if: unquote(condition) } diff --git a/lib/orb/func.ex b/lib/orb/func.ex index 9af2475..42d361e 100644 --- a/lib/orb/func.ex +++ b/lib/orb/func.ex @@ -103,16 +103,8 @@ defmodule Orb.Func do defstruct [:name, :type] - def to_wasm_type(:i32), do: 0x7F - def to_wasm_type(:i64), do: 0x7E - def to_wasm_type(:f32), do: 0x7D - def to_wasm_type(:f64), do: 0x7C - def to_wasm_type(:v128), do: 0x7B - - def to_wasm_type(custom_type) when is_atom(custom_type), - do: Orb.Ops.to_primitive_type(custom_type) |> to_wasm_type() - - def to_wasm_type(%Param{type: type}), do: to_wasm_type(type) + def to_wasm_type(type) when is_atom(type), do: Orb.ToWasm.Helpers.to_wasm_type(type) + def to_wasm_type(%Param{type: type}), do: Orb.ToWasm.Helpers.to_wasm_type(type) defimpl Orb.ToWat do import Orb.ToWat.Helpers @@ -126,7 +118,6 @@ defmodule Orb.Func do ] end - @spec to_wat(%Orb.Func.Param{}, any()) :: [...] def to_wat(%Param{name: name, type: type}, indent) do [ indent, diff --git a/lib/orb/if_else.ex b/lib/orb/if_else.ex index 008a924..215745d 100644 --- a/lib/orb/if_else.ex +++ b/lib/orb/if_else.ex @@ -126,6 +126,28 @@ defmodule Orb.IfElse do end end + defimpl Orb.ToWasm do + import Orb.ToWasm.Helpers + + def to_wasm( + %Orb.IfElse{ + push_type: result, + condition: condition, + when_true: when_true, + when_false: when_false + }, + context + ) do + [ + Orb.ToWasm.to_wasm(condition, context), + [0x04, if(result, do: to_wasm_type(result), else: 0x40)], + Orb.ToWasm.to_wasm(when_true, context), + if(when_false, do: [0x05, Orb.ToWasm.to_wasm(when_false, context)], else: []), + 0x0B + ] + end + end + defimpl Orb.TypeNarrowable do def type_narrow_to(%Orb.IfElse{push_type: current_type} = if_else, narrower_type) do case Ops.types_compatible?(current_type, narrower_type) do diff --git a/lib/orb/instruction.ex b/lib/orb/instruction.ex index 10bd0d8..39741e6 100644 --- a/lib/orb/instruction.ex +++ b/lib/orb/instruction.ex @@ -33,10 +33,6 @@ defmodule Orb.Instruction do } end - @spec wrap_constant!(any(), number() | %{:push_type => any(), optional(any()) => any()}) :: %{ - :push_type => any(), - optional(any()) => any() - } def wrap_constant!(type, value) def wrap_constant!(Elixir.Integer, value) when is_number(value), @@ -45,6 +41,7 @@ defmodule Orb.Instruction do def wrap_constant!(Elixir.Float, value) when is_number(value), do: raise("Need concrete type not Elixir.Float for constants.") + # TODO: replace with Orb.Instruction.Const def wrap_constant!(type, value) when is_number(value), do: %__MODULE__{ push_type: type, @@ -118,7 +115,9 @@ defmodule Orb.Instruction do end def local_set(type, local_name, value) do - new(nil, {:local_set, local_name, type}, [value]) + new(nil, {:local_set, local_name, type}, [ + Orb.Instruction.Const.wrap(type, value, "local.set $#{local_name}") + ]) end def global_set(type, global_name, value) do @@ -159,7 +158,7 @@ defmodule Orb.Instruction do received_type = cond do is_integer(param) -> Elixir.Integer - # TODO: Change to Elixir.Float + # FIXME: Change to Elixir.Float is_float(param) -> :f32 %{push_type: type} = param -> type end @@ -379,7 +378,7 @@ defmodule Orb.Instruction do indent ) do [ - for(operand <- operands, do: [indent, Instructions.do_wat(operand), "\n"]), + for(operand <- operands, do: [Instructions.do_wat(operand, indent), "\n"]), indent, "(local.set $", to_string(local_name), @@ -484,11 +483,12 @@ defmodule Orb.Instruction do defimpl Orb.ToWasm do import Orb.Leb - def type_const(:i32), do: 0x41 - def type_const(:i64), do: 0x42 - def type_const(:f32), do: 0x43 - def type_const(:f64), do: 0x44 + defp type_const(:i32), do: 0x41 + defp type_const(:i64), do: 0x42 + defp type_const(:f32), do: 0x43 + defp type_const(:f64), do: 0x44 + # TODO: remove, replace with Orb.Instruction.Const def to_wasm( %Orb.Instruction{ push_type: type, @@ -559,21 +559,21 @@ defmodule Orb.Instruction do defp i32_operation(:rotl), do: 0x77 defp i32_operation(:rotr), do: 0x78 - def i64_operation(:add), do: 0x7C - def i64_operation(:sub), do: 0x7D - def i64_operation(:mul), do: 0x7E - def i64_operation(:div_s), do: 0x7F - def i64_operation(:div_u), do: 0x80 - def i64_operation(:rem_s), do: 0x81 - def i64_operation(:rem_u), do: 0x82 - def i64_operation(:and), do: 0x83 - def i64_operation(:or), do: 0x84 - def i64_operation(:xor), do: 0x85 - def i64_operation(:shl), do: 0x86 - def i64_operation(:shr_s), do: 0x87 - def i64_operation(:shr_u), do: 0x88 - def i64_operation(:rotl), do: 0x89 - def i64_operation(:rotr), do: 0x8A + defp i64_operation(:add), do: 0x7C + defp i64_operation(:sub), do: 0x7D + defp i64_operation(:mul), do: 0x7E + defp i64_operation(:div_s), do: 0x7F + defp i64_operation(:div_u), do: 0x80 + defp i64_operation(:rem_s), do: 0x81 + defp i64_operation(:rem_u), do: 0x82 + defp i64_operation(:and), do: 0x83 + defp i64_operation(:or), do: 0x84 + defp i64_operation(:xor), do: 0x85 + defp i64_operation(:shl), do: 0x86 + defp i64_operation(:shr_s), do: 0x87 + defp i64_operation(:shr_u), do: 0x88 + defp i64_operation(:rotl), do: 0x89 + defp i64_operation(:rotr), do: 0x8A # def to_wasm( # %Orb.Instruction{ diff --git a/lib/orb/instruction/const.ex b/lib/orb/instruction/const.ex index 71a7ccb..fa55fb4 100644 --- a/lib/orb/instruction/const.ex +++ b/lib/orb/instruction/const.ex @@ -2,18 +2,95 @@ defmodule Orb.Instruction.Const do @moduledoc false defstruct [:push_type, :value] - def new(push_type, value) when push_type in ~w(i32 i64 f32 f64)a do + require alias Orb.Ops + + def new(push_type, value) do %__MODULE__{push_type: push_type, value: value} end + def wrap(push_type, value) do + wrap(push_type, value, "#{Ops.to_primitive_type(push_type)}.const") + end + + def wrap(push_type, value, instruction_identifier) when is_number(value) do + received_type = + cond do + is_integer(value) -> Elixir.Integer + is_float(value) -> Elixir.Float + end + + if Ops.types_compatible?(received_type, push_type) do + new(push_type, value) + else + raise Orb.TypeCheckError, + expected_type: push_type, + received_type: received_type, + instruction_identifier: instruction_identifier + end + end + + def wrap(push_type, %{push_type: push_type} = instruction, _) + when Ops.is_primitive_type(push_type) do + instruction + end + + def wrap(push_type, %{push_type: other_type} = instruction, instruction_identifier) do + case Orb.Ops.types_compatible?(push_type, other_type) do + true -> + instruction + + false -> + raise Orb.TypeCheckError, + expected_type: push_type, + received_type: other_type, + instruction_identifier: instruction_identifier + end + end + defimpl Orb.ToWat do + alias Orb.ToWat.Helpers + def to_wat(%Orb.Instruction.Const{push_type: push_type, value: value}, indent) do [ indent, - ["(", Atom.to_string(push_type), ".const "], + ["(", Helpers.do_type(push_type), ".const "], to_string(value), ")" ] end end + + defimpl Orb.ToWasm do + import Orb.Leb + + defp type_const(:i32), do: 0x41 + defp type_const(:i64), do: 0x42 + defp type_const(:f32), do: 0x43 + defp type_const(:f64), do: 0x44 + + defp type_const(custom_type) when is_atom(custom_type), + do: Orb.CustomType.resolve!(custom_type) |> type_const() + + def to_wasm( + %Orb.Instruction.Const{ + push_type: type, + value: number + }, + _ + ) do + [ + type_const(type), + cond do + is_integer(number) -> + uleb128(number) + + is_float(number) and type === :f32 -> + <> + + is_float(number) and type === :f64 -> + <> + end + ] + end + end end diff --git a/lib/orb/instruction/relative.ex b/lib/orb/instruction/relative.ex index 4a2f17d..f50d297 100644 --- a/lib/orb/instruction/relative.ex +++ b/lib/orb/instruction/relative.ex @@ -25,13 +25,11 @@ defmodule Orb.Instruction.Relative do } end - alias __MODULE__, as: Self - defimpl Orb.ToWat do alias Orb.ToWat.Instructions def to_wat( - %Self{ + %Orb.Instruction.Relative{ input_type: input_type, operation: operation, lhs: lhs, @@ -53,4 +51,60 @@ defmodule Orb.Instruction.Relative do ] end end + + defimpl Orb.ToWasm do + def to_wasm( + %Orb.Instruction.Relative{ + input_type: input_type, + operation: operation, + lhs: lhs, + rhs: rhs + }, + options + ) do + [ + Orb.Instruction.Const.wrap(input_type, lhs) |> Orb.ToWasm.to_wasm(options), + Orb.Instruction.Const.wrap(input_type, rhs) |> Orb.ToWasm.to_wasm(options), + do_operation(input_type, operation) + ] + end + + defp do_operation(:i32, :eqz), do: 0x45 + defp do_operation(:i32, :eq), do: 0x46 + defp do_operation(:i32, :ne), do: 0x47 + defp do_operation(:i32, :lt_s), do: 0x48 + defp do_operation(:i32, :lt_u), do: 0x49 + defp do_operation(:i32, :gt_s), do: 0x4A + defp do_operation(:i32, :gt_u), do: 0x4B + defp do_operation(:i32, :le_s), do: 0x4C + defp do_operation(:i32, :le_u), do: 0x4D + defp do_operation(:i32, :ge_s), do: 0x4E + defp do_operation(:i32, :ge_u), do: 0x4F + + defp do_operation(:i64, :eqz), do: 0x50 + defp do_operation(:i64, :eq), do: 0x51 + defp do_operation(:i64, :ne), do: 0x52 + defp do_operation(:i64, :lt_s), do: 0x53 + defp do_operation(:i64, :lt_u), do: 0x54 + defp do_operation(:i64, :gt_s), do: 0x55 + defp do_operation(:i64, :gt_u), do: 0x56 + defp do_operation(:i64, :le_s), do: 0x57 + defp do_operation(:i64, :le_u), do: 0x58 + defp do_operation(:i64, :ge_s), do: 0x59 + defp do_operation(:i64, :ge_u), do: 0x5A + + defp do_operation(:f32, :eq), do: 0x5B + defp do_operation(:f32, :ne), do: 0x5C + defp do_operation(:f32, :lt), do: 0x5D + defp do_operation(:f32, :gt), do: 0x5E + defp do_operation(:f32, :le), do: 0x5F + defp do_operation(:f32, :ge), do: 0x60 + + defp do_operation(:f64, :eq), do: 0x61 + defp do_operation(:f64, :ne), do: 0x62 + defp do_operation(:f64, :lt), do: 0x63 + defp do_operation(:f64, :gt), do: 0x64 + defp do_operation(:f64, :le), do: 0x65 + defp do_operation(:f64, :ge), do: 0x66 + end end diff --git a/lib/orb/instruction_sequence.ex b/lib/orb/instruction_sequence.ex index c8f5586..2a46a9f 100644 --- a/lib/orb/instruction_sequence.ex +++ b/lib/orb/instruction_sequence.ex @@ -220,13 +220,7 @@ defmodule Orb.InstructionSequence do options ) do for instruction <- instructions do - case instruction do - %Orb.InstructionSequence{} -> - to_wasm(instruction, options) - - _ -> - Orb.ToWasm.to_wasm(instruction, options) - end + Orb.ToWasm.to_wasm(instruction, options) end end end diff --git a/lib/orb/loop.ex b/lib/orb/loop.ex index 376fbdf..6d4ee21 100644 --- a/lib/orb/loop.ex +++ b/lib/orb/loop.ex @@ -24,6 +24,27 @@ defmodule Orb.Loop do end end + defimpl Orb.ToWasm do + import Orb.ToWasm.Helpers + + def to_wasm( + %Orb.Loop{ + identifier: identifier, + push_type: result, + body: body + }, + context + ) do + context = Orb.ToWasm.Context.register_loop_identifier(context, identifier) + + [ + [0x03, if(result, do: to_wasm_type(result), else: [0x40])], + Orb.ToWasm.to_wasm(body, context), + 0x0B + ] + end + end + defmodule Branch do defstruct [:identifier, :if] @@ -54,5 +75,31 @@ defmodule Orb.Loop do ] end end + + defimpl Orb.ToWasm do + alias Orb.Leb + + def to_wasm( + %Orb.Loop.Branch{identifier: identifier, if: nil}, + context + ) do + index = Orb.ToWasm.Context.fetch_loop_identifier_index!(context, identifier) + + [0x0C, Leb.uleb128(index)] + end + + def to_wasm( + %Orb.Loop.Branch{identifier: identifier, if: condition}, + context + ) do + index = Orb.ToWasm.Context.fetch_loop_identifier_index!(context, identifier) + + [ + Orb.ToWasm.to_wasm(condition, context), + 0x0D, + Leb.uleb128(index) + ] + end + end end end diff --git a/lib/orb/module_definition.ex b/lib/orb/module_definition.ex index 4a253c5..8e7690b 100644 --- a/lib/orb/module_definition.ex +++ b/lib/orb/module_definition.ex @@ -197,26 +197,34 @@ defmodule Orb.ModuleDefinition do uniq_func_types = for({f, _index} <- funcs, do: func_type_tuple(f)) - |> Enum.uniq() func_defs = - for {f, _index} <- funcs do - type_to_find = func_type_tuple(f) - - Enum.find_index(uniq_func_types, fn type -> type === type_to_find end) - |> uleb128() + for {_f, index} <- funcs do + index |> uleb128() end + # uniq_func_types = + # for({f, _index} <- funcs, do: func_type_tuple(f)) + # |> Enum.uniq() + + # func_defs = + # for {f, _index} <- funcs do + # type_to_find = func_type_tuple(f) + + # Enum.find_index(uniq_func_types, fn type -> type === type_to_find end) + # |> uleb128() + # end + export_funcs = for {%Orb.Func{exported_names: names}, index} <- funcs, name <- names do - export_func(name, index) + encode_export_func(name, index) end func_code = for {f, _index} <- funcs, do: Orb.ToWasm.to_wasm(f, context) [ @wasm_prefix, - section(:type, vec(for {p, r} <- uniq_func_types, do: func_type(p, r))), + section(:type, vec(for {p, r} <- uniq_func_types, do: encode_func_type(p, r))), section(:function, vec(func_defs)), section(:export, vec(export_funcs)), section(:code, vec(func_code)) @@ -235,7 +243,7 @@ defmodule Orb.ModuleDefinition do {params, result} end - defp func_type(params, results) do + defp encode_func_type(params, results) do alias Orb.Func.Param [ @@ -249,14 +257,14 @@ defmodule Orb.ModuleDefinition do for type <- Tuple.to_list(types), do: Param.to_wasm_type(type) type when is_atom(type) -> - [Param.to_wasm_type(type)] + [to_wasm_type(type)] end |> vec() end ] end - defp export_func(name, func_index) do + defp encode_export_func(name, func_index) do [ sized(name), 0x00, diff --git a/lib/orb/ops.ex b/lib/orb/ops.ex index fa1b5f7..6c1a989 100644 --- a/lib/orb/ops.ex +++ b/lib/orb/ops.ex @@ -98,7 +98,6 @@ defmodule Orb.Ops do def typeof(value, :primitive), do: typeof(value) |> to_primitive_type() - @spec pop_push_of(binary() | number() | map()) :: {any(), any()} def pop_push_of(n) when is_integer(n), do: {nil, Elixir.Integer} def pop_push_of(n) when is_float(n), do: {nil, Elixir.Float} def pop_push_of(%{pop_type: pop_type, push_type: push_type}), do: {pop_type, push_type} diff --git a/lib/orb/to_wasm.ex b/lib/orb/to_wasm.ex index b8dd420..4fb5f54 100644 --- a/lib/orb/to_wasm.ex +++ b/lib/orb/to_wasm.ex @@ -19,10 +19,20 @@ defmodule Orb.ToWasm.Helpers do items ] end + + def to_wasm_type(nil), do: raise("nil is not a valid type") + def to_wasm_type(:i32), do: 0x7F + def to_wasm_type(:i64), do: 0x7E + def to_wasm_type(:f32), do: 0x7D + def to_wasm_type(:f64), do: 0x7C + def to_wasm_type(:v128), do: 0x7B + + def to_wasm_type(custom_type) when is_atom(custom_type), + do: Orb.Ops.to_primitive_type(custom_type) |> to_wasm_type() end defmodule Orb.ToWasm.Context do - defstruct local_indexes: %{} + defstruct local_indexes: %{}, loop_indexes: %{} def new(), do: %__MODULE__{} @@ -43,4 +53,16 @@ defmodule Orb.ToWasm.Context do entry = Map.fetch!(context.local_indexes, local_identifier) entry.index end + + def register_loop_identifier(context = %__MODULE__{loop_indexes: loop_indexes}, loop_identifier) + when not is_map_key(loop_indexes, loop_identifier) do + next_index = map_size(loop_indexes) + loop_indexes = Map.put(loop_indexes, loop_identifier, next_index) + %__MODULE__{context | loop_indexes: loop_indexes} + end + + def fetch_loop_identifier_index!(%__MODULE__{loop_indexes: loop_indexes}, loop_identifier) + when is_map_key(loop_indexes, loop_identifier) do + Map.fetch!(loop_indexes, loop_identifier) + end end diff --git a/test/wasm_output_test.exs b/test/wasm_output_test.exs index 5b4db0c..6eef078 100644 --- a/test/wasm_output_test.exs +++ b/test/wasm_output_test.exs @@ -2,6 +2,8 @@ defmodule WasmOutputTest do alias OrbWasmtime.Wasm use ExUnit.Case, async: true + @moduletag timeout: 1_000 + test "basic module" do defmodule Basic do use Orb @@ -19,6 +21,31 @@ defmodule WasmOutputTest do Orb.to_wasm(Basic) end + test "conditionals" do + defmodule Conditionals do + use Orb + + defw is_odd?(n: I32), I32 do + if I32.rem_u(n, 2) === 1 do + 1 + else + 0 + end + end + end + + wasm = Orb.to_wasm(Conditionals) + + assert wasm |> Wasm.call(:is_odd?, 0) === 0 + assert wasm |> Wasm.call(:is_odd?, 1) === 1 + assert wasm |> Wasm.call(:is_odd?, 2) === 0 + assert wasm |> Wasm.call(:is_odd?, 3) === 1 + assert wasm |> Wasm.call(:is_odd?, 4) === 0 + assert wasm |> Wasm.call(:is_odd?, 5) === 1 + end + + # test "encoding negative i32" + test "i32 operations and func params" do defmodule MathI32 do use Orb @@ -27,9 +54,45 @@ defmodule WasmOutputTest do denominator = 4 + a - b a * b / denominator end + + defw fibonacci(n: I32), I32, a: I32, b: I32 do + if n <= 1 do + n + else + a = 0 + b = 1 + + loop Each do + b = + Orb.Stack.push a + b do + a = b + end + + n = n - 1 + + if n > 1 do + Each.continue() + end + end + + b + end + end end - assert ~S""" + wat = Orb.to_wat(MathI32) + wasm = Orb.to_wasm(MathI32) + + assert wat |> Wasm.call(:fibonacci, 0) === 0 + assert wat |> Wasm.call(:fibonacci, 1) === 1 + assert wat |> Wasm.call(:fibonacci, 2) === 1 + assert wat |> Wasm.call(:fibonacci, 3) === 2 + assert wat |> Wasm.call(:fibonacci, 4) === 3 + assert wat |> Wasm.call(:fibonacci, 5) === 5 + assert wat |> Wasm.call(:fibonacci, 6) === 8 + assert wat |> Wasm.call(:fibonacci, 7) === 13 + + assert wat === ~S""" (module $MathI32 (func $math (export "math") (param $a i32) (param $b i32) (result i32) (local $denominator i32) @@ -37,23 +100,65 @@ defmodule WasmOutputTest do (local.set $denominator) (i32.div_s (i32.mul (local.get $a) (local.get $b)) (local.get $denominator)) ) + (func $fibonacci (export "fibonacci") (param $n i32) (result i32) + (local $a i32) + (local $b i32) + (i32.le_s (local.get $n) (i32.const 1)) + (if (result i32) + (then + (local.get $n) + ) + (else + (i32.const 0) + (local.set $a) + (i32.const 1) + (local.set $b) + (loop $Each + (i32.add (local.get $a) (local.get $b)) + (local.get $b) + (local.set $a) + + (local.set $b) + (i32.sub (local.get $n) (i32.const 1)) + (local.set $n) + (i32.gt_s (local.get $n) (i32.const 1)) + (if + (then + (br $Each) + ) + ) + ) + (local.get $b) + ) + ) + ) ) - """ === Orb.to_wat(MathI32) + """ - assert <<"\0asm", 0x01000000::32>> <> - <<0x01, 0x07, 0x01>> <> - <<0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F>> <> - <<0x03, 0x02, 0x01, 0x00>> <> - <<0x07, 0x08, 0x01, 0x04, "math", 0x00, 0x00>> <> - <<0x0A, 0x18, 0x01, 0x16, <<0x01, 0x01, 0x7F>>, - <<"", <<0x41, 0x04>>, <<0x20, 0x00>>, 0x6A, <<0x20, 0x01>>, 0x6B>>, <<0x21, 0x02>>, - <<"", <<0x20, 0x00>>, <<0x20, 0x01>>, 0x6C, <<0x20, 0x02>>, 0x6D>>, - 0x0B>> = - Orb.to_wasm(MathI32) + if false do + path_wat = Path.join(__DIR__, "math.wat") + path_wasm = Path.join(__DIR__, "math.wasm") + File.write!(path_wat, wat) + File.write!(path_wasm, wasm) + end + + # assert <<"\0asm", 0x01000000::32>> <> + # <<0x01, 0x0C, 0x02, <<0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F>>, + # (<<0x60, 0x01, 0x7F, 0x01, 0x7F>>)>> <> + # <<0x03, 0x03, 0x02, 0x00, 0x01>> <> + # <<0x07, 0x14, 0x02, <<0x04, "math", 0x00, 0x00>>, + # (<<0x09, "fibonacci", 0x00, 0x01>>)>> <> + # <<0x0A, 0x18, 0x01, 0x16, <<0x01, 0x01, 0x7F>>, + # <<"", <<0x41, 0x04>>, <<0x20, 0x00>>, 0x6A, <<0x20, 0x01>>, 0x6B>>, <<0x21, 0x02>>, + # <<"", <<0x20, 0x00>>, <<0x20, 0x01>>, 0x6C, <<0x20, 0x02>>, 0x6D>>, + # 0x0B>> = + # wasm + + # extract_wasm_sections(wasm) |> dbg(base: :hex, limit: 1000) assert (11 * 7) |> div(4 + 11 - 7) === 9 - assert Orb.to_wat(MathI32) |> Wasm.call(:math, 11, 7) === 9 - assert Orb.to_wasm(MathI32) |> Wasm.call(:math, 11, 7) === 9 + assert wat |> Wasm.call(:math, 11, 7) === 9 + assert wasm |> Wasm.call(:math, 11, 7) === 9 end test "i64 operations and func params" do @@ -91,4 +196,39 @@ defmodule WasmOutputTest do assert Orb.to_wat(MathI64) |> Wasm.call(:math, {:i64, 11}, {:i64, 7}) === 9 assert Orb.to_wasm(MathI64) |> Wasm.call(:math, {:i64, 11}, {:i64, 7}) === 9 end + + defp extract_wasm_sections(<<"\0asm", 0x01000000::32>> <> bytes) do + extract_wasm_section(bytes, []) + end + + defp extract_wasm_section(<<>>, list) do + list |> :lists.reverse() + end + + defp extract_wasm_section(<>, list) do + extract_wasm_section(rest, [do_wasm_type(type, section) | list]) + end + + defp do_wasm_type(0x01, <>) do + section + |> :binary.split("\x60", [:global]) + |> Enum.reject(&(&1 == "")) + |> then(fn items when length(items) === count -> + {:type, items} + end) + end + + defp do_wasm_type(0x03, section), do: {:function, section} + defp do_wasm_type(0x07, section), do: {:export, section} + # defp do_wasm_type(0x0A, <>), do: {:code, {count, section}} + defp do_wasm_type(0x0A, <>) do + for <> do + {func_code} + end + |> then(fn items when length(items) === count -> + {:code, items} + end) + end + + defp do_wasm_type(type, section), do: {type, section} end