-module(gwr@execution@machine). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([allocate_web_assembly_function/4, initialize/1, address_to_int/1, address_to_string/1, i32_add/1, i32_const/2, local_get/2, execute/1, call/3]). -export_type([configuration/0, thread/0, machine/0, machine_state/0]). -type configuration() :: {configuration, gwr@execution@runtime:store(), thread()}. -type thread() :: {thread, gwr@execution@stack:frame_state(), list(gwr@syntax@instruction:instruction())}. -type machine() :: {machine, gwr@execution@runtime:module_instance(), machine_state()}. -type machine_state() :: {machine_state, configuration(), gwr@execution@stack:stack()}. -spec allocate_web_assembly_function( gwr@syntax@module:function_(), gwr@execution@runtime:store(), list(gwr@execution@runtime:address()), list(gwr@syntax@types:function_type()) ) -> {ok, {gwr@execution@runtime:store(), list(gwr@execution@runtime:address())}} | {error, binary()}. allocate_web_assembly_function(Function, Store, Addresses, Types_list) -> Empty_module_instance = {module_instance, [], [], [], [], [], [], [], []}, Function_address = {function_address, erlang:length(Addresses)}, case begin _pipe = Types_list, _pipe@1 = gleam@list:take(_pipe, erlang:element(2, Function) + 1), gleam@list:last(_pipe@1) end of {ok, Function_type} -> {ok, {erlang:setelement( 2, Store, lists:append( erlang:element(2, Store), [{web_assembly_function_instance, Function_type, Empty_module_instance, Function}] ) ), lists:append(Addresses, [Function_address])}}; {error, _} -> {error, <<"gwr/execution/machine.allocate_web_assembly_function: couldn't find the type of the function among module instance's types list"/utf8>>} end. -spec initialize(gwr@syntax@module:module_()) -> {ok, machine()} | {error, binary()}. initialize(Module) -> Module_instance = {module_instance, erlang:element(2, Module), [], [], [], [], [], [], []}, Config = {configuration, {store, [], [], [], [], [], []}, {thread, {frame_state, [], Module_instance}, []}}, Stack = gwr@execution@stack:create(), gleam@result:'try'( gleam@list:fold( erlang:element(3, Module), {ok, {erlang:element(2, Config), []}}, fun(State, Function) -> gleam@result:'try'( State, fun(_use0) -> {Store, Allocations} = _use0, allocate_web_assembly_function( Function, Store, Allocations, erlang:element(2, Module) ) end ) end ), fun(_use0@1) -> {Store@1, Allocations@1} = _use0@1, Module_instance@1 = erlang:setelement( 3, Module_instance, Allocations@1 ), Results = gleam@list:filter_map( erlang:element(2, Store@1), fun(Function@1) -> case Function@1 of {web_assembly_function_instance, Type_, _, Code} -> {ok, {web_assembly_function_instance, Type_, Module_instance@1, Code}}; _ -> {error, nil} end end ), Store@2 = erlang:setelement(2, Store@1, Results), Thread = erlang:setelement( 2, erlang:element(3, Config), erlang:setelement( 3, erlang:element(2, erlang:element(3, Config)), Module_instance@1 ) ), {ok, {machine, Module_instance@1, {machine_state, {configuration, Store@2, Thread}, Stack}}} end ). -spec address_to_int(gwr@execution@runtime:address()) -> integer(). address_to_int(Address) -> case Address of {function_address, Addr} -> Addr; {table_address, Addr@1} -> Addr@1; {memory_address, Addr@2} -> Addr@2; {global_address, Addr@3} -> Addr@3; {element_address, Addr@4} -> Addr@4; {data_address, Addr@5} -> Addr@5; {extern_address, Addr@6} -> Addr@6 end. -spec address_to_string(gwr@execution@runtime:address()) -> binary(). address_to_string(Address) -> gleam@int:to_string(address_to_int(Address)). -spec i32_add(machine_state()) -> {ok, machine_state()} | {error, binary()}. i32_add(State) -> {Stack, Values} = gwr@execution@stack:pop_repeat( erlang:element(3, State), 2 ), gleam@result:'try'(case gleam@option:values(Values) of [{value_entry, {number, A}}, {value_entry, {number, B}}] -> {ok, A + B}; [{label_entry, _}, _] -> {error, <<"gwr/execution/machine.i32_add: expected a value entry but got a label entry"/utf8>>}; [_, {label_entry, _}] -> {error, <<"gwr/execution/machine.i32_add: expected a value entry but got a label entry"/utf8>>}; [{activation_entry, _}, _] -> {error, <<"gwr/execution/machine.i32_add: expected a value entry but got an activation entry"/utf8>>}; [_, {activation_entry, _}] -> {error, <<"gwr/execution/machine.i32_add: expected a value entry but got an activation entry"/utf8>>}; [] -> {error, <<"gwr/execution/machine.i32_add: empty operands"/utf8>>}; _ -> {error, <<"gwr/execution/machine.i32_add: unknown error"/utf8>>} end, fun(Result) -> {ok, erlang:setelement( 3, State, gwr@execution@stack:push( Stack, {value_entry, {number, Result}} ) )} end). -spec i32_const(machine_state(), integer()) -> {ok, machine_state()} | {error, any()}. i32_const(State, Value) -> {ok, erlang:setelement( 3, State, gwr@execution@stack:push( erlang:element(3, State), {value_entry, {number, Value}} ) )}. -spec local_get(machine_state(), integer()) -> {ok, machine_state()} | {error, binary()}. local_get(State, Index) -> gleam@result:'try'( case begin _pipe = erlang:element( 2, erlang:element(2, erlang:element(3, erlang:element(2, State))) ), _pipe@1 = gleam@list:take(_pipe, Index + 1), gleam@list:last(_pipe@1) end of {ok, V} -> {ok, V}; {error, _} -> {error, <<"gwr/execution/machine.local_get: couldn't get the local with index "/utf8, (gleam@int:to_string(Index))/binary>>} end, fun(Local) -> {ok, erlang:setelement( 3, State, gwr@execution@stack:push( erlang:element(3, State), {value_entry, Local} ) )} end ). -spec execute(machine_state()) -> {ok, machine_state()} | {error, binary()}. execute(Current_state) -> gleam@result:'try'( gleam@list:fold( erlang:element( 3, erlang:element(3, erlang:element(2, Current_state)) ), {ok, Current_state}, fun(Current_state@1, Instruction) -> gleam@result:'try'( Current_state@1, fun(Current_state@2) -> case Instruction of {local_get, Index} -> local_get(Current_state@2, Index); i32_add -> i32_add(Current_state@2); {i32_const, Value} -> i32_const(Current_state@2, Value); _ -> {ok, Current_state@2} end end ) end ), fun(New_state) -> {ok, New_state} end ). -spec call(machine_state(), integer(), list(gwr@execution@runtime:value())) -> {ok, {machine_state(), list(gwr@execution@runtime:value())}} | {error, binary()}. call(State, Index, Arguments) -> Function_address = {function_address, Index}, gleam@result:'try'( case begin _pipe = erlang:element( 2, erlang:element(2, erlang:element(2, State)) ), _pipe@1 = gleam@list:take( _pipe, address_to_int(Function_address) + 1 ), gleam@list:last(_pipe@1) end of {ok, Function_instance} -> {ok, Function_instance}; {error, _} -> {error, <<<<"gwr/execution/machine.call: couldn't find a function instance with the give address \""/utf8, (address_to_string(Function_address))/binary>>/binary, "\""/utf8>>} end, fun(Function_instance@1) -> case Function_instance@1 of {web_assembly_function_instance, Function_type, Function_module_instance, Function_code} -> Function_locals@1 = gleam@list:fold( erlang:element(3, Function_code), Arguments, fun(Function_locals, Local) -> Value = case Local of {number, integer32} -> {number, 0}; {number, integer64} -> {number, 0}; {number, float32} -> {number, 0}; {number, float64} -> {number, 0}; {vector, vector128} -> {vector, 0}; {reference, function_reference} -> {reference, null}; {reference, extern_reference} -> {reference, null} end, lists:append(Function_locals, [Value]) end ), Function_frame = {activation_frame, erlang:length(erlang:element(3, Function_type)), {frame_state, Function_locals@1, Function_module_instance}}, New_state_stack = gwr@execution@stack:push( erlang:element(3, State), {activation_entry, Function_frame} ), New_state_configuration = erlang:setelement( 3, erlang:element(2, State), {thread, erlang:element(3, Function_frame), erlang:element(4, Function_code)} ), New_state = {machine_state, New_state_configuration, New_state_stack}, gleam@result:'try'( execute(New_state), fun(After_state) -> {Result_stack, Result_values} = gwr@execution@stack:pop_repeat( erlang:element(3, After_state), erlang:element(2, Function_frame) ), gleam@bool:guard( gleam@option:values( [gwr@execution@stack:peek(Result_stack)] ) /= [{activation_entry, Function_frame}], {error, <<"gwr/execution/machine.call: expected the last stack frame to be the calling function frame"/utf8>>}, fun() -> Result_values@1 = gleam@option:values( Result_values ), Result_arity = erlang:length( Result_values@1 ), gleam@bool:guard( Result_arity /= erlang:element( 2, Function_frame ), {error, <<<<<<"gwr/execution/machine.call: expected "/utf8, (gleam@int:to_string( erlang:element( 2, Function_frame ) ))/binary>>/binary, " values but got only "/utf8>>/binary, (gleam@int:to_string( Result_arity ))/binary>>}, fun() -> Results = gleam@list:filter_map( Result_values@1, fun(Entry) -> case Entry of {value_entry, V} -> {ok, V}; _ -> {error, nil} end end ), {ok, {erlang:setelement( 3, After_state, Result_stack ), Results}} end ) end ) end ); {host_function_instance, _, _} -> {error, <<"@TODO: call host function"/utf8>>} end end ).