onion_vm/types/lambda/
definition.rs

1use std::{
2    collections::VecDeque,
3    fmt::{Debug, Display},
4    sync::Arc,
5};
6
7use arc_gc::{
8    arc::{GCArc, GCArcWeak},
9    gc::GC,
10    traceable::GCTraceable,
11};
12
13use crate::{
14    lambda::runnable::{Runnable, RuntimeError},
15    types::{
16        lambda::{
17            parameter::LambdaParameter, vm_instructions::instruction_set::VMInstructionPackage,
18        },
19        object::{OnionObject, OnionObjectCell, OnionStaticObject},
20    },
21    utils::fastmap::{OnionFastMap, OnionKeyPool},
22};
23
24use super::runnable::OnionLambdaRunnable;
25
26pub enum LambdaBody {
27    Instruction(Arc<VMInstructionPackage>),
28    NativeFunction(
29        (
30            Arc<
31                dyn Fn(
32                        &OnionObject,                               // self_object
33                        &OnionFastMap<Box<str>, OnionStaticObject>, // argument
34                        &OnionFastMap<Box<str>, OnionObject>,       // captured_vars
35                        &mut GC<OnionObjectCell>,                   // gc
36                    ) -> Box<dyn Runnable>
37                    + Send
38                    + Sync,
39            >,
40            OnionKeyPool<Box<str>>,
41        ),
42    ),
43}
44
45impl Clone for LambdaBody {
46    fn clone(&self) -> Self {
47        match self {
48            LambdaBody::Instruction(instruction) => LambdaBody::Instruction(instruction.clone()),
49            LambdaBody::NativeFunction(native_function) => {
50                LambdaBody::NativeFunction(native_function.clone())
51            }
52        }
53    }
54}
55
56impl LambdaBody {
57    fn create_string_pool(&self) -> OnionKeyPool<Box<str>> {
58        match self {
59            LambdaBody::Instruction(instruction) => instruction.create_key_pool(),
60            LambdaBody::NativeFunction((_, key_pool)) => key_pool.clone(),
61        }
62    }
63}
64
65impl Debug for LambdaBody {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        match self {
68            LambdaBody::Instruction(_) => write!(f, "Instruction(...)"),
69            LambdaBody::NativeFunction(_) => write!(f, "NativeFunction"),
70        }
71    }
72}
73
74impl Display for LambdaBody {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        match self {
77            LambdaBody::Instruction(_) => write!(f, "Instruction(...)"),
78            LambdaBody::NativeFunction(_) => write!(f, "NativeFunction"),
79        }
80    }
81}
82
83pub enum LambdaType {
84    Normal,
85    AsyncLauncher,
86    SyncLauncher,
87}
88
89pub struct OnionLambdaDefinition {
90    parameter: LambdaParameter,
91    flatten_param_keys: Box<[Box<str>]>,
92    flatten_param_constraints: Box<[OnionObject]>,
93    body: LambdaBody,
94    capture: OnionFastMap<Box<str>, OnionObject>,
95    signature: Box<str>,
96    lambda_type: LambdaType,
97}
98
99impl OnionLambdaDefinition {
100    pub fn new_static(
101        parameter: LambdaParameter,
102        body: LambdaBody,
103        capture: OnionFastMap<Box<str>, OnionObject>,
104        signature: Box<str>,
105        lambda_type: LambdaType,
106    ) -> OnionStaticObject {
107        let flatten_param_keys = parameter.flatten_keys();
108        let flatten_param_constraints = parameter.flatten_constraints();
109        OnionObject::Lambda((
110            OnionLambdaDefinition {
111                parameter,
112                flatten_param_keys,
113                flatten_param_constraints,
114                body,
115                capture,
116                signature,
117                lambda_type,
118            }
119            .into(),
120            OnionObject::Undefined(None).into(),
121        ))
122        .consume_and_stabilize()
123    }
124
125    pub fn new_static_with_self(
126        parameter: LambdaParameter,
127        body: LambdaBody,
128        capture: OnionFastMap<Box<str>, OnionObject>,
129        self_object: &OnionObject,
130        signature: Box<str>,
131        lambda_type: LambdaType,
132    ) -> OnionStaticObject {
133        let flatten_param_keys = parameter.flatten_keys();
134        let flatten_param_constraints = parameter.flatten_constraints();
135        OnionObject::Lambda((
136            OnionLambdaDefinition {
137                parameter,
138                flatten_param_keys,
139                flatten_param_constraints,
140                body,
141                capture,
142                signature,
143                lambda_type,
144            }
145            .into(),
146            self_object.clone().into(),
147        ))
148        .consume_and_stabilize()
149    }
150
151    // 从定义创建可用字符串池,Lambda自身只能使用这个字符串池中的字符串
152    pub fn create_key_pool(&self) -> OnionKeyPool<Box<str>> {
153        self.body.create_string_pool()
154    }
155
156    pub fn with_lambda_type(&self, lambda_type: LambdaType) -> Self {
157        OnionLambdaDefinition {
158            parameter: self.parameter.clone(),
159            flatten_param_keys: self.flatten_param_keys.clone(),
160            flatten_param_constraints: self.flatten_param_constraints.clone(),
161            body: self.body.clone(),
162            capture: self.capture.clone(),
163            signature: self.signature.clone(),
164            lambda_type,
165        }
166    }
167
168    pub fn lambda_type(&self) -> &LambdaType {
169        &self.lambda_type
170    }
171
172    // 显然我们在Launcher里已经严格保证argument所使用的字符串池是Lambda定义的字符串池
173    pub fn create_runnable(
174        &self,
175        argument: &OnionFastMap<Box<str>, OnionStaticObject>,
176        this_lambda: &OnionStaticObject,
177        self_object: &OnionObject,
178        gc: &mut GC<OnionObjectCell>,
179    ) -> Result<Box<dyn Runnable>, RuntimeError> {
180        match &self.body {
181            LambdaBody::Instruction(instruction) => {
182                let runnable = OnionLambdaRunnable::new(
183                    argument,
184                    &self.capture,
185                    self_object,
186                    this_lambda,
187                    instruction.clone(),
188                    match instruction.get_table().get(self.signature.as_ref()) {
189                        Some(ip) => *ip as isize,
190                        None => {
191                            return Err(RuntimeError::InvalidOperation(
192                                format!(
193                                    "Signature '{}' not found in instruction package",
194                                    self.signature
195                                )
196                                .into(),
197                            ));
198                        }
199                    },
200                )?;
201                Ok(Box::new(runnable))
202            }
203            LambdaBody::NativeFunction((native_function, _)) => {
204                Ok(native_function(self_object, argument, &self.capture, gc))
205            }
206        }
207    }
208
209    pub fn get_signature(&self) -> &str {
210        &self.signature
211    }
212
213    pub fn get_parameter(&self) -> &LambdaParameter {
214        &self.parameter
215    }
216
217    pub fn get_flatten_param_keys(&self) -> &[Box<str>] {
218        &self.flatten_param_keys
219    }
220
221    pub fn get_flatten_param_constraints(&self) -> &[OnionObject] {
222        &self.flatten_param_constraints
223    }
224
225    pub fn get_capture(&self) -> &OnionFastMap<Box<str>, OnionObject> {
226        &self.capture
227    }
228
229    pub fn get_body(&self) -> &LambdaBody {
230        &self.body
231    }
232
233    pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
234        self.parameter.upgrade(collected);
235        for (_, obj) in self.capture.pairs() {
236            obj.upgrade(collected);
237        }
238    }
239
240    pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
241    where
242        F: Fn(&OnionObject) -> Result<R, RuntimeError>,
243    {
244        match key {
245            OnionObject::String(s) if s.as_ref() == "$parameter" => {
246                let parameter = self.parameter.to_onion();
247                f(parameter.weak())
248            }
249            OnionObject::String(s) if s.as_ref() == "$signature" => {
250                f(&OnionObject::String(Arc::from(self.signature.clone())))
251            }
252            OnionObject::String(s) => {
253                if let Some(value) = self.capture.get(s.as_ref()) {
254                    f(value)
255                } else {
256                    Err(RuntimeError::InvalidOperation(
257                        format!("Attribute '{:?}' not found in lambda definition", key).into(),
258                    ))
259                }
260            }
261            _ => Err(RuntimeError::InvalidOperation(
262                format!("Attribute '{:?}' not found in lambda definition", key).into(),
263            )),
264        }
265    }
266}
267
268impl GCTraceable<OnionObjectCell> for OnionLambdaDefinition {
269    fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
270        self.parameter.collect(queue);
271        for (_, obj) in self.capture.pairs() {
272            obj.collect(queue);
273        }
274    }
275}
276
277impl Debug for OnionLambdaDefinition {
278    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279        write!(
280            f,
281            "OnionLambdaDefinition {{ parameter: {:?}, body: {:?}, capture: {:?} }}",
282            self.parameter, self.body, self.capture
283        )
284    }
285}