geam 0.1.1

Experimental Rust-embedded execution runtime for typed Gleam programs
mod bit_array;
mod environment;
mod instruction;
mod pattern;
mod terminator;
mod value;

pub(super) use environment::RetainedValues;
pub(super) use value::GraphValue;

pub(in crate::runtime) use self::environment::BlockEnvironment;
use self::terminator::{GraphAction, NeverCall, terminator_action};
use crate::plan::execution::graph::{BlockGraphExitId, ParamLocal, ProfiledBlockGraph};
use crate::runtime::{ExecutableRuntimePlan, RuntimeGraph};

pub(super) fn execute<Plan: ExecutableRuntimePlan>(
    plan: &Plan,
    state: &mut RuntimeStateFor<'_, Plan>,
    graph: &ProfiledBlockGraph<RuntimeGraph<Plan>>,
    inputs: RetainedValues,
) -> ExecutionResult<CompletedGraph> {
    let mut block_id = graph.entry();
    let mut environment = BlockEnvironment::from_retained(inputs);

    loop {
        let block = graph.block(block_id);
        for instruction in block.instructions() {
            instruction::execute(plan, state, &mut environment, instruction)?;
        }

        match terminator_action(plan, state, &environment, block.terminator())? {
            GraphAction::Continue { block, inputs } => {
                drop(environment);
                state.lists_mut().drain_releases();
                block_id = block;
                environment = BlockEnvironment::from_retained(inputs);
            }
            GraphAction::Exit(exit) => return Ok(CompletedGraph { exit, environment }),
            GraphAction::NeverCall {
                function,
                inputs,
                site,
            } => {
                drop(environment);
                state.lists_mut().drain_releases();
                let origin = crate::runtime::error::HostCallOrigin::source(site);
                return match function {
                    NeverCall::Direct(function) => {
                        crate::runtime::function::run_never(plan, state, function, origin, inputs)
                            .map(|never| match never {})
                    }
                    NeverCall::Value(function) => crate::runtime::function::run_never_value(
                        plan, state, function, origin, inputs,
                    )
                    .map(|never| match never {}),
                };
            }
        }
    }
}
use crate::runtime::error::ExecutionResult;
use crate::runtime::state::{RuntimeState, RuntimeStateFor};

pub(super) struct CompletedGraph {
    exit: BlockGraphExitId,
    environment: BlockEnvironment,
}

impl CompletedGraph {
    pub(super) fn exit(&self) -> BlockGraphExitId {
        self.exit
    }

    pub(super) fn into_value<Value, State>(
        self,
        state: &mut RuntimeState<'_, State>,
        value: &Value,
    ) -> Value::Evaluated
    where
        Value: GraphValue,
    {
        let value = value.read(&self.environment);
        drop(self.environment);
        state.lists_mut().drain_releases();
        value
    }

    pub(super) fn into_retained<State>(
        self,
        state: &mut RuntimeState<'_, State>,
        values: &[ParamLocal],
    ) -> RetainedValues {
        let retained = self.environment.retain(values);
        drop(self.environment);
        state.lists_mut().drain_releases();
        retained
    }
}

pub(in crate::runtime) fn execute_external_list_instruction<Plan>(
    plan: &Plan,
    state: &mut RuntimeStateFor<'_, Plan>,
    environment: &mut BlockEnvironment,
    instruction: &crate::plan::execution::graph::ExternalListInstruction,
    expected: &crate::plan::ValueType,
) -> ExecutionResult<()>
where
    Plan: ExecutableRuntimePlan
        + crate::plan::execution::runtime::RuntimeExecutionPlan<
            Profile = crate::plan::execution::host::HostedExecutionProfile,
        >,
{
    instruction::execute_external_list(plan, state, environment, instruction, expected)
}

pub(in crate::runtime) fn execute_external_function_instruction<Plan>(
    plan: &Plan,
    state: &mut RuntimeStateFor<'_, Plan>,
    environment: &mut BlockEnvironment,
    instruction: &crate::plan::execution::graph::ExternalFunctionInstruction,
) -> ExecutionResult<()>
where
    Plan: ExecutableRuntimePlan
        + crate::plan::execution::runtime::RuntimeExecutionPlan<
            Profile = crate::plan::execution::host::HostedExecutionProfile,
        >,
{
    instruction::execute_external_function(plan, state, environment, instruction)
}

#[cfg(test)]
mod tests {
    use super::RetainedValues;
    use crate::ValueType;
    use crate::plan::execution::function::IntFunctionId;
    use crate::runtime::error::InvariantError;
    use crate::runtime::evaluated::{EvaluatedCustomValue, EvaluatedValue};
    use crate::runtime::state::RuntimeState;
    use crate::runtime::{ExecutionError, Value};

    #[test]
    fn deeply_nested_intra_function_control_flow_runs_iteratively() {
        let mut body = "1".to_string();
        for _ in 0..512 {
            body = format!("case flag {{ True -> {body} False -> 0 }}");
        }
        let source =
            format!("fn deep(flag: Bool) -> Int {{ {body} }} pub fn main() {{ deep(True) }}");
        let plan = std::thread::Builder::new()
            .stack_size(32 * 1024 * 1024)
            .spawn(move || {
                let module = crate::compile_typed_module("main", "main.gleam", source.as_str())
                    .expect("deep source should compile");
                let module_plan = crate::plan_module(module).expect("deep source should plan");
                crate::ExecutionPlan::from_module_plan(module_plan)
            })
            .expect("deep-plan lowering thread should start")
            .join()
            .expect("deep-plan lowering should complete");

        assert_eq!(
            crate::run_main(&plan, &mut Vec::new()),
            Ok(Value::Int(1.into())),
        );
    }

    #[test]
    fn match_terminator_propagates_custom_field_family_corruption() {
        let plan = execution_plan(
            r#"
pub type Boxed {
  Boxed(Int)
  Empty
}

fn read(value: Boxed) {
  let assert Boxed(inner) = value
  inner
}

pub fn main() {
  read(Boxed(1))
}
"#,
        );
        assert_eq!(
            crate::run_main(&plan, &mut Vec::new()),
            Ok(Value::Int(1.into())),
        );

        let constructor = plan.custom_constructor_id(0, 0);
        let descriptor = plan.custom_constructor(constructor);
        let malformed = EvaluatedCustomValue::from_fields(
            constructor,
            vec![EvaluatedValue::String("wrong".into())].into_boxed_slice(),
        );
        let mut inputs = RetainedValues::empty();
        inputs.push_evaluated(EvaluatedValue::Custom(malformed));

        assert_eq!(
            super::execute(
                &plan,
                &mut RuntimeState::new(&mut Vec::new()),
                plan.int_function(IntFunctionId(1)).body().block_graph(),
                inputs,
            )
            .map(|_| ()),
            Err(ExecutionError::Invariant(
                InvariantError::CustomFieldFamilyMismatch {
                    custom_type: plan.custom_value_type(constructor.type_id()),
                    constructor: descriptor.name().clone(),
                    field_index: 0,
                    expected: ValueType::Int,
                    actual: ValueType::String,
                },
            )),
        );
    }

    fn execution_plan(source: &str) -> crate::ExecutionPlan {
        let module = crate::compile_typed_module("main", "main.gleam", source)
            .expect("source should compile");
        let module_plan = crate::plan_module(module).expect("source should plan");
        crate::ExecutionPlan::from_module_plan(module_plan)
    }
}