onion_vm/types/lambda/
launcher.rs1use 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, lambda_ref: Arc<OnionLambdaDefinition>,
22 lambda_self_object: OnionStaticObject,
23
24 argument: OnionStaticObject, flatten_argument: Vec<OnionObject>,
26
27 string_pool: OnionKeyPool<Box<str>>,
28 current_argument_index: usize, runnable_mapper:
31 Arc<dyn Fn(Box<dyn Runnable>) -> Result<Box<dyn Runnable>, RuntimeError> + Sync + Send>,
32}
33
34impl OnionLambdaRunnableLauncher {
35 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 Err(RuntimeError::DetailedError(
80 "OnionLambdaRunnableLauncher cannot yield new runnables"
81 .to_string()
82 .into(),
83 ))
84 }
85 StepResult::Return(constraint_result) => {
86 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 Err(RuntimeError::DetailedError(
98 "OnionLambdaRunnableLauncher cannot replace runnables"
99 .to_string()
100 .into(),
101 ))
102 }
103 StepResult::Error(e) => {
104 Err(e.clone())
106 }
107 StepResult::SpawnRunnable(_) => {
108 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}