Current section
Files
Jump to
Current section
Files
src/gwr@execution@machine.erl
-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
).