Current section
Files
Jump to
Current section
Files
lib/quickbeam/vm/compiler/lowering/ops/with_scope.ex
defmodule QuickBEAM.VM.Compiler.Lowering.Ops.WithScope do
@moduledoc "with-statement opcodes: with_get_var, with_put_var, with_delete_var, with_make_ref, with_get_ref, with_get_ref_undef."
alias QuickBEAM.VM.Compiler.Lowering.{Builder, State}
alias QuickBEAM.VM.ObjectModel.{Delete, Get, Put}
@doc "Lowers a VM instruction or function into compiler IR."
def lower(state, next_entry, stack_depths, name_args) do
case name_args do
{{:ok, :with_get_var}, [atom_idx, target, _is_with]} ->
lower_with_get_var(state, next_entry, stack_depths, atom_idx, target)
{{:ok, :with_get_ref}, [atom_idx, target, _is_with]} ->
lower_with_get_ref(state, next_entry, stack_depths, atom_idx, target)
{{:ok, :with_get_ref_undef}, [atom_idx, target, _is_with]} ->
lower_with_get_ref_undef(state, next_entry, stack_depths, atom_idx, target)
{{:ok, :with_put_var}, [atom_idx, target, _is_with]} ->
lower_with_put_var(state, next_entry, stack_depths, atom_idx, target)
{{:ok, :with_delete_var}, [atom_idx, _target, _is_with]} ->
with {:ok, obj, state} <- State.pop(state) do
key = State.compiler_call(state, :push_atom_value, [Builder.literal(atom_idx)])
State.effectful_push(
state,
Builder.remote_call(Delete, :delete_property, [obj, key])
)
end
{{:ok, :with_make_ref}, [atom_idx, target, _is_with]} ->
lower_with_make_ref(state, next_entry, stack_depths, atom_idx, target)
_ ->
:not_handled
end
end
defp lower_with_get_var(state, next_entry, stack_depths, atom_idx, target) do
with {:ok, obj, _type, state} <- State.pop_typed(state) do
key = State.compiler_call(state, :push_atom_value, [Builder.literal(atom_idx)])
val = Builder.remote_call(Get, :get, [obj, key])
target_state = State.push(state, val)
branch_with_has_property(state, target_state, next_entry, stack_depths, obj, key, target)
end
end
defp lower_with_put_var(state, next_entry, stack_depths, atom_idx, target) do
with {:ok, obj, next_state} <- State.pop(state),
{:ok, val, target_state} <- State.pop(next_state),
key = State.compiler_call(next_state, :push_atom_value, [Builder.literal(atom_idx)]),
{:ok, target_call} <- State.block_jump_call(target_state, target, stack_depths),
{:ok, next_call} <- State.block_jump_call(next_state, next_entry, stack_depths) do
condition = State.compiler_call(next_state, :with_has_property, [obj, key])
put = Builder.remote_call(Put, :put, [obj, key, val])
branch = Builder.branch_case(condition, [next_call], [put, target_call])
{:done, Enum.reverse([branch | state.body])}
end
end
defp lower_with_get_ref(state, next_entry, stack_depths, atom_idx, target) do
with {:ok, obj, _type, state} <- State.pop_typed(state) do
key = State.compiler_call(state, :push_atom_value, [Builder.literal(atom_idx)])
val = Builder.remote_call(Get, :get, [obj, key])
target_state = state |> State.push(obj) |> State.push(val)
branch_with_has_property(state, target_state, next_entry, stack_depths, obj, key, target)
end
end
defp lower_with_get_ref_undef(state, next_entry, stack_depths, atom_idx, target) do
with {:ok, obj, _type, state} <- State.pop_typed(state) do
key = State.compiler_call(state, :push_atom_value, [Builder.literal(atom_idx)])
val = Builder.remote_call(Get, :get, [obj, key])
target_state = state |> State.push(Builder.atom(:undefined), :undefined) |> State.push(val)
branch_with_has_property(state, target_state, next_entry, stack_depths, obj, key, target)
end
end
defp lower_with_make_ref(state, next_entry, stack_depths, atom_idx, target) do
with {:ok, obj, state} <- State.pop(state) do
key = State.compiler_call(state, :push_atom_value, [Builder.literal(atom_idx)])
target_state = state |> State.push(obj, :object) |> State.push(key, :string)
branch_with_has_property(state, target_state, next_entry, stack_depths, obj, key, target)
end
end
defp branch_with_has_property(state, target_state, next_entry, stack_depths, obj, key, target) do
branch_with_has_property(
state,
target_state,
state,
next_entry,
stack_depths,
obj,
key,
target
)
end
defp branch_with_has_property(
state,
target_state,
next_state,
next_entry,
stack_depths,
obj,
key,
target
) do
with {:ok, target_call} <- State.block_jump_call(target_state, target, stack_depths),
{:ok, next_call} <- State.block_jump_call(next_state, next_entry, stack_depths) do
condition = State.compiler_call(state, :with_has_property, [obj, key])
body =
Enum.reverse([Builder.branch_case(condition, [next_call], [target_call]) | state.body])
{:done, body}
end
end
end