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 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 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 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 drop(typechecker);
956
957 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 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 let mut signatures = compiler.take_function_signatures();
993 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}