defmodule WaParserTest do use ExUnit.Case def parse_file(filename) do filename |> File.read!() |> WaParser.stream() |> Enum.to_list() end test "0_return_0" do assert parse_file("test_data/0_return_0.opt.wasm") == [ {:magic, <<0, 97, 115, 109>>}, {:version, <<1, 0, 0, 0>>}, {:section_id, 1}, {:section_type, :type}, {:section_length, 8}, {:section_body, [{[], []}, {[], [:i32]}]}, {:section_id, 3}, {:section_type, :function}, {:section_length, 3}, {:section_body, [0, 1]}, {:section_id, 6}, {:section_type, :global}, {:section_length, 56}, {:section_body, [ {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 131_072]}, {{:i32, :const}, ["i32.const": 0]}, {{:i32, :const}, ["i32.const": 1]} ]}, {:section_id, 7}, {:section_type, :export}, {:section_length, 163}, {:section_body, [ {"__wasm_call_ctors", {:func, 0}}, {"return_0", {:func, 1}}, {"__dso_handle", {:global, 0}}, {"__data_end", {:global, 1}}, {"__stack_low", {:global, 2}}, {"__stack_high", {:global, 3}}, {"__global_base", {:global, 4}}, {"__heap_base", {:global, 5}}, {"__heap_end", {:global, 6}}, {"__memory_base", {:global, 7}}, {"__table_base", {:global, 8}} ]}, {:section_id, 10}, {:section_type, :code}, {:section_length, 9}, {:section_body, [ %{code: %{expr: [], locals: []}, size: 2}, %{code: %{expr: ["i32.const": 0], locals: []}, size: 4} ]} ] end test "1_int_identity" do assert parse_file("test_data/1_int_identity.opt.wasm") |> Enum.filter(fn {k, _} -> k == :section_type or k == :section_body end) == [ {:section_type, :type}, {:section_body, [{[], []}, {[:i32], [:i32]}]}, {:section_type, :function}, {:section_body, [0, 1]}, {:section_type, :global}, {:section_body, [ {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 131_072]}, {{:i32, :const}, ["i32.const": 0]}, {{:i32, :const}, ["i32.const": 1]} ]}, {:section_type, :export}, {:section_body, [ {"__wasm_call_ctors", {:func, 0}}, {"identity", {:func, 1}}, {"__dso_handle", {:global, 0}}, {"__data_end", {:global, 1}}, {"__stack_low", {:global, 2}}, {"__stack_high", {:global, 3}}, {"__global_base", {:global, 4}}, {"__heap_base", {:global, 5}}, {"__heap_end", {:global, 6}}, {"__memory_base", {:global, 7}}, {"__table_base", {:global, 8}} ]}, {:section_type, :code}, {:section_body, [ %{code: %{expr: [], locals: []}, size: 2}, %{code: %{expr: ["local.get": 0], locals: []}, size: 4} ]} ] end test "2_int_arithm" do assert parse_file("test_data/2_int_arithm.opt.wasm") |> Enum.filter(fn {k, _} -> k == :section_type or k == :section_body end) == [ {:section_type, :type}, {:section_body, [{[:i32], [:i32]}, {[], []}]}, {:section_type, :function}, {:section_body, [1, 0, 0, 0, 0]}, {:section_type, :global}, { :section_body, [ {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 131_072]}, {{:i32, :const}, ["i32.const": 0]}, {{:i32, :const}, ["i32.const": 1]} ] }, {:section_type, :export}, { :section_body, [ {"__wasm_call_ctors", {:func, 0}}, {"inc", {:func, 1}}, {"dec", {:func, 2}}, {"tri", {:func, 3}}, {"third", {:func, 4}}, {"__dso_handle", {:global, 0}}, {"__data_end", {:global, 1}}, {"__stack_low", {:global, 2}}, {"__stack_high", {:global, 3}}, {"__global_base", {:global, 4}}, {"__heap_base", {:global, 5}}, {"__heap_end", {:global, 6}}, {"__memory_base", {:global, 7}}, {"__table_base", {:global, 8}} ] }, {:section_type, :code}, {:section_body, [ %{code: %{expr: [], locals: []}, size: 2}, %{ code: %{expr: [{:"local.get", 0}, {:"i32.const", 1}, :"i32.add"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"i32.const", 1}, :"i32.sub"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"i32.const", 3}, :"i32.mul"], locals: []}, size: 7 }, %{ code: %{ expr: [{:"local.get", 0}, {:"i32.const", 3}, :"i32.div_s"], locals: [] }, size: 7 } ]} ] end test "3_int_cond" do assert parse_file("test_data/3_int_cond.opt.wasm") |> Enum.filter(fn {k, _} -> k == :section_type or k == :section_body end) == [ {:section_type, :type}, {:section_body, [{[], []}, {[:i32, :i32], [:i32]}]}, {:section_type, :function}, {:section_body, [0, 1]}, {:section_type, :global}, { :section_body, [ {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 131_072]}, {{:i32, :const}, ["i32.const": 0]}, {{:i32, :const}, ["i32.const": 1]} ] }, {:section_type, :export}, { :section_body, [ {"__wasm_call_ctors", {:func, 0}}, {"ge_select", {:func, 1}}, {"__dso_handle", {:global, 0}}, {"__data_end", {:global, 1}}, {"__stack_low", {:global, 2}}, {"__stack_high", {:global, 3}}, {"__global_base", {:global, 4}}, {"__heap_base", {:global, 5}}, {"__heap_end", {:global, 6}}, {"__memory_base", {:global, 7}}, {"__table_base", {:global, 8}} ] }, {:section_type, :code}, {:section_body, [ %{code: %{expr: [], locals: []}, size: 2}, %{ code: %{ expr: [ {:"local.get", 0}, {:"local.get", 1}, {:"local.get", 0}, {:"local.get", 1}, :"i32.gt_s", :select ], locals: [] }, size: 12 } ]} ] end test "4_int_comp" do assert parse_file("test_data/4_int_comp.opt.wasm") |> Enum.filter(fn {k, _} -> k == :section_type or k == :section_body end) == [ {:section_type, :type}, {:section_body, [{[:i32, :i32], [:i32]}, {[], []}]}, {:section_type, :function}, {:section_body, [1, 0, 0, 0, 0, 0, 0]}, {:section_type, :global}, { :section_body, [ {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 131_072]}, {{:i32, :const}, ["i32.const": 0]}, {{:i32, :const}, ["i32.const": 1]} ] }, {:section_type, :export}, { :section_body, [ {"__wasm_call_ctors", {:func, 0}}, {"eq", {:func, 1}}, {"ne", {:func, 2}}, {"lt", {:func, 3}}, {"gt", {:func, 4}}, {"le", {:func, 5}}, {"ge", {:func, 6}}, {"__dso_handle", {:global, 0}}, {"__data_end", {:global, 1}}, {"__stack_low", {:global, 2}}, {"__stack_high", {:global, 3}}, {"__global_base", {:global, 4}}, {"__heap_base", {:global, 5}}, {"__heap_end", {:global, 6}}, {"__memory_base", {:global, 7}}, {"__table_base", {:global, 8}} ] }, {:section_type, :code}, {:section_body, [ %{code: %{expr: [], locals: []}, size: 2}, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.eq"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.ne"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.lt_s"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.gt_s"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.le_s"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.ge_s"], locals: []}, size: 7 } ]} ] end test "5_int_factorial" do assert parse_file("test_data/5_int_factorial.opt.wasm") |> Enum.filter(fn {k, _} -> k == :section_type or k == :section_body end) == [ {:section_type, :type}, {:section_body, [{[], []}, {[:i32], [:i32]}]}, {:section_type, :function}, {:section_body, [0, 1]}, {:section_type, :global}, { :section_body, [ {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 131_072]}, {{:i32, :const}, ["i32.const": 0]}, {{:i32, :const}, ["i32.const": 1]} ] }, {:section_type, :export}, { :section_body, [ {"__wasm_call_ctors", {:func, 0}}, {"factorial", {:func, 1}}, {"__dso_handle", {:global, 0}}, {"__data_end", {:global, 1}}, {"__stack_low", {:global, 2}}, {"__stack_high", {:global, 3}}, {"__global_base", {:global, 4}}, {"__heap_base", {:global, 5}}, {"__heap_end", {:global, 6}}, {"__memory_base", {:global, 7}}, {"__table_base", {:global, 8}} ] }, {:section_type, :code}, {:section_body, [ %{code: %{expr: [], locals: []}, size: 2}, %{ code: %{ expr: [ "i32.const": 1, "local.set": 1, loop: %{ instr: [ {:"local.get", 0}, {:"i32.const", 0}, :"i32.le_s", {:br_if, 0}, {:"local.get", 0}, {:"i32.const", 7}, :"i32.and", {:"local.set", 2}, {:loop, %{ instr: [ {:"local.get", 0}, {:"i32.const", 8}, :"i32.lt_u", {:if, %{instr: ["i32.const": 1, br: 1], blocktype: :empty}}, {:"i32.const", 0}, {:"local.get", 0}, {:"i32.const", 2_147_483_640}, :"i32.and", :"i32.sub", {:"local.set", 3}, {:"i32.const", 8}, {:"local.set", 0}, {:loop, %{ instr: [ {:"local.get", 0}, {:"local.get", 0}, {:"i32.const", 1}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 2}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 3}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 4}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 5}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 6}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 7}, :"i32.sub", {:"local.get", 1}, :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", {:"local.set", 1}, {:"local.get", 3}, {:"local.get", 0}, {:"i32.const", 8}, :"i32.add", {:"local.tee", 0}, :"i32.add", {:"i32.const", 8}, :"i32.ne", {:br_if, 0} ], blocktype: :empty }}, {:"local.get", 0}, {:"i32.const", 7}, :"i32.sub" ], blocktype: :i32 }}, {:"local.set", 0}, {:"local.get", 2}, :"i32.eqz", {:br_if, 0}, {:loop, %{ instr: [ {:"local.get", 0}, {:"local.get", 1}, :"i32.mul", {:"local.set", 1}, {:"local.get", 0}, {:"i32.const", 1}, :"i32.add", {:"local.set", 0}, {:"local.get", 2}, {:"i32.const", 1}, :"i32.sub", {:"local.tee", 2}, {:br_if, 0} ], blocktype: :empty }} ], blocktype: :empty }, "local.get": 1 ], locals: [%{type: :i32, num: 3}] }, size: 165 } ]} ] end test "9_all" do assert parse_file("test_data/9_all.opt.wasm") |> Enum.filter(fn {k, _} -> k == :section_type or k == :section_body end) == [ {:section_type, :type}, {:section_body, [{[:i32, :i32], [:i32]}, {[:i32], [:i32]}, {[], []}, {[], [:i32]}]}, {:section_type, :function}, {:section_body, [2, 3, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1]}, {:section_type, :memory}, {:section_body, [{2, nil}]}, {:section_type, :global}, {:section_body, [ {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 1024]}, {{:i32, :const}, ["i32.const": 66560]}, {{:i32, :const}, ["i32.const": 131_072]}, {{:i32, :const}, ["i32.const": 0]}, {{:i32, :const}, ["i32.const": 1]} ]}, {:section_type, :export}, {:section_body, [ {"memory", {:mem, 0}}, {"__wasm_call_ctors", {:func, 0}}, {"return_0", {:func, 1}}, {"identity", {:func, 2}}, {"inc", {:func, 3}}, {"dec", {:func, 4}}, {"tri", {:func, 5}}, {"third", {:func, 6}}, {"ge_select", {:func, 7}}, {"eq", {:func, 8}}, {"ne", {:func, 9}}, {"lt", {:func, 10}}, {"gt", {:func, 11}}, {"le", {:func, 12}}, {"ge", {:func, 13}}, {"factorial", {:func, 14}}, {"__dso_handle", {:global, 0}}, {"__data_end", {:global, 1}}, {"__stack_low", {:global, 2}}, {"__stack_high", {:global, 3}}, {"__global_base", {:global, 4}}, {"__heap_base", {:global, 5}}, {"__heap_end", {:global, 6}}, {"__memory_base", {:global, 7}}, {"__table_base", {:global, 8}} ]}, {:section_type, :code}, {:section_body, [ %{code: %{expr: [], locals: []}, size: 2}, %{code: %{expr: ["i32.const": 0], locals: []}, size: 4}, %{code: %{expr: ["local.get": 0], locals: []}, size: 4}, %{ code: %{ expr: [ {:"local.get", 0}, {:"i32.const", 1}, :"i32.add" ], locals: [] }, size: 7 }, %{ code: %{ expr: [ {:"local.get", 0}, {:"i32.const", 1}, :"i32.sub" ], locals: [] }, size: 7 }, %{ code: %{ expr: [ {:"local.get", 0}, {:"i32.const", 3}, :"i32.mul" ], locals: [] }, size: 7 }, %{ code: %{ expr: [ {:"local.get", 0}, {:"i32.const", 3}, :"i32.div_s" ], locals: [] }, size: 7 }, %{ code: %{ expr: [ {:"local.get", 0}, {:"local.get", 1}, {:"local.get", 0}, {:"local.get", 1}, :"i32.gt_s", :select ], locals: [] }, size: 12 }, %{ code: %{ expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.eq"], locals: [] }, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.ne"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.lt_s"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.gt_s"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.le_s"], locals: []}, size: 7 }, %{ code: %{expr: [{:"local.get", 0}, {:"local.get", 1}, :"i32.ge_s"], locals: []}, size: 7 }, %{ code: %{ expr: [ "i32.const": 1, "local.set": 1, loop: %{ instr: [ {:"local.get", 0}, {:"i32.const", 0}, :"i32.le_s", {:br_if, 0}, {:"local.get", 0}, {:"i32.const", 7}, :"i32.and", {:"local.set", 2}, {:loop, %{ instr: [ {:"local.get", 0}, {:"i32.const", 8}, :"i32.lt_u", {:if, %{instr: ["i32.const": 1, br: 1], blocktype: :empty}}, {:"i32.const", 0}, {:"local.get", 0}, {:"i32.const", 2_147_483_640}, :"i32.and", :"i32.sub", {:"local.set", 3}, {:"i32.const", 8}, {:"local.set", 0}, {:loop, %{ instr: [ {:"local.get", 0}, {:"local.get", 0}, {:"i32.const", 1}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 2}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 3}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 4}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 5}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 6}, :"i32.sub", {:"local.get", 0}, {:"i32.const", 7}, :"i32.sub", {:"local.get", 1}, :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", :"i32.mul", {:"local.set", 1}, {:"local.get", 3}, {:"local.get", 0}, {:"i32.const", 8}, :"i32.add", {:"local.tee", 0}, :"i32.add", {:"i32.const", 8}, :"i32.ne", {:br_if, 0} ], blocktype: :empty }}, {:"local.get", 0}, {:"i32.const", 7}, :"i32.sub" ], blocktype: :i32 }}, {:"local.set", 0}, {:"local.get", 2}, :"i32.eqz", {:br_if, 0}, {:loop, %{ instr: [ {:"local.get", 0}, {:"local.get", 1}, :"i32.mul", {:"local.set", 1}, {:"local.get", 0}, {:"i32.const", 1}, :"i32.add", {:"local.set", 0}, {:"local.get", 2}, {:"i32.const", 1}, :"i32.sub", {:"local.tee", 2}, {:br_if, 0} ], blocktype: :empty }} ], blocktype: :empty }, "local.get": 1 ], locals: [%{num: 3, type: :i32}] }, size: 165 } ]} ] end end