Skip to main content

lust/embed/
program.rs

1use super::async_runtime::{
2    signal_pair, AsyncRegistry, AsyncTaskEntry, AsyncTaskQueue, AsyncTaskTarget, AsyncValueFuture,
3    PendingAsyncTask,
4};
5use super::conversions::{
6    FromLustArgs, FromLustValue, FunctionArgs, IntoLustValue, IntoTypedValue,
7};
8use super::native_types::ExternRegistry;
9use super::values::{EnumInstance, FunctionHandle, StructField, StructInstance, TypedValue};
10use crate::ast::{
11    EnumDef, FieldOwnership, FunctionDef, ImplBlock, Item, ItemKind, Span, StructDef, TraitDef,
12    Type, TypeKind,
13};
14use crate::bytecode::{Compiler, NativeCallResult, Value};
15use crate::modules::{ModuleImports, ModuleLoader};
16use crate::typechecker::{FunctionSignature, TypeChecker};
17use crate::vm::{NativeExport, NativeExportParam, VM};
18use crate::{LustConfig, LustError, Result};
19use hashbrown::HashMap;
20use std::cell::RefCell;
21use std::future::Future;
22use std::io;
23use std::path::{Path, PathBuf};
24use std::rc::Rc;
25use std::task::{Context, Poll};
26
27pub struct EmbeddedBuilder {
28    base_dir: PathBuf,
29    modules: HashMap<String, String>,
30    entry_module: Option<String>,
31    config: LustConfig,
32    extern_registry: ExternRegistry,
33}
34
35impl Default for EmbeddedBuilder {
36    fn default() -> Self {
37        Self {
38            base_dir: PathBuf::from("__embedded__"),
39            modules: HashMap::new(),
40            entry_module: None,
41            config: LustConfig::default(),
42            extern_registry: ExternRegistry::new(),
43        }
44    }
45}
46
47impl EmbeddedBuilder {
48    pub fn new() -> Self {
49        Self::default()
50    }
51
52    pub fn with_base_dir(self, base_dir: impl Into<PathBuf>) -> Self {
53        self.set_base_dir(base_dir)
54    }
55
56    pub fn set_base_dir(mut self, base_dir: impl Into<PathBuf>) -> Self {
57        self.base_dir = base_dir.into();
58        self
59    }
60
61    pub fn module(mut self, module_path: impl Into<String>, source: impl Into<String>) -> Self {
62        self.modules.insert(module_path.into(), source.into());
63        self
64    }
65
66    pub fn enable_stdlib_module<S: AsRef<str>>(mut self, module: S) -> Self {
67        self.config.enable_module(module);
68        self
69    }
70
71    pub fn add_stdlib_module<S: AsRef<str>>(mut self, module: S) -> Self {
72        self.config.enable_module(module);
73        self
74    }
75
76    pub fn declare_struct(mut self, def: StructDef) -> Self {
77        self.extern_registry.add_struct(def);
78        self
79    }
80
81    pub fn declare_enum(mut self, def: EnumDef) -> Self {
82        self.extern_registry.add_enum(def);
83        self
84    }
85
86    pub fn declare_trait(mut self, def: TraitDef) -> Self {
87        self.extern_registry.add_trait(def);
88        self
89    }
90
91    pub fn declare_impl(mut self, impl_block: ImplBlock) -> Self {
92        self.extern_registry.add_impl(impl_block);
93        self
94    }
95
96    pub fn declare_function(mut self, func: FunctionDef) -> Self {
97        self.extern_registry.add_function(func);
98        self
99    }
100
101    pub fn with_extern_registry(mut self, registry: ExternRegistry) -> Self {
102        self.extern_registry = registry;
103        self
104    }
105
106    pub fn extern_registry_mut(&mut self) -> &mut ExternRegistry {
107        &mut self.extern_registry
108    }
109
110    pub fn with_config(mut self, config: LustConfig) -> Self {
111        self.config = config;
112        self
113    }
114
115    pub fn set_config(mut self, config: LustConfig) -> Self {
116        self.config = config;
117        self
118    }
119
120    pub fn add_module(
121        &mut self,
122        module_path: impl Into<String>,
123        source: impl Into<String>,
124    ) -> &mut Self {
125        self.modules.insert(module_path.into(), source.into());
126        self
127    }
128
129    pub fn entry_module(mut self, module_path: impl Into<String>) -> Self {
130        self.set_entry_module(module_path);
131        self
132    }
133
134    pub fn set_entry_module(&mut self, module_path: impl Into<String>) -> &mut Self {
135        self.entry_module = Some(module_path.into());
136        self
137    }
138
139    pub fn compile(self) -> Result<EmbeddedProgram> {
140        let entry_module = self
141            .entry_module
142            .ok_or_else(|| LustError::Unknown("No entry module configured for embedding".into()))?;
143        let has_entry = self.modules.contains_key(&entry_module);
144        if !has_entry {
145            return Err(LustError::Unknown(format!(
146                "Entry module '{}' was not provided via EmbeddedBuilder::module",
147                entry_module
148            )));
149        }
150
151        let overrides: HashMap<PathBuf, String> = self
152            .modules
153            .into_iter()
154            .map(|(module, source)| (module_path_to_file(&self.base_dir, &module), source))
155            .collect();
156        compile_in_memory(
157            self.base_dir,
158            entry_module,
159            overrides,
160            self.config,
161            self.extern_registry,
162        )
163    }
164}
165
166pub struct EmbeddedProgram {
167    vm: VM,
168    signatures: HashMap<String, FunctionSignature>,
169    struct_defs: HashMap<String, StructDef>,
170    enum_defs: HashMap<String, EnumDef>,
171    entry_script: Option<String>,
172    entry_module: String,
173    async_registry: Rc<RefCell<AsyncRegistry>>,
174}
175
176pub struct AsyncDriver<'a> {
177    program: &'a mut EmbeddedProgram,
178}
179
180impl<'a> AsyncDriver<'a> {
181    pub fn new(program: &'a mut EmbeddedProgram) -> Self {
182        Self { program }
183    }
184
185    pub fn poll(&mut self) -> Result<()> {
186        self.program.poll_async_tasks()
187    }
188
189    pub fn pump_until_idle(&mut self) -> Result<()> {
190        while self.program.has_pending_async_tasks() {
191            self.program.poll_async_tasks()?;
192        }
193        Ok(())
194    }
195
196    pub fn has_pending(&self) -> bool {
197        self.program.has_pending_async_tasks()
198    }
199}
200
201impl EmbeddedProgram {
202    pub fn builder() -> EmbeddedBuilder {
203        EmbeddedBuilder::default()
204    }
205
206    pub fn vm_mut(&mut self) -> &mut VM {
207        &mut self.vm
208    }
209
210    pub fn set_gas_budget(&mut self, limit: u64) {
211        self.vm.set_gas_budget(limit);
212    }
213
214    pub fn clear_gas_budget(&mut self) {
215        self.vm.clear_gas_budget();
216    }
217
218    pub fn reset_gas_counter(&mut self) {
219        self.vm.reset_gas_counter();
220    }
221
222    pub fn gas_used(&self) -> u64 {
223        self.vm.gas_used()
224    }
225
226    pub fn gas_remaining(&self) -> Option<u64> {
227        self.vm.gas_remaining()
228    }
229
230    pub fn set_memory_budget_bytes(&mut self, limit_bytes: usize) {
231        self.vm.set_memory_budget_bytes(limit_bytes);
232    }
233
234    pub fn set_memory_budget_kb(&mut self, limit_kb: u64) {
235        self.vm.set_memory_budget_kb(limit_kb);
236    }
237
238    pub fn clear_memory_budget(&mut self) {
239        self.vm.clear_memory_budget();
240    }
241
242    pub fn reset_memory_counter(&mut self) {
243        self.vm.reset_memory_counter();
244    }
245
246    pub fn memory_used_bytes(&self) -> usize {
247        self.vm.memory_used_bytes()
248    }
249
250    pub fn memory_remaining_bytes(&self) -> Option<usize> {
251        self.vm.memory_remaining_bytes()
252    }
253
254    pub fn dump_externs_to_dir(&self, output_root: impl AsRef<Path>) -> io::Result<Vec<PathBuf>> {
255        self.vm.dump_externs_to_dir(output_root)
256    }
257
258    pub(crate) fn vm(&self) -> &VM {
259        &self.vm
260    }
261
262    pub fn global_names(&self) -> Vec<String> {
263        self.vm.global_names()
264    }
265
266    pub fn globals(&self) -> Vec<(String, Value)> {
267        self.vm.globals_snapshot()
268    }
269
270    pub fn signature(&self, function_name: &str) -> Option<&FunctionSignature> {
271        self.find_signature(function_name).map(|(_, sig)| sig)
272    }
273
274    pub fn typed_functions(&self) -> impl Iterator<Item = (&String, &FunctionSignature)> {
275        self.signatures.iter()
276    }
277
278    pub fn struct_definition(&self, type_name: &str) -> Option<&StructDef> {
279        self.struct_defs.get(type_name)
280    }
281
282    pub fn enum_definition(&self, type_name: &str) -> Option<&EnumDef> {
283        self.enum_defs.get(type_name)
284    }
285
286    fn find_signature(&self, name: &str) -> Option<(String, &FunctionSignature)> {
287        if let Some(sig) = self.signatures.get(name) {
288            return Some((name.to_string(), sig));
289        }
290
291        for candidate in self.signature_lookup_candidates(name) {
292            if let Some(sig) = self.signatures.get(&candidate) {
293                return Some((candidate, sig));
294            }
295        }
296
297        let matches = self
298            .signatures
299            .iter()
300            .filter_map(|(key, sig)| {
301                if Self::simple_name(key) == name {
302                    Some((key, sig))
303                } else {
304                    None
305                }
306            })
307            .collect::<Vec<_>>();
308        if matches.len() == 1 {
309            let (key, sig) = matches[0];
310            return Some((key.clone(), sig));
311        }
312
313        None
314    }
315
316    fn resolve_signature(&self, name: &str) -> Result<(String, &FunctionSignature)> {
317        if let Some(found) = self.find_signature(name) {
318            return Ok(found);
319        }
320
321        let matches = self
322            .signatures
323            .keys()
324            .filter(|key| Self::simple_name(key) == name)
325            .count();
326        if matches > 1 {
327            return Err(LustError::TypeError {
328                message: format!(
329                    "Cannot register native '{}': multiple matching functions found; specify a fully qualified name",
330                    name
331                ),
332            });
333        }
334
335        Err(LustError::TypeError {
336            message: format!(
337                "Cannot register native '{}': function not declared in Lust source",
338                name
339            ),
340        })
341    }
342
343    fn signature_lookup_candidates(&self, name: &str) -> Vec<String> {
344        let mut candidates: Vec<String> = Vec::new();
345        if name.contains("::") {
346            candidates.push(name.replace("::", "."));
347        }
348
349        if name.contains('.') {
350            candidates.push(name.replace('.', "::"));
351        }
352
353        if !name.contains('.') && !name.contains("::") {
354            let module = &self.entry_module;
355            candidates.push(format!("{}.{}", module, name));
356            candidates.push(format!("{}::{}", module, name));
357        }
358
359        candidates
360    }
361
362    fn simple_name(name: &str) -> &str {
363        name.rsplit(|c| c == '.' || c == ':').next().unwrap_or(name)
364    }
365
366    fn register_native_with_aliases<F>(&mut self, requested_name: &str, canonical: String, func: F)
367    where
368        F: Fn(&[Value]) -> std::result::Result<NativeCallResult, String> + 'static,
369    {
370        let native_fn: Rc<dyn Fn(&[Value]) -> std::result::Result<NativeCallResult, String>> =
371            Rc::new(func);
372        let value = Value::NativeFunction(native_fn);
373        let mut aliases: Vec<String> = Vec::new();
374        aliases.push(canonical.clone());
375        let canonical_normalized = normalize_global_name(&canonical);
376        if canonical_normalized != canonical {
377            aliases.push(canonical_normalized);
378        }
379
380        if requested_name != canonical {
381            aliases.push(requested_name.to_string());
382            let normalized = normalize_global_name(requested_name);
383            if normalized != requested_name {
384                aliases.push(normalized);
385            }
386        }
387
388        aliases.sort();
389        aliases.dedup();
390        #[cfg(debug_assertions)]
391        eprintln!(
392            "register_native_with_aliases requested='{}' canonical='{}' aliases={:?}",
393            requested_name, canonical, aliases
394        );
395        for key in aliases {
396            self.vm.register_native(key, value.clone());
397        }
398    }
399
400    pub fn get_global_value(&self, name: &str) -> Option<Value> {
401        let normalized = normalize_global_name(name);
402        self.vm.get_global(&normalized)
403    }
404
405    pub fn get_typed_global<T: FromLustValue>(&self, name: &str) -> Result<Option<T>> {
406        let normalized = normalize_global_name(name);
407        match self.vm.get_global(&normalized) {
408            Some(value) => T::from_value(value).map(Some),
409            None => Ok(None),
410        }
411    }
412
413    pub fn set_global_value<V: IntoTypedValue>(&mut self, name: impl Into<String>, value: V) {
414        let name_string = name.into();
415        let normalized = normalize_global_name(&name_string);
416        let value = value.into_typed_value().into_value();
417        self.vm.set_global(normalized, value);
418    }
419
420    pub fn struct_instance<I>(
421        &self,
422        type_name: impl Into<String>,
423        fields: I,
424    ) -> Result<StructInstance>
425    where
426        I: IntoIterator,
427        I::Item: Into<StructField>,
428    {
429        let type_name = type_name.into();
430        let def = self
431            .struct_defs
432            .get(&type_name)
433            .ok_or_else(|| LustError::TypeError {
434                message: format!("Unknown struct '{}'", type_name),
435            })?;
436        let mut provided: HashMap<String, TypedValue> = fields
437            .into_iter()
438            .map(|field| {
439                let field: StructField = field.into();
440                field.into_parts()
441            })
442            .collect();
443        let mut ordered_fields: Vec<(Rc<String>, Value)> = Vec::with_capacity(def.fields.len());
444        for field in &def.fields {
445            let typed_value = provided
446                .remove(&field.name)
447                .ok_or_else(|| LustError::TypeError {
448                    message: format!(
449                        "Struct '{}' is missing required field '{}'",
450                        type_name, field.name
451                    ),
452                })?;
453
454            let matches_ref_inner = matches!(field.ownership, FieldOwnership::Weak)
455                && matches!(typed_value.as_value(), Value::Struct { .. });
456            if !typed_value.matches(&field.ty) && !matches_ref_inner {
457                return Err(LustError::TypeError {
458                    message: format!(
459                        "Struct field '{}' expects Rust value of type '{}' but received '{}'",
460                        field.name,
461                        field.ty,
462                        typed_value.description()
463                    ),
464                });
465            }
466
467            ordered_fields.push((Rc::new(field.name.clone()), typed_value.into_value()));
468        }
469
470        if !provided.is_empty() {
471            let mut unexpected: Vec<String> = provided.into_keys().collect();
472            unexpected.sort();
473            return Err(LustError::TypeError {
474                message: format!(
475                    "Struct '{}' received unexpected field(s): {}",
476                    type_name,
477                    unexpected.join(", ")
478                ),
479            });
480        }
481
482        let value = self.vm.instantiate_struct(&type_name, ordered_fields)?;
483        Ok(StructInstance::new(type_name.clone(), value))
484    }
485
486    pub fn enum_variant(
487        &self,
488        type_name: impl Into<String>,
489        variant: impl Into<String>,
490    ) -> Result<EnumInstance> {
491        self.enum_variant_with(type_name, variant, std::iter::empty::<Value>())
492    }
493
494    pub fn enum_variant_with<I, V>(
495        &self,
496        type_name: impl Into<String>,
497        variant: impl Into<String>,
498        payload: I,
499    ) -> Result<EnumInstance>
500    where
501        I: IntoIterator<Item = V>,
502        V: IntoTypedValue,
503    {
504        let type_name = type_name.into();
505        let variant_name = variant.into();
506        let def = self
507            .enum_defs
508            .get(&type_name)
509            .ok_or_else(|| LustError::TypeError {
510                message: format!("Unknown enum '{}'", type_name),
511            })?;
512        let enum_variant = def
513            .variants
514            .iter()
515            .find(|v| v.name == variant_name)
516            .ok_or_else(|| LustError::TypeError {
517                message: format!(
518                    "Enum '{}' has no variant named '{}'",
519                    type_name, variant_name
520                ),
521            })?;
522        let mut values: Vec<TypedValue> =
523            payload.into_iter().map(|v| v.into_typed_value()).collect();
524        let coerced_values: Option<Rc<Vec<Value>>> = match &enum_variant.fields {
525            None => {
526                if !values.is_empty() {
527                    return Err(LustError::TypeError {
528                        message: format!(
529                            "Enum variant '{}.{}' does not accept payload values",
530                            type_name, variant_name
531                        ),
532                    });
533                }
534
535                None
536            }
537
538            Some(field_types) => {
539                if values.len() != field_types.len() {
540                    return Err(LustError::TypeError {
541                        message: format!(
542                            "Enum variant '{}.{}' expects {} value(s) but {} were supplied",
543                            type_name,
544                            variant_name,
545                            field_types.len(),
546                            values.len()
547                        ),
548                    });
549                }
550
551                let mut collected = Vec::with_capacity(field_types.len());
552                for (idx, (typed_value, field_ty)) in
553                    values.drain(..).zip(field_types.iter()).enumerate()
554                {
555                    if !typed_value.matches(field_ty) {
556                        return Err(LustError::TypeError {
557                            message: format!(
558                                "Enum variant '{}.{}' field {} expects Lust type '{}' but Rust provided '{}'",
559                                type_name,
560                                variant_name,
561                                idx + 1,
562                                field_ty,
563                                typed_value.description()
564                            ),
565                        });
566                    }
567
568                    collected.push(typed_value.into_value());
569                }
570
571                Some(Rc::new(collected))
572            }
573        };
574        Ok(EnumInstance::new(
575            type_name.clone(),
576            variant_name.clone(),
577            Value::Enum {
578                enum_name: type_name,
579                variant: variant_name,
580                values: coerced_values,
581            },
582        ))
583    }
584
585    pub fn register_native_fn<F>(&mut self, name: impl Into<String>, func: F)
586    where
587        F: Fn(&[Value]) -> std::result::Result<NativeCallResult, String> + 'static,
588    {
589        let native = Value::NativeFunction(Rc::new(func));
590        self.vm.register_native(name, native);
591    }
592
593    pub fn register_async_native<F, Fut>(&mut self, name: impl Into<String>, func: F) -> Result<()>
594    where
595        F: Fn(Vec<Value>) -> Fut + 'static,
596        Fut: Future<Output = std::result::Result<Value, String>> + 'static,
597    {
598        let registry = self.async_registry.clone();
599        let name_string = name.into();
600        let handler = move |values: &[Value]| -> std::result::Result<NativeCallResult, String> {
601            let args: Vec<Value> = values.iter().cloned().collect();
602            let future: AsyncValueFuture = Box::pin(func(args));
603            VM::with_current(|vm| {
604                let handle = vm
605                    .current_task_handle()
606                    .ok_or_else(|| "Async native functions require a running task".to_string())?;
607                let mut registry = registry.borrow_mut();
608                if registry.has_pending_for(handle) {
609                    return Err(format!(
610                        "Task {} already has a pending async native call",
611                        handle.id()
612                    ));
613                }
614
615                registry.register(AsyncTaskEntry::new(
616                    AsyncTaskTarget::ScriptTask(handle),
617                    future,
618                ));
619                Ok(NativeCallResult::Yield(Value::Nil))
620            })
621        };
622        self.register_native_fn(name_string, handler);
623        Ok(())
624    }
625
626    pub fn register_typed_native<Args, R, F>(&mut self, name: &str, func: F) -> Result<()>
627    where
628        Args: FromLustArgs,
629        R: IntoLustValue + FromLustValue,
630        F: Fn(Args) -> std::result::Result<R, String> + 'static,
631    {
632        let (canonical, signature) = self.resolve_signature(name)?;
633        if !Args::matches_signature(&signature.params) {
634            return Err(LustError::TypeError {
635                message: format!(
636                    "Native '{}' argument types do not match Lust signature",
637                    name
638                ),
639            });
640        }
641
642        ensure_return_type::<R>(name, &signature.return_type)?;
643        self.vm
644            .record_exported_native(native_export_from_signature(&canonical, &signature));
645        let handler = move |values: &[Value]| -> std::result::Result<NativeCallResult, String> {
646            let args = Args::from_values(values)?;
647            let result = func(args)?;
648            Ok(NativeCallResult::Return(result.into_value()))
649        };
650        self.register_native_with_aliases(name, canonical, handler);
651        Ok(())
652    }
653
654    pub fn register_async_typed_native<Args, R, F, Fut>(
655        &mut self,
656        name: &str,
657        func: F,
658    ) -> Result<()>
659    where
660        Args: FromLustArgs,
661        R: IntoLustValue + FromLustValue,
662        F: Fn(Args) -> Fut + 'static,
663        Fut: Future<Output = std::result::Result<R, String>> + 'static,
664    {
665        let (canonical, signature) = self.resolve_signature(name)?;
666        let signature = signature.clone();
667        self.vm
668            .record_exported_native(native_export_from_signature(&canonical, &signature));
669        if !Args::matches_signature(&signature.params) {
670            return Err(LustError::TypeError {
671                message: format!(
672                    "Native '{}' argument types do not match Lust signature",
673                    name
674                ),
675            });
676        }
677
678        let registry = self.async_registry.clone();
679        let handler = move |values: &[Value]| -> std::result::Result<NativeCallResult, String> {
680            let args = Args::from_values(values)?;
681            let future = func(args);
682            let mapped = async move {
683                match future.await {
684                    Ok(result) => Ok(result.into_value()),
685                    Err(err) => Err(err),
686                }
687            };
688            let future: AsyncValueFuture = Box::pin(mapped);
689            VM::with_current(|vm| {
690                let handle = vm
691                    .current_task_handle()
692                    .ok_or_else(|| "Async native functions require a running task".to_string())?;
693                let mut registry = registry.borrow_mut();
694                if registry.has_pending_for(handle) {
695                    return Err(format!(
696                        "Task {} already has a pending async native call",
697                        handle.id()
698                    ));
699                }
700
701                registry.register(AsyncTaskEntry::new(
702                    AsyncTaskTarget::ScriptTask(handle),
703                    future,
704                ));
705                Ok(NativeCallResult::Yield(Value::Nil))
706            })
707        };
708        self.register_native_with_aliases(name, canonical, handler);
709        Ok(())
710    }
711
712    pub fn register_async_task_native<Args, R, F, Fut>(&mut self, name: &str, func: F) -> Result<()>
713    where
714        Args: FromLustArgs,
715        R: IntoLustValue + FromLustValue,
716        F: Fn(Args) -> Fut + 'static,
717        Fut: Future<Output = std::result::Result<R, String>> + 'static,
718    {
719        let (canonical, signature) = self.resolve_signature(name)?;
720        let signature = signature.clone();
721        self.vm
722            .record_exported_native(native_export_from_signature(&canonical, &signature));
723        if !Args::matches_signature(&signature.params) {
724            return Err(LustError::TypeError {
725                message: format!(
726                    "Native '{}' argument types do not match Lust signature",
727                    name
728                ),
729            });
730        }
731
732        let registry = self.async_registry.clone();
733        let handler = move |values: &[Value]| -> std::result::Result<NativeCallResult, String> {
734            let args = Args::from_values(values)?;
735            let future = func(args);
736            let mapped = async move {
737                match future.await {
738                    Ok(result) => Ok(result.into_value()),
739                    Err(err) => Err(err),
740                }
741            };
742            let future: AsyncValueFuture = Box::pin(mapped);
743            VM::with_current(|vm| {
744                let mut registry = registry.borrow_mut();
745                let handle = vm.create_native_future_task();
746                let entry = AsyncTaskEntry::new(AsyncTaskTarget::NativeTask(handle), future);
747                registry.register(entry);
748                Ok(NativeCallResult::Return(Value::task(handle)))
749            })
750        };
751        self.register_native_with_aliases(name, canonical, handler);
752        Ok(())
753    }
754
755    pub fn register_async_task_queue<Args, R>(
756        &mut self,
757        name: &str,
758        queue: AsyncTaskQueue<Args, R>,
759    ) -> Result<()>
760    where
761        Args: FromLustArgs + 'static,
762        R: IntoLustValue + FromLustValue + 'static,
763    {
764        let (canonical, signature) = self.resolve_signature(name)?;
765        let signature = signature.clone();
766        self.vm
767            .record_exported_native(native_export_from_signature(&canonical, &signature));
768        if !Args::matches_signature(&signature.params) {
769            return Err(LustError::TypeError {
770                message: format!(
771                    "Native '{}' argument types do not match Lust signature",
772                    name
773                ),
774            });
775        }
776
777        let registry = self.async_registry.clone();
778        let queue_clone = queue.clone();
779        let handler = move |values: &[Value]| -> std::result::Result<NativeCallResult, String> {
780            let args = Args::from_values(values)?;
781            let (completer, signal_future) = signal_pair::<R>();
782            let future: AsyncValueFuture = Box::pin(async move {
783                match signal_future.await {
784                    Ok(result) => Ok(result.into_value()),
785                    Err(err) => Err(err),
786                }
787            });
788
789            VM::with_current(|vm| {
790                let mut registry = registry.borrow_mut();
791                let handle = vm.create_native_future_task();
792                let entry = AsyncTaskEntry::new(AsyncTaskTarget::NativeTask(handle), future);
793                registry.register(entry);
794                queue_clone.push(PendingAsyncTask::new(handle, args, completer));
795                Ok(NativeCallResult::Return(Value::task(handle)))
796            })
797        };
798        self.register_native_with_aliases(name, canonical, handler);
799        Ok(())
800    }
801
802    pub fn call_typed<Args, R>(&mut self, function_name: &str, args: Args) -> Result<R>
803    where
804        Args: FunctionArgs,
805        R: FromLustValue,
806    {
807        let signature = self
808            .signatures
809            .get(function_name)
810            .ok_or_else(|| LustError::TypeError {
811                message: format!(
812                    "No type information available for function '{}'; \
813                     use call_raw if the function is dynamically typed",
814                    function_name
815                ),
816            })?;
817        Args::validate_signature(function_name, &signature.params)?;
818        ensure_return_type::<R>(function_name, &signature.return_type)?;
819        let values = args.into_values();
820        let value = self.vm.call(function_name, values)?;
821        R::from_value(value)
822    }
823
824    pub fn call_raw(&mut self, function_name: &str, args: Vec<Value>) -> Result<Value> {
825        self.vm.call(function_name, args)
826    }
827
828    pub fn function_handle(&self, function_name: &str) -> Result<FunctionHandle> {
829        let mut candidates = Vec::new();
830        candidates.push(function_name.to_string());
831        candidates.extend(self.signature_lookup_candidates(function_name));
832        for name in candidates {
833            if let Some(value) = self.vm.function_value(&name) {
834                return FunctionHandle::from_value(value);
835            }
836        }
837        Err(LustError::RuntimeError {
838            message: format!("Function '{}' not found in embedded program", function_name),
839        })
840    }
841
842    pub fn run_entry_script(&mut self) -> Result<()> {
843        let Some(entry) = &self.entry_script else {
844            return Err(LustError::RuntimeError {
845                message: "Embedded program has no entry script".into(),
846            });
847        };
848        let result = self.vm.call(entry, Vec::new())?;
849        match result {
850            Value::Nil => Ok(()),
851            other => Err(LustError::RuntimeError {
852                message: format!(
853                    "Entry script '{}' returned non-unit value: {:?}",
854                    entry, other
855                ),
856            }),
857        }
858    }
859
860    pub fn poll_async_tasks(&mut self) -> Result<()> {
861        let pending_ids: Vec<u64> = {
862            let registry = self.async_registry.borrow();
863            registry.pending.keys().copied().collect()
864        };
865
866        for id in pending_ids {
867            let mut completion: Option<(AsyncTaskTarget, std::result::Result<Value, String>)> =
868                None;
869            {
870                let mut registry = self.async_registry.borrow_mut();
871                let entry = match registry.pending.get_mut(&id) {
872                    Some(entry) => entry,
873                    None => continue,
874                };
875
876                if !entry.take_should_poll() {
877                    continue;
878                }
879
880                let waker = entry.make_waker();
881                let mut cx = Context::from_waker(&waker);
882                if let Poll::Ready(result) = entry.future.as_mut().poll(&mut cx) {
883                    completion = Some((entry.target, result));
884                }
885            }
886
887            if let Some((target, outcome)) = completion {
888                self.async_registry.borrow_mut().pending.remove(&id);
889                match target {
890                    AsyncTaskTarget::ScriptTask(handle) => match outcome {
891                        Ok(value) => {
892                            self.vm.resume_task_handle(handle, Some(value))?;
893                        }
894
895                        Err(message) => {
896                            self.vm
897                                .fail_task_handle(handle, LustError::RuntimeError { message })?;
898                        }
899                    },
900
901                    AsyncTaskTarget::NativeTask(handle) => {
902                        self.vm.complete_native_future_task(handle, outcome)?;
903                    }
904                }
905            }
906        }
907
908        Ok(())
909    }
910
911    pub fn has_pending_async_tasks(&self) -> bool {
912        !self.async_registry.borrow().is_empty()
913    }
914}
915
916fn compile_in_memory(
917    base_dir: PathBuf,
918    entry_module: String,
919    overrides: HashMap<PathBuf, String>,
920    config: LustConfig,
921    extern_registry: ExternRegistry,
922) -> Result<EmbeddedProgram> {
923    let mut loader = ModuleLoader::new(base_dir.clone());
924    loader.set_source_overrides(overrides);
925    let entry_path = module_path_to_file(&base_dir, &entry_module);
926    let entry_path_str = entry_path
927        .to_str()
928        .ok_or_else(|| LustError::Unknown("Entry path contained invalid UTF-8".into()))?
929        .to_string();
930    let program = loader.load_program_from_entry(&entry_path_str)?;
931
932    // Build imports map (small: just alias/path strings, not AST)
933    let mut imports_map: HashMap<String, ModuleImports> = HashMap::new();
934    for module in &program.modules {
935        imports_map.insert(module.path.clone(), module.imports.clone());
936    }
937
938    // Phase 1: type check while program is still alive (borrows module items)
939    let mut typechecker = TypeChecker::with_config(&config);
940    typechecker.set_imports_by_module(imports_map.clone());
941    extern_registry.register_with_typechecker(&mut typechecker)?;
942    typechecker.check_program(&program.modules)?;
943    let option_coercions = typechecker.take_option_coercions();
944    // Use take_ to move data out instead of cloning
945    let mut struct_defs = typechecker.take_struct_definitions();
946    for def in extern_registry.structs() {
947        struct_defs.insert(def.name.clone(), def.clone());
948    }
949    let mut enum_defs = typechecker.take_enum_definitions();
950    for def in extern_registry.enums() {
951        enum_defs.insert(def.name.clone(), def.clone());
952    }
953    let signatures = typechecker.take_function_signatures();
954    // Typechecker no longer needed — free its memory (expr_types, variable_types, scopes, etc.)
955    drop(typechecker);
956
957    // Phase 2: consume program.modules to build wrapped_items without cloning AST items
958    let program_entry_module = program.entry_module;
959    let mut init_funcs: Vec<(String, String)> = Vec::new();
960    let mut wrapped_items: Vec<Item> = Vec::new();
961    for module in program.modules {
962        if module.path != program_entry_module {
963            if let Some(ref init) = module.init_function {
964                let init_name = module
965                    .imports
966                    .function_aliases
967                    .get(init)
968                    .cloned()
969                    .unwrap_or_else(|| init.clone());
970                init_funcs.push((module.path.clone(), init_name));
971            }
972        }
973        wrapped_items.push(Item::new(
974            ItemKind::Module {
975                name: module.path,
976                items: module.items,
977            },
978            Span::new(0, 0, 0, 0),
979        ));
980    }
981
982    // Phase 3: compile — move signatures into compiler to avoid a second copy
983    let mut compiler = Compiler::new();
984    compiler.set_option_coercions(option_coercions);
985    compiler.configure_stdlib(&config);
986    compiler.set_imports_by_module(imports_map);
987    compiler.set_entry_module(program_entry_module.clone());
988    compiler.set_function_signatures(signatures);
989    let functions = compiler.compile_module(&wrapped_items)?;
990    let trait_impls = compiler.get_trait_impls().to_vec();
991    // Recover signatures from compiler (avoids keeping two copies alive simultaneously)
992    let mut signatures = compiler.take_function_signatures();
993    // AST no longer needed — free it before setting up the VM
994    drop(wrapped_items);
995
996    let entry_script = functions
997        .iter()
998        .find(|f| f.name == "__script")
999        .map(|f| f.name.clone());
1000    if let Some(script_name) = &entry_script {
1001        signatures
1002            .entry(script_name.clone())
1003            .or_insert_with(|| FunctionSignature {
1004                params: Vec::new(),
1005                return_type: Type::new(TypeKind::Unit, Span::new(0, 0, 0, 0)),
1006                is_method: false,
1007            });
1008    }
1009
1010    let mut vm = VM::with_config(&config);
1011    vm.load_functions(functions);
1012    vm.register_structs(&struct_defs);
1013    extern_registry.register_type_stubs(&mut vm);
1014    for (type_name, trait_name) in trait_impls {
1015        vm.register_trait_impl(type_name, trait_name);
1016    }
1017
1018    for (module_path, init) in init_funcs {
1019        let value = vm.call(&init, Vec::new())?;
1020        vm.set_global(module_path, value);
1021    }
1022
1023    Ok(EmbeddedProgram {
1024        vm,
1025        signatures,
1026        struct_defs,
1027        enum_defs,
1028        entry_script,
1029        entry_module: program_entry_module,
1030        async_registry: Rc::new(RefCell::new(AsyncRegistry::new())),
1031    })
1032}
1033
1034fn module_path_to_file(base_dir: &Path, module_path: &str) -> PathBuf {
1035    let mut path = base_dir.to_path_buf();
1036    for segment in module_path.split('.') {
1037        path.push(segment);
1038    }
1039
1040    path.set_extension("lust");
1041    path
1042}
1043
1044pub(crate) fn normalize_global_name(name: &str) -> String {
1045    if name.contains("::") {
1046        name.to_string()
1047    } else if let Some((module, identifier)) = name.rsplit_once('.') {
1048        format!("{}::{}", module, identifier)
1049    } else {
1050        name.to_string()
1051    }
1052}
1053
1054pub(crate) fn ensure_return_type<R: FromLustValue>(function_name: &str, ty: &Type) -> Result<()> {
1055    if matches!(ty.kind, TypeKind::Unknown) || R::matches_lust_type(ty) {
1056        return Ok(());
1057    }
1058
1059    Err(LustError::TypeError {
1060        message: format!(
1061            "Function '{}' reports return type '{}' which is incompatible with Rust receiver '{}'",
1062            function_name,
1063            ty,
1064            R::type_description()
1065        ),
1066    })
1067}
1068
1069fn normalize_extern_type_string(mut ty: String) -> String {
1070    if ty.starts_with("function(") && ty.ends_with(": ()") {
1071        ty.truncate(ty.len().saturating_sub(4));
1072    }
1073    ty
1074}
1075
1076fn native_export_from_signature(canonical_name: &str, signature: &FunctionSignature) -> NativeExport {
1077    let name = canonical_name.replace("::", ".");
1078    let params = signature
1079        .params
1080        .iter()
1081        .enumerate()
1082        .map(|(idx, ty)| {
1083            NativeExportParam::new(
1084                format!("arg{idx}"),
1085                normalize_extern_type_string(ty.to_string()),
1086            )
1087        })
1088        .collect::<Vec<_>>();
1089    let return_type = normalize_extern_type_string(signature.return_type.to_string());
1090    NativeExport::new(name, params, return_type)
1091}