onion_vm/types/lambda/
launcher.rs

1use std::sync::Arc;
2
3use arc_gc::gc::GC;
4
5use crate::{
6    lambda::runnable::{Runnable, RuntimeError, StepResult},
7    types::{
8        lambda::definition::OnionLambdaDefinition,
9        object::{OnionObject, OnionObjectCell, OnionStaticObject},
10    },
11    unwrap_step_result,
12    utils::{
13        fastmap::{OnionFastMap, OnionKeyPool},
14        format_object_summary,
15    },
16};
17
18#[allow(unused)]
19pub struct OnionLambdaRunnableLauncher {
20    lambda: OnionStaticObject, // The OnionObject::Lambda itself
21    lambda_ref: Arc<OnionLambdaDefinition>,
22    lambda_self_object: OnionStaticObject,
23
24    argument: OnionStaticObject, // hold the refs for flatten_argument
25    flatten_argument: Vec<OnionObject>,
26
27    string_pool: OnionKeyPool<Box<str>>,
28    current_argument_index: usize, // Index into argument_elements for current phase
29
30    runnable_mapper:
31        Arc<dyn Fn(Box<dyn Runnable>) -> Result<Box<dyn Runnable>, RuntimeError> + Sync + Send>,
32}
33
34impl OnionLambdaRunnableLauncher {
35    // string_pool 是被调用的 Lambda 的所需要的字符串池,这意味着被调用者无法使用除了 string_pool 中的字符串
36    // runnable_mapper 是一个函数,用于将生成的 Runnable 进行映射处理
37    pub fn new_static<F: Sync + Send + 'static>(
38        lambda: &OnionObject,
39        argument: OnionStaticObject,
40        runnable_mapper: F,
41    ) -> Result<OnionLambdaRunnableLauncher, RuntimeError>
42    where
43        F: Fn(Box<dyn Runnable>) -> Result<Box<dyn Runnable>, RuntimeError> + Sync + Send + 'static,
44    {
45        let OnionObject::Lambda((lambda_ref, self_object)) = lambda else {
46            return Err(RuntimeError::InvalidType(
47                "Cannot launch non-lambda object".into(),
48            ));
49        };
50        let key_pool = lambda_ref.create_key_pool();
51
52        let flatten_argument = lambda_ref
53            .get_parameter()
54            .unpack_arguments(argument.weak())?;
55
56        Ok(Self {
57            lambda: lambda.stabilize(),
58            lambda_ref: lambda_ref.clone(),
59            lambda_self_object: self_object.stabilize(),
60            argument,
61            flatten_argument,
62            string_pool: key_pool.clone(),
63            current_argument_index: 0,
64            runnable_mapper: Arc::new(runnable_mapper),
65        })
66    }
67}
68
69impl Runnable for OnionLambdaRunnableLauncher {
70    fn receive(
71        &mut self,
72        step_result: &StepResult,
73        _gc: &mut GC<OnionObjectCell>,
74    ) -> Result<(), RuntimeError> {
75        match step_result {
76            StepResult::Continue => Ok(()),
77            StepResult::NewRunnable(_) => {
78                // This should not happen, as this launcher is not designed to yield new runnables.
79                Err(RuntimeError::DetailedError(
80                    "OnionLambdaRunnableLauncher cannot yield new runnables"
81                        .to_string()
82                        .into(),
83                ))
84            }
85            StepResult::Return(constraint_result) => {
86                // 我们在这里接收约束求解结果
87                if constraint_result.weak().to_boolean()? {
88                    Ok(())
89                } else {
90                    Err(RuntimeError::InvalidOperation(
91                        "Constraint check failed".into(),
92                    ))
93                }
94            }
95            StepResult::ReplaceRunnable(_) => {
96                // This should not happen, as this launcher is not designed to replace runnables.
97                Err(RuntimeError::DetailedError(
98                    "OnionLambdaRunnableLauncher cannot replace runnables"
99                        .to_string()
100                        .into(),
101                ))
102            }
103            StepResult::Error(e) => {
104                // Propagate any errors received from the runnable.
105                Err(e.clone())
106            }
107            StepResult::SpawnRunnable(_) => {
108                // This should not happen, as this launcher is not designed to spawn new runnables.
109                Err(RuntimeError::DetailedError(
110                    "OnionLambdaRunnableLauncher cannot spawn new runnables"
111                        .to_string()
112                        .into(),
113                ))
114            }
115        }
116    }
117
118    fn step(&mut self, gc: &mut GC<OnionObjectCell>) -> StepResult {
119        if self.current_argument_index == self.lambda_ref.get_flatten_param_keys().len() {
120            let mut collected_arguments = OnionFastMap::new(self.string_pool.clone());
121            for i in 0..self.current_argument_index {
122                collected_arguments.push(
123                    &self.lambda_ref.get_flatten_param_keys()[i],
124                    self.flatten_argument[i].stabilize(),
125                );
126            }
127
128            let runnable = unwrap_step_result!(self.lambda_ref.create_runnable(
129                &collected_arguments,
130                &self.lambda,
131                self.lambda_self_object.weak(),
132                gc,
133            ));
134
135            let mapped = unwrap_step_result!((self.runnable_mapper)(runnable));
136            return StepResult::ReplaceRunnable(mapped);
137        }
138
139        let mut index = self.current_argument_index;
140        while index < self.lambda_ref.get_flatten_param_keys().len() {
141            match &self.lambda_ref.get_flatten_param_constraints()[index] {
142                OnionObject::Boolean(v) => {
143                    if !*v {
144                        self.current_argument_index = index + 1;
145                        return StepResult::Error(RuntimeError::InvalidOperation(
146                            "Constraint check failed".into(),
147                        ));
148                    }
149                }
150                lambda @ OnionObject::Lambda(_) => {
151                    self.current_argument_index = index + 1;
152                    return StepResult::NewRunnable(Box::new(unwrap_step_result!(
153                        OnionLambdaRunnableLauncher::new_static(
154                            lambda,
155                            self.flatten_argument[index].stabilize(),
156                            |r| Ok(r),
157                        )
158                    )));
159                }
160                v => {
161                    self.current_argument_index = index + 1;
162                    return StepResult::Error(
163                        RuntimeError::InvalidType(
164                            format!(
165                                "Expect boolean or lambda for constraint, but found: {:?}",
166                                v
167                            )
168                            .into(),
169                        )
170                        .into(),
171                    );
172                }
173            }
174            index += 1;
175        }
176        self.current_argument_index = index;
177        StepResult::Continue
178    }
179
180    fn format_context(&self) -> String {
181        "-> At lambda runnable launcher".to_string()
182            + &format!(
183                " (current index: {}, expected: {})",
184                self.current_argument_index,
185                self.lambda_ref.get_flatten_param_keys().len()
186            )
187            + &format!(", lambda: {}", format_object_summary(self.lambda.weak()))
188    }
189}