onion_vm/types/lambda/
launcher.rs1use std::sync::Arc;
14
15use arc_gc::gc::GC;
16
17use crate::{
18 lambda::runnable::{Runnable, RuntimeError, StepResult},
19 types::{
20 lambda::definition::OnionLambdaDefinition,
21 object::{OnionObject, OnionObjectCell, OnionStaticObject},
22 },
23 unwrap_step_result,
24 utils::{
25 fastmap::{OnionFastMap, OnionKeyPool},
26 format_object_summary,
27 },
28};
29
30#[allow(unused)]
45pub struct OnionLambdaRunnableLauncher {
46 lambda: OnionStaticObject,
48 lambda_ref: Arc<OnionLambdaDefinition>,
50 lambda_self_object: OnionStaticObject,
52
53 argument: OnionStaticObject,
55 flatten_argument: Vec<OnionObject>,
57
58 string_pool: OnionKeyPool<Box<str>>,
60 current_argument_index: usize,
62
63 runnable_mapper:
65 Box<dyn Fn(Box<dyn Runnable>) -> Result<Box<dyn Runnable>, RuntimeError> + Sync + Send>,
66}
67
68impl OnionLambdaRunnableLauncher {
69 pub fn new<F: Sync + Send + 'static>(
84 lambda: &OnionObject,
85 argument: OnionStaticObject,
86 runnable_mapper: F,
87 ) -> Result<OnionLambdaRunnableLauncher, RuntimeError>
88 where
89 F: Fn(Box<dyn Runnable>) -> Result<Box<dyn Runnable>, RuntimeError> + Sync + Send + 'static,
90 {
91 let OnionObject::Lambda((lambda_ref, self_object)) = lambda else {
92 return Err(RuntimeError::InvalidType(
93 "Cannot launch non-lambda object".into(),
94 ));
95 };
96 let key_pool = lambda_ref.create_key_pool();
97
98 let flatten_argument = lambda_ref
100 .get_parameter()
101 .unpack_arguments(argument.weak())?;
102
103 Ok(Self {
104 lambda: lambda.stabilize(),
105 lambda_ref: lambda_ref.clone(),
106 lambda_self_object: self_object.stabilize(),
107 argument,
108 flatten_argument,
109 string_pool: key_pool.clone(),
110 current_argument_index: 0,
111 runnable_mapper: Box::new(runnable_mapper),
112 })
113 }
114}
115
116impl Runnable for OnionLambdaRunnableLauncher {
117 fn receive(
124 &mut self,
125 step_result: &StepResult,
126 _gc: &mut GC<OnionObjectCell>,
127 ) -> Result<(), RuntimeError> {
128 match step_result {
129 StepResult::Continue => Ok(()),
130 StepResult::NewRunnable(_) => Err(RuntimeError::DetailedError(
131 "OnionLambdaRunnableLauncher cannot yield new runnables"
132 .to_string()
133 .into(),
134 )),
135 StepResult::Return(constraint_result) => {
136 if constraint_result.weak().to_boolean()? {
138 Ok(())
139 } else {
140 Err(RuntimeError::InvalidOperation(
141 "Constraint check failed".into(),
142 ))
143 }
144 }
145 StepResult::ReplaceRunnable(_) => Err(RuntimeError::DetailedError(
146 "OnionLambdaRunnableLauncher cannot replace runnables"
147 .to_string()
148 .into(),
149 )),
150 StepResult::Error(e) => Err(e.clone()),
151 StepResult::SpawnRunnable(_) => Err(RuntimeError::DetailedError(
152 "OnionLambdaRunnableLauncher cannot spawn new runnables"
153 .to_string()
154 .into(),
155 )),
156 }
157 }
158
159 fn step(&mut self, gc: &mut GC<OnionObjectCell>) -> StepResult {
168 if self.current_argument_index == self.lambda_ref.get_flatten_param_keys().len() {
169 let mut collected_arguments = OnionFastMap::new(self.string_pool.clone());
171 for i in 0..self.current_argument_index {
172 collected_arguments.push(
173 &self.lambda_ref.get_flatten_param_keys()[i],
174 self.flatten_argument[i].stabilize(),
175 );
176 }
177
178 let runnable = unwrap_step_result!(self.lambda_ref.create_runnable(
179 &collected_arguments,
180 &self.lambda,
181 self.lambda_self_object.weak(),
182 gc,
183 ));
184
185 let mapped = unwrap_step_result!((self.runnable_mapper)(runnable));
187 return StepResult::ReplaceRunnable(mapped);
188 }
189
190 let mut index = self.current_argument_index;
192 while index < self.lambda_ref.get_flatten_param_keys().len() {
193 match &self.lambda_ref.get_flatten_param_constraints()[index] {
194 OnionObject::Boolean(v) => {
195 if !*v {
196 self.current_argument_index = index + 1;
197 return StepResult::Error(RuntimeError::InvalidOperation(
198 "Constraint check failed".into(),
199 ));
200 }
201 }
202 lambda @ OnionObject::Lambda(_) => {
203 self.current_argument_index = index + 1;
205 return StepResult::NewRunnable(Box::new(unwrap_step_result!(
206 OnionLambdaRunnableLauncher::new(
207 lambda,
208 self.flatten_argument[index].stabilize(),
209 |r| Ok(r),
210 )
211 )));
212 }
213 v => {
214 self.current_argument_index = index + 1;
215 return StepResult::Error(
216 RuntimeError::InvalidType(
217 format!(
218 "Expect boolean or lambda for constraint, but found: {:?}",
219 v
220 )
221 .into(),
222 )
223 .into(),
224 );
225 }
226 }
227 index += 1;
228 }
229 self.current_argument_index = index;
230 StepResult::Continue
231 }
232
233 fn format_context(&self) -> String {
237 "-> At lambda runnable launcher".to_string()
238 + &format!(
239 " (current index: {}, expected: {})",
240 self.current_argument_index,
241 self.lambda_ref.get_flatten_param_keys().len()
242 )
243 + &format!(", lambda: {}", format_object_summary(self.lambda.weak()))
244 }
245}