onion_vm/types/lambda/
launcher.rs

1use std::sync::Arc;
2
3use arc_gc::{arc::GCArc, gc::GC};
4
5use crate::{
6    lambda::{
7        runnable::{Runnable, RuntimeError, StepResult},
8        scheduler::scheduler::Scheduler,
9    },
10    types::{
11        named::OnionNamed,
12        object::{OnionObject, OnionObjectCell, OnionStaticObject},
13        tuple::OnionTuple,
14    },
15    unwrap_step_result,
16};
17
18#[derive(Clone)]
19enum ArgumentProcessingPhase {
20    NamedArguments,
21    PositionalArguments,
22    Done,
23}
24
25pub struct OnionLambdaRunnableLauncher {
26    lambda: OnionStaticObject, // The OnionObject::Lambda itself
27
28    parameter_tuple: Arc<OnionTuple>, // The parameter tuple from the lambda definition
29    #[allow(unused_attributes)]
30    parameter_arcs: Vec<GCArc<OnionObjectCell>>, // The parameter arcs from the lambda definition
31
32    argument_tuple: Arc<OnionTuple>, // The provided argument tuple object
33    #[allow(unused_attributes)]
34    argument_arcs: Vec<GCArc<OnionObjectCell>>, // The provided argument arcs from the argument tuple
35
36    collected_arguments: Vec<OnionStaticObject>, // Arguments collected during processing
37    assigned: Vec<bool>, // Tracks assignment for parameter slots, then for appended args
38    current_argument_index: usize, // Index into argument_elements for current phase
39
40    phase: ArgumentProcessingPhase,
41
42    runnable_mapper:
43        Arc<dyn Fn(Box<dyn Runnable>) -> Result<Box<dyn Runnable>, RuntimeError> + Sync + Send>,
44    constrain_runnable: Option<Box<dyn Runnable>>,
45}
46
47impl OnionLambdaRunnableLauncher {
48    pub fn new_static<F: Sync + Send + 'static>(
49        lambda_obj: &OnionStaticObject,
50        argument_tuple_obj: &OnionStaticObject,
51        runnable_mapper: F,
52    ) -> Result<OnionLambdaRunnableLauncher, RuntimeError>
53    where
54        F: Fn(Box<dyn Runnable>) -> Result<Box<dyn Runnable>, RuntimeError> + Sync + Send + 'static,
55    {
56        // Initialize collected_arguments based on parameter count
57        let mut collected_arguments = Vec::new();
58        let mut assigned = Vec::new();
59
60        let (parameter_tuple, parameter_arcs) = lambda_obj.weak().with_data(|obj_ref| {
61            if let OnionObject::Lambda(definition) = obj_ref {
62                definition.get_parameter().with_data(|p_obj| {
63                    if let OnionObject::Tuple(tuple) = p_obj {
64                        // Initialize collected_arguments and assigned based on parameter count
65                        for param in tuple.get_elements() {
66                            param.with_data(|param_obj| {
67                                match param_obj {
68                                    OnionObject::LazySet(lazy_set) => {
69                                        // If the parameter is a LazySet, we collect its container.
70                                        collected_arguments
71                                            .push(lazy_set.get_container().stabilize());
72                                    }
73                                    _ => {
74                                        // For other types, we just clone the parameter as is.
75                                        collected_arguments.push(param.stabilize());
76                                    }
77                                }
78                                Ok(())
79                            })?;
80                        }
81                        assigned = vec![false; tuple.get_elements().len()];
82
83                        let parameter_tuple = tuple.clone();
84                        let mut arcs = vec![];
85                        tuple.upgrade(&mut arcs);
86
87                        Ok((parameter_tuple, arcs))
88                    } else {
89                        Err(RuntimeError::DetailedError(
90                            "Lambda parameters must be a Tuple".to_string().into(),
91                        ))
92                    }
93                })
94            } else {
95                Err(RuntimeError::DetailedError(
96                    "Expected a Lambda definition object".to_string().into(),
97                ))
98            }
99        })?;
100
101        let (argument_tuple, argument_arcs) = argument_tuple_obj.weak().with_data(|arg_obj| {
102            if let OnionObject::Tuple(tuple) = arg_obj {
103                // Initialize argument_tuple and its arcs(simulate `stabilize` behavior)
104                let argument_tuple = tuple.clone();
105                let mut arcs = vec![];
106                tuple.upgrade(&mut arcs);
107                Ok((argument_tuple, arcs))
108            } else {
109                Err(RuntimeError::DetailedError(
110                    "Lambda arguments must be a Tuple".to_string().into(),
111                ))
112            }
113        })?;
114
115        Ok(OnionLambdaRunnableLauncher {
116            lambda: lambda_obj.clone(),
117            parameter_tuple: parameter_tuple,
118            parameter_arcs: parameter_arcs,
119            argument_tuple: argument_tuple,
120            argument_arcs: argument_arcs,
121            collected_arguments,
122            assigned,
123            phase: ArgumentProcessingPhase::NamedArguments,
124            current_argument_index: 0,
125            runnable_mapper: Arc::new(runnable_mapper),
126            constrain_runnable: None,
127        })
128    }
129}
130
131impl Runnable for OnionLambdaRunnableLauncher {
132    fn copy(&self) -> Box<dyn Runnable> {
133        Box::new(OnionLambdaRunnableLauncher {
134            lambda: self.lambda.clone(),
135            parameter_tuple: self.parameter_tuple.clone(),
136            parameter_arcs: self.parameter_arcs.clone(),
137            argument_tuple: self.argument_tuple.clone(),
138            argument_arcs: self.argument_arcs.clone(),
139            collected_arguments: self.collected_arguments.clone(),
140            assigned: self.assigned.clone(),
141            phase: self.phase.clone(),
142            current_argument_index: self.current_argument_index,
143            runnable_mapper: self.runnable_mapper.clone(),
144            constrain_runnable: match &self.constrain_runnable {
145                Some(runnable) => Some(runnable.copy()),
146                None => None,
147            },
148        })
149    }
150
151    fn receive(
152        &mut self,
153        step_result: &StepResult,
154        _gc: &mut GC<OnionObjectCell>,
155    ) -> Result<(), RuntimeError> {
156        match step_result {
157            StepResult::Continue => Ok(()),
158            StepResult::NewRunnable(_) => {
159                // This should not happen, as this launcher is not designed to yield new runnables.
160                Err(RuntimeError::DetailedError(
161                    "OnionLambdaRunnableLauncher cannot yield new runnables"
162                        .to_string()
163                        .into(),
164                ))
165            }
166            StepResult::Return(_) => {
167                // This should not happen, as this launcher is not designed to return values.
168                Err(RuntimeError::DetailedError(
169                    "OnionLambdaRunnableLauncher cannot return values"
170                        .to_string()
171                        .into(),
172                ))
173            }
174            StepResult::ReplaceRunnable(_) => {
175                // This should not happen, as this launcher is not designed to replace runnables.
176                Err(RuntimeError::DetailedError(
177                    "OnionLambdaRunnableLauncher cannot replace runnables"
178                        .to_string()
179                        .into(),
180                ))
181            }
182            StepResult::Error(e) => {
183                // Propagate any errors received from the runnable.
184                Err(e.clone())
185            }
186            StepResult::SetSelfObject(_) => {
187                // This should not happen, as this launcher is not designed to set self objects.
188                Err(RuntimeError::DetailedError(
189                    "OnionLambdaRunnableLauncher cannot set self objects"
190                        .to_string()
191                        .into(),
192                ))
193            }
194            StepResult::SpawnRunnable(_) => {
195                // This should not happen, as this launcher is not designed to spawn new runnables.
196                Err(RuntimeError::DetailedError(
197                    "OnionLambdaRunnableLauncher cannot spawn new runnables"
198                        .to_string()
199                        .into(),
200                ))
201            }
202        }
203    }
204    /// 执行 Lambda 启动器的下一步操作。
205    ///
206    /// 此方法通过一个状态机来处理参数的收集、验证(通过约束)并最终启动 Lambda。
207    /// 它的主要职责是:
208    /// 1. 如果存在参数约束 (`constrain_runnable`),则先执行约束。
209    /// 2. 根据当前的 `phase`(命名参数、位置参数、完成)处理传入的参数。
210    /// 3. 收集和整理参数,直到所有参数处理完毕。
211    /// 4. 创建并返回目标 Lambda 的可运行实例。
212    ///
213    /// # 参数
214    /// * `gc`: 垃圾收集器的可变引用,用于内存管理。
215    ///
216    /// # 返回
217    /// * `Ok(StepResult)`: 表示操作成功,并指示调度器下一步应该做什么。
218    ///   - `StepResult::Continue`: 表示启动器需要更多步骤来完成参数处理或约束执行。
219    ///   - `StepResult::ReplaceRunnable`: 表示参数处理完成,启动器应被新的 Lambda 可运行实例替换。
220    /// * `Err(RuntimeError)`: 表示在执行过程中发生错误。
221    fn step(&mut self, gc: &mut GC<OnionObjectCell>) -> StepResult {
222        // 阶段一:执行参数约束 (如果存在)
223        // 如果 `constrain_runnable` (通常是一个由 LazySet 的 filter 创建的 Lambda 启动器) 存在,
224        // 意味着当前正在处理的参数有一个关联的约束需要被满足。
225        if let Some(constrain_runnable) = self.constrain_runnable.as_mut() {
226            // 执行约束可运行对象的 `step` 方法
227            match constrain_runnable.step(gc) {
228                // 约束尚未完成,需要继续执行。启动器也返回 Continue。
229                StepResult::Continue => {
230                    return StepResult::Continue;
231                }
232                // 约束执行过程中不应产生新的可运行对象或替换自身。
233                v @ StepResult::NewRunnable(_) => return v,
234                v @ StepResult::SpawnRunnable(_) => return v,
235                v @ StepResult::Error(_) => return v,
236                StepResult::ReplaceRunnable(runnable) => self.constrain_runnable = Some(runnable),
237
238                // 约束执行完成并返回了一个值。
239                // 约束的返回值约定为一个 Pair:(布尔值表示是否panic, 布尔值表示约束是否通过)
240                StepResult::Return(ref v) => {
241                    // 解析约束的返回值
242                    unwrap_step_result!(v.weak().with_data(|v_obj| {
243                        // 约束的返回值必须是一个 Pair 对象
244                        let OnionObject::Pair(constrain_result_pair) = v_obj else {
245                            // 如果不是 Pair,则认为约束失败(或者约束实现有误)
246                            return Err(RuntimeError::DetailedError(
247                                "Constrain runnable did not return a Pair object"
248                                    .to_string()
249                                    .into(),
250                            ));
251                        };
252
253                        // 第一个元素 (key) 表示约束执行是否 panic
254                        match constrain_result_pair.get_key().to_boolean() {
255                            Ok(true) => {
256                                // true 表示约束执行没有 panic
257                                // 第二个元素 (value) 表示约束是否通过
258                                if constrain_result_pair.get_value().to_boolean()? {
259                                    // 约束通过,清除 `constrain_runnable`,准备处理下一个参数或阶段。
260                                    self.constrain_runnable = None;
261                                    // 注意:这里原代码是 `return Ok(())`,这不符合 `step` 的签名。
262                                    // 应该返回 `Ok(StepResult::Continue)` 以便继续处理参数。
263                                    // 假设这是期望的行为,即约束成功后,继续当前 `step` 的后续逻辑。
264                                    // 如果这里直接返回,则当前 `step` 的参数处理逻辑会被跳过。
265                                    // 为了与原逻辑最接近(即清除约束后继续本轮 step 的后续参数处理),
266                                    // 我们不在这里 `return`,而是让代码流继续到下面的 `match self.phase`。
267                                    // 如果期望的是约束成功后立即开始下一轮 `step`,則應 `return Ok(StepResult::Continue)`。
268                                    // 鉴于后续代码会继续处理参数,这里不返回是合理的。
269                                    Ok(()) // 标记约束已处理,但不立即返回,让后续的 phase match 执行
270                                } else {
271                                    // 约束未通过 (返回 false)
272                                    return Err(RuntimeError::DetailedError(
273                                        "Argument constraint failed".to_string().into(),
274                                    ));
275                                }
276                            }
277                            Ok(false) => {
278                                // false 表示约束执行过程中发生了 panic
279                                return Err(RuntimeError::CustomValue(Box::new(
280                                    constrain_result_pair.get_value().stabilize(),
281                                )));
282                            }
283                            Err(err) => return Err(err), // 转换布尔值失败
284                        }
285                    }))
286
287                    // 如果约束成功并通过 (上面返回 Ok(()) 但没有实际 return),则会继续到下面的 phase 处理。
288                    // 如果约束失败或 panic (上面返回 Err),则整个 step 会在这里结束。
289                }
290                StepResult::SetSelfObject(_) => {
291                    // 这个启动器不支持设置 self 对象,因此返回错误。
292                    return StepResult::Error(RuntimeError::DetailedError(
293                        "OnionLambdaRunnableLauncher does not support setting self object"
294                            .to_string()
295                            .into(),
296                    ));
297                }
298            }
299        } // 结束 `if let Some(constrain_runnable)`
300
301        // 阶段二:根据当前处理阶段 (phase) 处理参数
302        match self.phase {
303            // 阶段 2.1: 处理命名参数
304            ArgumentProcessingPhase::NamedArguments => {
305                // 直接使用 self.argument_tuple 获取参数元素
306                let argument_elements = &self.argument_tuple.get_elements();
307                let argument_count = argument_elements.len();
308
309                // 如果当前参数索引超出了提供的参数列表的范围,
310                // 说明所有提供的参数都已在命名参数阶段被初步检查过。
311                // 切换到位置参数处理阶段。
312                if self.current_argument_index >= argument_count {
313                    self.phase = ArgumentProcessingPhase::PositionalArguments;
314                    self.current_argument_index = 0; // 重置索引以供位置参数阶段使用
315                    return StepResult::Continue; // 请求调度器再次调用 step
316                }
317
318                // 获取当前正在处理的由调用者提供的参数
319                let current_arg_index = self.current_argument_index;
320                self.current_argument_index += 1; // 移动到下一个提供的参数
321
322                let arg_obj_view = &argument_elements[current_arg_index];
323                // 检查当前提供的参数是否是命名参数 (`OnionObject::Named`)
324                if let OnionObject::Named(named_arg) = arg_obj_view {
325                    let key_to_match = &named_arg.get_key(); // 获取命名参数的名称
326
327                    // 遍历 Lambda 定义中的参数,直接使用 self.parameter_tuple
328                    let parameter_elements = &self.parameter_tuple.get_elements();
329                    let parameter_count = parameter_elements.len();
330
331                    for param_idx in 0..parameter_count {
332                        // 直接访问参数定义
333                        let param_element = &parameter_elements[param_idx];
334                        let matched = match param_element {
335                            // 情况 A: Lambda 定义的参数也是一个命名参数 (`name: Type`)
336                            OnionObject::Named(param_named_def) => {
337                                if unwrap_step_result!(param_named_def
338                                    .get_key()
339                                    .equals(key_to_match))
340                                {
341                                    // 名称匹配成功!
342                                    self.collected_arguments[param_idx] = arg_obj_view.stabilize();
343                                    self.assigned[param_idx] = true;
344                                    true
345                                } else {
346                                    false
347                                }
348                            }
349                            // 情况 B: Lambda 定义的参数是一个 LazySet (`name: {constraint}`)
350                            OnionObject::LazySet(lazy_set) => {
351                                if let OnionObject::Named(container_named) =
352                                    lazy_set.get_container()
353                                {
354                                    if unwrap_step_result!(container_named
355                                        .get_key()
356                                        .equals(key_to_match))
357                                    {
358                                        // 名称匹配成功!
359                                        self.collected_arguments[param_idx] =
360                                            arg_obj_view.stabilize();
361                                        self.assigned[param_idx] = true;
362
363                                        // 设置约束
364                                        let argument_for_filter = OnionObject::Tuple(
365                                            OnionTuple::new(vec![named_arg.get_value().clone()])
366                                                .into(),
367                                        )
368                                        .consume_and_stabilize();
369
370                                        let runnable = Box::new(unwrap_step_result!(
371                                            OnionLambdaRunnableLauncher::new_static(
372                                                &lazy_set.get_filter().stabilize(),
373                                                &argument_for_filter,
374                                                &|r| Ok(r),
375                                            )
376                                        ));
377                                        self.constrain_runnable =
378                                            Some(Box::new(Scheduler::new(vec![runnable])));
379                                        true
380                                    } else {
381                                        false
382                                    }
383                                } else {
384                                    false
385                                }
386                            }
387                            _ => false,
388                        };
389
390                        if matched {
391                            return StepResult::Continue;
392                        }
393                    }
394
395                    // 如果遍历完所有 Lambda 定义的参数后,没有找到匹配的名称,
396                    // 说明这是一个额外的命名参数。
397                    self.collected_arguments.push(arg_obj_view.stabilize());
398                    self.assigned.push(true);
399                };
400
401                // 如果当前提供的参数不是 OnionObject::Named,则在命名参数阶段被忽略。
402                StepResult::Continue
403            }
404            // 阶段 2.2: 处理位置参数
405            ArgumentProcessingPhase::PositionalArguments => {
406                // 直接使用 self.argument_tuple 获取参数数量
407                let argument_count = self.argument_tuple.get_elements().len();
408
409                // 如果当前参数索引超出了提供的参数列表的范围,
410                // 说明所有提供的参数都已在位置参数阶段被处理。
411                // 切换到完成阶段。
412                if self.current_argument_index >= argument_count {
413                    self.phase = ArgumentProcessingPhase::Done;
414                    return StepResult::Continue; // 请求调度器再次调用 step
415                }
416
417                // 获取当前正在处理的由调用者提供的参数
418                let current_processing_arg_idx = self.current_argument_index;
419                self.current_argument_index += 1; // 移动到下一个提供的参数
420
421                // 直接从 self.argument_tuple 获取参数元素
422                let argument_elements = &self.argument_tuple.get_elements();
423
424                if current_processing_arg_idx >= argument_elements.len() {
425                    return StepResult::Error(RuntimeError::DetailedError(
426                        "Argument index out of bounds during positional processing"
427                            .to_string()
428                            .into(),
429                    ));
430                }
431
432                let current_provided_arg_view = &argument_elements[current_processing_arg_idx];
433
434                // 如果当前提供的参数是命名参数,则在位置参数阶段跳过。
435                if let OnionObject::Named(_) = current_provided_arg_view {
436                    // Skip named arguments in this phase.
437                } else {
438                    // This is a positional argument.
439                    let current_provided_arg_static = current_provided_arg_view.stabilize();
440
441                    // 尝试找到第一个尚未被赋值的 Lambda 定义参数槽。
442                    if let Some(param_idx) = self.assigned.iter().position(|&assigned| !assigned) {
443                        // 找到了一个未分配的参数槽。直接从 self.parameter_tuple 访问参数定义
444                        let parameter_elements = &self.parameter_tuple.get_elements();
445
446                        if param_idx >= parameter_elements.len() {
447                            return StepResult::Error(RuntimeError::DetailedError(
448                                "Parameter index out of bounds for assignment"
449                                    .to_string()
450                                    .into(),
451                            ));
452                        }
453
454                        let param_def = &parameter_elements[param_idx];
455                        match param_def {
456                            // 情况 A: Lambda 定义的参数是 `name: Type` (Named)
457                            OnionObject::Named(original_named_def) => {
458                                // 将位置参数包装成一个新的 Named 对象,使用原始定义的名称。
459                                let new_value_for_slot = OnionObject::Named(
460                                    OnionNamed::new(
461                                        original_named_def.get_key().clone(),
462                                        current_provided_arg_view.clone(),
463                                    )
464                                    .into(),
465                                )
466                                .consume_and_stabilize();
467                                self.collected_arguments[param_idx] = new_value_for_slot;
468                                self.assigned[param_idx] = true;
469                            }
470                            // 情况 B: Lambda 定义的参数是 `name: {constraint}` (LazySet)
471                            OnionObject::LazySet(lazy_set_def) => {
472                                if let OnionObject::Named(container_named_def) =
473                                    lazy_set_def.get_container()
474                                {
475                                    // 将位置参数包装成 Named 对象
476                                    let new_value_for_slot = OnionObject::Named(
477                                        OnionNamed::new(
478                                            container_named_def.get_key().clone(),
479                                            current_provided_arg_view.clone(),
480                                        )
481                                        .into(),
482                                    )
483                                    .consume_and_stabilize();
484                                    self.collected_arguments[param_idx] = new_value_for_slot;
485                                    self.assigned[param_idx] = true;
486
487                                    // 设置约束
488                                    // The argument to the filter lambda is the provided argument itself.
489                                    let argument_for_filter = OnionObject::Tuple(
490                                        OnionTuple::new(vec![current_provided_arg_view.clone()])
491                                            .into(),
492                                    )
493                                    .consume_and_stabilize();
494
495                                    let runnable = Box::new(unwrap_step_result!(
496                                        OnionLambdaRunnableLauncher::new_static(
497                                            &lazy_set_def.get_filter().stabilize(),
498                                            &argument_for_filter,
499                                            &|r| Ok(r),
500                                        )
501                                    ));
502                                    self.constrain_runnable =
503                                        Some(Box::new(Scheduler::new(vec![runnable])));
504                                } else {
505                                    return StepResult::Error(RuntimeError::DetailedError(
506                                        "LazySet's container must be a Named object for positional assignment".to_string().into(),
507                                    ));
508                                }
509                            }
510                            // 情况 C: Lambda 定义的参数是普通类型
511                            _ => {
512                                self.collected_arguments[param_idx] = current_provided_arg_static;
513                                self.assigned[param_idx] = true;
514                            }
515                        }
516                    } else {
517                        // 所有 Lambda 定义的参数槽都已被填充。 This is an extra positional argument.
518                        self.collected_arguments.push(current_provided_arg_static);
519                        self.assigned.push(true);
520                    }
521                }
522
523                StepResult::Continue
524            }
525
526            // 阶段 2.3: 完成参数处理,准备启动 Lambda
527            ArgumentProcessingPhase::Done => {
528                // 检查是否仍有未完成的约束。如果 `constrain_runnable` 仍然是 `Some`,
529                // 这意味着上一个参数的约束还没有执行完毕或返回结果。
530                // 此时应该等待约束完成,而不是直接创建 Lambda。
531                if self.constrain_runnable.is_some() {
532                    // 理论上,如果约束存在,应该在 `step` 的开头被处理。
533                    // 如果执行到 `Done` 阶段约束仍在,说明之前的约束处理逻辑可能需要返回 `Continue`
534                    // 直到约束被清除。或者,这是一个不期望的状态。
535                    // 为安全起见,如果还有约束,则继续等待。
536                    return StepResult::Continue;
537                }
538
539                // 所有参数都已收集完毕,并且所有约束(如果有的话)都已满足。
540                // 使用 `collected_arguments` 创建最终的参数元组。
541                let final_args_static = OnionTuple::new_static_no_ref(&self.collected_arguments);
542
543                // 获取原始 Lambda 定义对象。
544                unwrap_step_result!(self.lambda.weak().with_data(|obj| {
545                    if let OnionObject::Lambda(lambda_def) = obj {
546                        // 使用最终的参数元组和 Lambda 定义来创建实际的 Lambda 可运行实例。
547                        let runnable = lambda_def
548                            .create_runnable(final_args_static, &self.lambda, gc)
549                            .map_err(|e| {
550                                RuntimeError::InvalidType(
551                                    format!("Failed to create runnable from lambda: {}", e).into(),
552                                )
553                            })?;
554                        // 应用 mapper。
555                        // 成功映射,返回 ReplaceRunnable
556                        (self.runnable_mapper)(runnable)
557                            .map(|result_runnable| StepResult::ReplaceRunnable(result_runnable))
558                    } else {
559                        // 这是一个内部错误,启动器持有的 lambda 对象不是 Lambda 类型。
560                        Err(RuntimeError::DetailedError(
561                            "Launcher's lambda object is not OnionObject::Lambda"
562                                .to_string()
563                                .into(),
564                        ))
565                    }
566                }))
567            }
568        }
569    }
570
571    fn format_context(&self) -> Result<serde_json::Value, RuntimeError> {
572        // 此启动器不提供上下文格式化功能。
573        Err(RuntimeError::DetailedError(
574            "OnionLambdaRunnableLauncher does not support context formatting"
575                .to_string()
576                .into(),
577        ))
578    }
579}