rune/runtime/function.rs
1use core::fmt;
2use core::future::Future;
3
4use crate as rune;
5use crate::alloc::fmt::TryWrite;
6use crate::alloc::prelude::*;
7use crate::alloc::{self, Box, Vec};
8use crate::function;
9use crate::runtime;
10use crate::runtime::vm::Isolated;
11use crate::shared::AssertSend;
12use crate::sync::Arc;
13use crate::{Any, Hash};
14
15use super::{
16 Address, AnySequence, Args, Call, ConstValueBuf, Dismantle, Formatter, FromValue,
17 FunctionHandler, Globals, GuardedArgs, Handover, Output, OwnedTuple, Rtti, RuntimeContext,
18 RuntimeError, Stack, Unit, Value, Vm, VmCall, VmError, VmErrorKind, VmHalt,
19};
20
21/// The type of a function in Rune.
22///
23/// Functions can be called using call expression syntax, such as `<expr>()`.
24///
25/// There are multiple different kind of things which can be coerced into a
26/// function in Rune:
27/// * Regular functions.
28/// * Closures (which might or might not capture their environment).
29/// * Built-in constructors for tuple types (tuple structs, tuple variants).
30///
31/// # Examples
32///
33/// ```rune
34/// // Captures the constructor for the `Some(<value>)` tuple variant.
35/// let build_some = Some;
36/// assert_eq!(build_some(42), Some(42));
37///
38/// fn build(value) {
39/// Some(value)
40/// }
41///
42/// // Captures the function previously defined.
43/// let build_some = build;
44/// assert_eq!(build_some(42), Some(42));
45/// ```
46#[derive(Any, TryClone)]
47#[repr(transparent)]
48#[rune(item = ::std::ops, dismantle)]
49pub struct Function(FunctionImpl<Value>);
50
51impl Function {
52 /// Construct a [Function] from a Rust closure.
53 ///
54 /// # Examples
55 ///
56 /// ```
57 /// use rune::{Hash, Vm};
58 /// use rune::runtime::Function;
59 /// use rune::sync::Arc;
60 ///
61 /// let mut sources = rune::sources! {
62 /// entry => {
63 /// pub fn main(function) {
64 /// function(41)
65 /// }
66 /// }
67 /// };
68 ///
69 /// let unit = rune::prepare(&mut sources).build()?;
70 /// let unit = Arc::try_new(unit)?;
71 /// let mut vm = Vm::without_runtime(unit)?;
72 ///
73 /// let function = Function::new(|value: u32| value + 1)?;
74 ///
75 /// assert_eq!(function.type_hash(), Hash::EMPTY);
76 ///
77 /// let value = vm.call(["main"], (function,))?;
78 /// let value: u32 = rune::from_value(value)?;
79 /// assert_eq!(value, 42);
80 /// # Ok::<_, rune::support::Error>(())
81 /// ```
82 ///
83 /// Asynchronous functions:
84 ///
85 /// ```
86 /// use rune::{Hash, Vm};
87 /// use rune::runtime::Function;
88 /// use rune::sync::Arc;
89 ///
90 /// # futures_executor::block_on(async move {
91 /// let mut sources = rune::sources! {
92 /// entry => {
93 /// pub async fn main(function) {
94 /// function(41).await
95 /// }
96 /// }
97 /// };
98 ///
99 /// let unit = rune::prepare(&mut sources).build()?;
100 /// let unit = Arc::try_new(unit)?;
101 /// let mut vm = Vm::without_runtime(unit)?;
102 ///
103 /// let function = Function::new(|value: u32| async move { value + 1 })?;
104 ///
105 /// assert_eq!(function.type_hash(), Hash::EMPTY);
106 ///
107 /// let value = vm.async_call(["main"], (function,)).await?;
108 /// let value: u32 = rune::from_value(value)?;
109 /// assert_eq!(value, 42);
110 /// # Ok::<_, rune::support::Error>(())
111 /// # })?;
112 /// # Ok::<_, rune::support::Error>(())
113 /// ```
114 pub fn new<F, A, K>(f: F) -> alloc::Result<Self>
115 where
116 F: function::Function<A, K>,
117 K: function::FunctionKind,
118 {
119 Ok(Self(FunctionImpl {
120 inner: Inner::FnHandler(FnHandler {
121 handler: FunctionHandler::new(move |stack, addr, args, output| {
122 f.call(stack, addr, args, output)
123 })?,
124 hash: Hash::EMPTY,
125 }),
126 }))
127 }
128
129 /// Perform an asynchronous call over the function which also implements
130 /// [Send].
131 pub async fn async_send_call<A, T>(&self, args: A) -> Result<T, VmError>
132 where
133 A: Send + GuardedArgs,
134 T: Send + FromValue,
135 {
136 self.0.async_send_call(args).await
137 }
138
139 /// Perform a call over the function represented by this function pointer.
140 ///
141 /// # Examples
142 ///
143 /// ```
144 /// use rune::{Hash, Vm};
145 /// use rune::runtime::Function;
146 /// use rune::sync::Arc;
147 ///
148 /// let mut sources = rune::sources! {
149 /// entry => {
150 /// fn add(a, b) {
151 /// a + b
152 /// }
153 ///
154 /// pub fn main() { add }
155 /// }
156 /// };
157 ///
158 /// let unit = rune::prepare(&mut sources).build()?;
159 /// let unit = Arc::try_new(unit)?;
160 /// let mut vm = Vm::without_runtime(unit)?;
161 ///
162 /// let value = vm.call(["main"], ())?;
163 ///
164 /// let value: Function = rune::from_value(value)?;
165 /// assert_eq!(value.call::<u32>((1, 2))?, 3);
166 /// # Ok::<_, rune::support::Error>(())
167 /// ```
168 pub fn call<T>(&self, args: impl GuardedArgs) -> Result<T, VmError>
169 where
170 T: FromValue,
171 {
172 self.0.call(args)
173 }
174
175 /// Call with the given virtual machine. This allows for certain
176 /// optimizations, like avoiding the allocation of a new vm state in case
177 /// the call is internal.
178 ///
179 /// A stop reason will be returned in case the function call results in
180 /// a need to suspend the execution.
181 pub(crate) fn call_with_vm(
182 &self,
183 vm: &mut Vm,
184 addr: Address,
185 args: usize,
186 out: Output,
187 ) -> Result<Option<VmHalt>, VmError> {
188 self.0.call_with_vm(vm, addr, args, out)
189 }
190
191 /// Create a function pointer from a handler.
192 pub(crate) fn from_handler(handler: FunctionHandler, hash: Hash) -> Self {
193 Self(FunctionImpl::from_handler(handler, hash))
194 }
195
196 /// Create a function pointer from an offset.
197 pub(crate) fn from_vm_offset(
198 context: Arc<RuntimeContext>,
199 unit: Arc<Unit>,
200 globals: Globals,
201 offset: usize,
202 call: Call,
203 args: usize,
204 hash: Hash,
205 ) -> Self {
206 Self(FunctionImpl::from_offset(
207 context, unit, globals, offset, call, args, hash,
208 ))
209 }
210
211 /// Create a function pointer from an offset.
212 pub(crate) fn from_vm_closure(
213 context: Arc<RuntimeContext>,
214 unit: Arc<Unit>,
215 globals: Globals,
216 offset: usize,
217 call: Call,
218 args: usize,
219 environment: Box<[Value]>,
220 hash: Hash,
221 ) -> Self {
222 Self(FunctionImpl::from_closure(
223 context,
224 unit,
225 globals,
226 offset,
227 call,
228 args,
229 environment,
230 hash,
231 ))
232 }
233
234 /// Create a function pointer from an offset.
235 pub(crate) fn from_unit_struct(rtti: Arc<Rtti>) -> Self {
236 Self(FunctionImpl::from_unit_struct(rtti))
237 }
238
239 /// Create a function pointer from an offset.
240 pub(crate) fn from_tuple_struct(rtti: Arc<Rtti>, args: usize) -> Self {
241 Self(FunctionImpl::from_tuple_struct(rtti, args))
242 }
243
244 /// Type [Hash][struct@Hash] of the underlying function.
245 ///
246 /// # Examples
247 ///
248 /// The type hash of a top-level function matches what you get out of
249 /// [Hash::type_hash].
250 ///
251 /// ```
252 /// use rune::runtime::Function;
253 /// use rune::sync::Arc;
254 /// use rune::{Hash, Vm};
255 ///
256 /// let mut sources = rune::sources! {
257 /// entry => {
258 /// fn pony() { }
259 ///
260 /// pub fn main() { pony }
261 /// }
262 /// };
263 ///
264 /// let unit = rune::prepare(&mut sources).build()?;
265 /// let unit = Arc::try_new(unit)?;
266 /// let mut vm = Vm::without_runtime(unit)?;
267 ///
268 /// let pony = vm.call(["main"], ())?;
269 /// let pony: Function = rune::from_value(pony)?;
270 ///
271 /// assert_eq!(pony.type_hash(), Hash::type_hash(["pony"]));
272 /// # Ok::<_, rune::support::Error>(())
273 /// ```
274 pub fn type_hash(&self) -> Hash {
275 self.0.type_hash()
276 }
277
278 /// Try to convert into a [SyncFunction]. This might not be possible if this
279 /// function is something which is not [Sync], like a closure capturing
280 /// context which is not thread-safe.
281 ///
282 /// # Examples
283 ///
284 /// ```
285 /// use rune::{Hash, Vm};
286 /// use rune::runtime::Function;
287 /// use rune::sync::Arc;
288 ///
289 /// let mut sources = rune::sources! {
290 /// entry => {
291 /// fn pony() { }
292 ///
293 /// pub fn main() { pony }
294 /// }
295 /// };
296 ///
297 /// let unit = rune::prepare(&mut sources).build()?;
298 /// let unit = Arc::try_new(unit)?;
299 /// let mut vm = Vm::without_runtime(unit)?;
300 ///
301 /// let pony = vm.call(["main"], ())?;
302 /// let pony: Function = rune::from_value(pony)?;
303 ///
304 /// // This is fine, since `pony` is a free function.
305 /// let pony = pony.into_sync()?;
306 ///
307 /// assert_eq!(pony.type_hash(), Hash::type_hash(["pony"]));
308 /// # Ok::<_, rune::support::Error>(())
309 /// ```
310 ///
311 /// The following *does not* work, because we return a closure which tries
312 /// to make use of a [Generator][crate::runtime::Generator] which is not a
313 /// constant value.
314 ///
315 /// ```
316 /// use rune::runtime::Function;
317 /// use rune::sync::Arc;
318 /// use rune::{Hash, Vm};
319 ///
320 /// let mut sources = rune::sources! {
321 /// entry => {
322 /// fn generator() {
323 /// yield 42;
324 /// }
325 ///
326 /// pub fn main() {
327 /// let g = generator();
328 ///
329 /// move || {
330 /// g.next()
331 /// }
332 /// }
333 /// }
334 /// };
335 ///
336 /// let unit = rune::prepare(&mut sources).build()?;
337 /// let unit = Arc::try_new(unit)?;
338 /// let mut vm = Vm::without_runtime(unit)?;
339 ///
340 /// let closure = vm.call(["main"], ())?;
341 /// let closure: Function = rune::from_value(closure)?;
342 ///
343 /// // This is *not* fine since the returned closure has captured a
344 /// // generator which is not a constant value.
345 /// assert!(closure.into_sync().is_err());
346 /// # Ok::<_, rune::support::Error>(())
347 /// ```
348 pub fn into_sync(self) -> Result<SyncFunction, RuntimeError> {
349 Ok(SyncFunction(self.0.into_sync()?))
350 }
351
352 /// Clone a function.
353 ///
354 /// # Examples
355 ///
356 /// ```rune
357 /// fn function() {
358 /// 42
359 /// }
360 ///
361 /// let a = function;
362 /// let b = a.clone();
363 /// assert_eq!(a(), b());
364 /// ```
365 #[rune::function(keep, protocol = CLONE)]
366 fn clone(&self) -> Result<Function, VmError> {
367 Ok(self.try_clone()?)
368 }
369
370 /// Debug format a function.
371 ///
372 /// # Examples
373 ///
374 /// ```rune
375 /// fn function() {
376 /// 42
377 /// }
378 ///
379 /// println!("{function:?}");
380 /// ```
381 #[rune::function(keep, protocol = DEBUG_FMT)]
382 fn debug_fmt(&self, f: &mut Formatter) -> alloc::Result<()> {
383 write!(f, "{self:?}")
384 }
385}
386
387/// A callable sync function. This currently only supports a subset of values
388/// that are supported by the Vm.
389#[repr(transparent)]
390pub struct SyncFunction(FunctionImpl<ConstValueBuf>);
391
392assert_impl!(SyncFunction: Send + Sync);
393
394impl SyncFunction {
395 /// Perform an asynchronous call over the function which also implements
396 /// [Send].
397 ///
398 /// # Examples
399 ///
400 /// ```
401 /// use rune::runtime::SyncFunction;
402 /// use rune::sync::Arc;
403 /// use rune::{Hash, Vm};
404 ///
405 /// # futures_executor::block_on(async move {
406 /// let mut sources = rune::sources! {
407 /// entry => {
408 /// async fn add(a, b) {
409 /// a + b
410 /// }
411 ///
412 /// pub fn main() { add }
413 /// }
414 /// };
415 ///
416 /// let unit = rune::prepare(&mut sources).build()?;
417 /// let unit = Arc::try_new(unit)?;
418 /// let mut vm = Vm::without_runtime(unit)?;
419 ///
420 /// let add = vm.call(["main"], ())?;
421 /// let add: SyncFunction = rune::from_value(add)?;
422 ///
423 /// let value = add.async_send_call::<u32>((1, 2)).await?;
424 /// assert_eq!(value, 3);
425 /// # Ok::<_, rune::support::Error>(())
426 /// # })?;
427 /// # Ok::<_, rune::support::Error>(())
428 /// ```
429 pub async fn async_send_call<T>(&self, args: impl GuardedArgs + Send) -> Result<T, VmError>
430 where
431 T: Send + FromValue,
432 {
433 self.0.async_send_call(args).await
434 }
435
436 /// Perform a call over the function represented by this function pointer.
437 ///
438 /// # Examples
439 ///
440 /// ```
441 /// use rune::runtime::SyncFunction;
442 /// use rune::sync::Arc;
443 /// use rune::{Hash, Vm};
444 ///
445 /// let mut sources = rune::sources! {
446 /// entry => {
447 /// fn add(a, b) {
448 /// a + b
449 /// }
450 ///
451 /// pub fn main() { add }
452 /// }
453 /// };
454 ///
455 /// let unit = rune::prepare(&mut sources).build()?;
456 /// let unit = Arc::try_new(unit)?;
457 /// let mut vm = Vm::without_runtime(unit)?;
458 ///
459 /// let add = vm.call(["main"], ())?;
460 /// let add: SyncFunction = rune::from_value(add)?;
461 ///
462 /// assert_eq!(add.call::<u32>((1, 2))?, 3);
463 /// # Ok::<_, rune::support::Error>(())
464 /// ```
465 pub fn call<T>(&self, args: impl GuardedArgs) -> Result<T, VmError>
466 where
467 T: FromValue,
468 {
469 self.0.call(args)
470 }
471
472 /// Type [Hash][struct@Hash] of the underlying function.
473 ///
474 /// # Examples
475 ///
476 /// The type hash of a top-level function matches what you get out of
477 /// [Hash::type_hash].
478 ///
479 /// ```
480 /// use rune::runtime::SyncFunction;
481 /// use rune::sync::Arc;
482 /// use rune::{Hash, Vm};
483 ///
484 /// let mut sources = rune::sources! {
485 /// entry => {
486 /// fn pony() { }
487 ///
488 /// pub fn main() { pony }
489 /// }
490 /// };
491 ///
492 /// let unit = rune::prepare(&mut sources).build()?;
493 /// let unit = Arc::try_new(unit)?;
494 /// let mut vm = Vm::without_runtime(unit)?;
495 ///
496 /// let pony = vm.call(["main"], ())?;
497 /// let pony: SyncFunction = rune::from_value(pony)?;
498 ///
499 /// assert_eq!(pony.type_hash(), Hash::type_hash(["pony"]));
500 /// # Ok::<_, rune::support::Error>(())
501 /// ```
502 pub fn type_hash(&self) -> Hash {
503 self.0.type_hash()
504 }
505}
506
507impl TryClone for SyncFunction {
508 fn try_clone(&self) -> alloc::Result<Self> {
509 Ok(Self(self.0.try_clone()?))
510 }
511}
512
513/// A stored function, of some specific kind.
514struct FunctionImpl<V>
515where
516 V: FnValue,
517{
518 inner: Inner<V>,
519}
520
521impl<V> TryClone for FunctionImpl<V>
522where
523 V: FnValue,
524{
525 #[inline]
526 fn try_clone(&self) -> alloc::Result<Self> {
527 Ok(Self {
528 inner: self.inner.try_clone()?,
529 })
530 }
531}
532
533impl<V> FunctionImpl<V>
534where
535 V: FnValue,
536 OwnedTuple: TryFrom<Box<[V]>>,
537 VmErrorKind: From<<OwnedTuple as TryFrom<Box<[V]>>>::Error>,
538{
539 fn call<T>(&self, args: impl GuardedArgs) -> Result<T, VmError>
540 where
541 T: FromValue,
542 {
543 let value = match &self.inner {
544 Inner::FnHandler(handler) => {
545 let count = args.count();
546 let size = count.max(1);
547 // Ensure we have space for the return value.
548 let mut stack = Stack::with_capacity(size)?;
549 let _guard = unsafe { args.guarded_into_stack(&mut stack) }?;
550 stack.resize(size)?;
551 handler
552 .handler
553 .call(&mut stack, Address::ZERO, count, Address::ZERO.output())?;
554 stack.at(Address::ZERO).clone()
555 }
556 Inner::FnOffset(fn_offset) => fn_offset.call(args, ())?,
557 Inner::FnClosureOffset(closure) => {
558 let environment = closure.environment.try_clone()?;
559 let environment = OwnedTuple::try_from(environment)?;
560 closure.fn_offset.call(args, (environment,))?
561 }
562 Inner::FnUnitStruct(empty) => {
563 check_args(args.count(), 0)?;
564 Value::empty_struct(empty.rtti.clone())?
565 }
566 Inner::FnTupleStruct(tuple) => {
567 check_args(args.count(), tuple.args)?;
568 // SAFETY: We don't let the guard outlive the value.
569 let (args, _guard) = unsafe { args.guarded_into_vec()? };
570 Value::tuple_struct(tuple.rtti.clone(), args)?
571 }
572 };
573
574 Ok(T::from_value(value)?)
575 }
576
577 fn async_send_call<'a, A, T>(
578 &'a self,
579 args: A,
580 ) -> impl Future<Output = Result<T, VmError>> + Send + 'a
581 where
582 A: 'a + Send + GuardedArgs,
583 T: 'a + Send + FromValue,
584 {
585 let future = async move {
586 let value: Value = self.call(args)?;
587
588 let value = match value.try_borrow_mut::<runtime::Future>()? {
589 Some(future) => future.await?,
590 None => value,
591 };
592
593 Ok(T::from_value(value)?)
594 };
595
596 // Safety: Future is send because there is no way to call this
597 // function in a manner which allows any values from the future
598 // to escape outside of this future, hence it can only be
599 // scheduled by one thread at a time.
600 unsafe { AssertSend::new(future) }
601 }
602
603 /// Call with the given virtual machine. This allows for certain
604 /// optimizations, like avoiding the allocation of a new vm state in case
605 /// the call is internal.
606 ///
607 /// A stop reason will be returned in case the function call results in
608 /// a need to suspend the execution.
609 pub(crate) fn call_with_vm(
610 &self,
611 vm: &mut Vm,
612 addr: Address,
613 args: usize,
614 out: Output,
615 ) -> Result<Option<VmHalt>, VmError> {
616 let reason = match &self.inner {
617 Inner::FnHandler(handler) => {
618 handler.handler.call(vm.stack_mut(), addr, args, out)?;
619 None
620 }
621 Inner::FnOffset(fn_offset) => {
622 if let Some(vm_call) = fn_offset.call_with_vm(vm, addr, args, (), out)? {
623 return Ok(Some(VmHalt::VmCall(vm_call)));
624 }
625
626 None
627 }
628 Inner::FnClosureOffset(closure) => {
629 let environment = closure.environment.try_clone()?;
630 let environment = OwnedTuple::try_from(environment)?;
631
632 if let Some(vm_call) =
633 closure
634 .fn_offset
635 .call_with_vm(vm, addr, args, (environment,), out)?
636 {
637 return Ok(Some(VmHalt::VmCall(vm_call)));
638 }
639
640 None
641 }
642 Inner::FnUnitStruct(empty) => {
643 check_args(args, 0)?;
644 vm.store(out, || Value::empty_struct(empty.rtti.clone()))?;
645 None
646 }
647 Inner::FnTupleStruct(tuple) => {
648 check_args(args, tuple.args)?;
649
650 let seq = vm.stack().slice_at(addr, args)?;
651 let data = seq.iter().cloned();
652 let value = AnySequence::new(tuple.rtti.clone(), data)?;
653 vm.store(out, value)?;
654 None
655 }
656 };
657
658 Ok(reason)
659 }
660
661 /// Create a function pointer from a handler.
662 pub(crate) fn from_handler(handler: FunctionHandler, hash: Hash) -> Self {
663 Self {
664 inner: Inner::FnHandler(FnHandler { handler, hash }),
665 }
666 }
667
668 /// Create a function pointer from an offset.
669 pub(crate) fn from_offset(
670 context: Arc<RuntimeContext>,
671 unit: Arc<Unit>,
672 globals: V::Globals,
673 offset: usize,
674 call: Call,
675 args: usize,
676 hash: Hash,
677 ) -> Self {
678 Self {
679 inner: Inner::FnOffset(FnOffset {
680 context,
681 unit,
682 globals,
683 offset,
684 call,
685 args,
686 hash,
687 }),
688 }
689 }
690
691 /// Create a function pointer from an offset.
692 pub(crate) fn from_closure(
693 context: Arc<RuntimeContext>,
694 unit: Arc<Unit>,
695 globals: V::Globals,
696 offset: usize,
697 call: Call,
698 args: usize,
699 environment: Box<[V]>,
700 hash: Hash,
701 ) -> Self {
702 Self {
703 inner: Inner::FnClosureOffset(FnClosureOffset {
704 fn_offset: FnOffset {
705 context,
706 unit,
707 globals,
708 offset,
709 call,
710 args,
711 hash,
712 },
713 environment,
714 }),
715 }
716 }
717
718 /// Create a function pointer from an offset.
719 pub(crate) fn from_unit_struct(rtti: Arc<Rtti>) -> Self {
720 Self {
721 inner: Inner::FnUnitStruct(FnUnitStruct { rtti }),
722 }
723 }
724
725 /// Create a function pointer from an offset.
726 pub(crate) fn from_tuple_struct(rtti: Arc<Rtti>, args: usize) -> Self {
727 Self {
728 inner: Inner::FnTupleStruct(FnTupleStruct { rtti, args }),
729 }
730 }
731
732 #[inline]
733 fn type_hash(&self) -> Hash {
734 match &self.inner {
735 Inner::FnHandler(FnHandler { hash, .. }) | Inner::FnOffset(FnOffset { hash, .. }) => {
736 *hash
737 }
738 Inner::FnClosureOffset(fco) => fco.fn_offset.hash,
739 Inner::FnUnitStruct(func) => func.rtti.type_hash(),
740 Inner::FnTupleStruct(func) => func.rtti.type_hash(),
741 }
742 }
743}
744
745impl FunctionImpl<Value> {
746 /// Try to convert into a [SyncFunction].
747 fn into_sync(self) -> Result<FunctionImpl<ConstValueBuf>, RuntimeError> {
748 let inner = match self.inner {
749 Inner::FnClosureOffset(closure) => {
750 let mut env = Vec::try_with_capacity(closure.environment.len())?;
751
752 for value in Vec::from(closure.environment) {
753 env.try_push(FromValue::from_value(value)?)?;
754 }
755
756 Inner::FnClosureOffset(FnClosureOffset {
757 fn_offset: closure.fn_offset.into_sync(),
758 environment: env.try_into_boxed_slice()?,
759 })
760 }
761 Inner::FnHandler(inner) => Inner::FnHandler(inner),
762 Inner::FnOffset(inner) => Inner::FnOffset(inner.into_sync()),
763 Inner::FnUnitStruct(inner) => Inner::FnUnitStruct(inner),
764 Inner::FnTupleStruct(inner) => Inner::FnTupleStruct(inner),
765 };
766
767 Ok(FunctionImpl { inner })
768 }
769}
770
771/// A closure holds the environment it captured, so a closure which captured
772/// another one nests just like a container does.
773impl Dismantle for Function {
774 fn dismantle(&mut self, out: &mut Handover<'_>) {
775 let Inner::FnClosureOffset(closure) = &mut self.0.inner else {
776 return;
777 };
778
779 out.consume_all(closure.environment.iter_mut());
780 }
781}
782
783impl fmt::Debug for Function {
784 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
785 match &self.0.inner {
786 Inner::FnHandler(handler) => {
787 write!(f, "native function ({:p})", handler.handler)?;
788 }
789 Inner::FnOffset(offset) => {
790 write!(f, "{} function (at: 0x{:x})", offset.call, offset.offset)?;
791 }
792 Inner::FnClosureOffset(closure) => {
793 write!(
794 f,
795 "closure (at: 0x{:x}, env:{:?})",
796 closure.fn_offset.offset, closure.environment
797 )?;
798 }
799 Inner::FnUnitStruct(empty) => {
800 write!(f, "empty {}", empty.rtti.item)?;
801 }
802 Inner::FnTupleStruct(tuple) => {
803 write!(f, "tuple {}", tuple.rtti.item)?;
804 }
805 }
806
807 Ok(())
808 }
809}
810
811#[derive(Debug)]
812enum Inner<V>
813where
814 V: FnValue,
815{
816 /// A native function handler.
817 /// This is wrapped as an `Arc<dyn FunctionHandler>`.
818 FnHandler(FnHandler),
819 /// The offset to a free function.
820 ///
821 /// This also captures the context and unit it belongs to allow for external
822 /// calls.
823 FnOffset(FnOffset<V>),
824 /// A closure with a captured environment.
825 ///
826 /// This also captures the context and unit it belongs to allow for external
827 /// calls.
828 FnClosureOffset(FnClosureOffset<V>),
829 /// Constructor for a unit struct.
830 FnUnitStruct(FnUnitStruct),
831 /// Constructor for a tuple.
832 FnTupleStruct(FnTupleStruct),
833}
834
835impl<V> TryClone for Inner<V>
836where
837 V: FnValue,
838{
839 fn try_clone(&self) -> alloc::Result<Self> {
840 Ok(match self {
841 Inner::FnHandler(inner) => Inner::FnHandler(inner.clone()),
842 Inner::FnOffset(inner) => Inner::FnOffset(inner.clone()),
843 Inner::FnClosureOffset(inner) => Inner::FnClosureOffset(inner.try_clone()?),
844 Inner::FnUnitStruct(inner) => Inner::FnUnitStruct(inner.clone()),
845 Inner::FnTupleStruct(inner) => Inner::FnTupleStruct(inner.clone()),
846 })
847 }
848}
849
850#[derive(Clone, TryClone)]
851struct FnHandler {
852 /// The function handler.
853 handler: FunctionHandler,
854 /// Hash for the function type
855 hash: Hash,
856}
857
858impl fmt::Debug for FnHandler {
859 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
860 write!(f, "FnHandler")
861 }
862}
863
864/// The kind of value a function closes over, and with it the kind of static
865/// item storage the function is able to carry.
866///
867/// A [`Function`] carries the [`Globals`] of the virtual machine which produced
868/// it, so that calling it from the outside still observes the same statics. A
869/// [`SyncFunction`] can be sent between threads and the storage isn't thread
870/// safe, so it carries nothing.
871pub(crate) trait FnValue: TryClone {
872 /// How static item storage is represented for this kind of function.
873 type Globals: TryClone + Clone;
874
875 /// Materialize the storage a virtual machine should be given.
876 fn globals(globals: &Self::Globals) -> Globals;
877
878 /// Test if the given vm already uses this storage.
879 fn same_globals(globals: &Self::Globals, vm: &Vm) -> bool;
880}
881
882impl FnValue for Value {
883 type Globals = Globals;
884
885 #[inline]
886 fn globals(globals: &Self::Globals) -> Globals {
887 globals.clone()
888 }
889
890 #[inline]
891 fn same_globals(globals: &Self::Globals, vm: &Vm) -> bool {
892 vm.is_same_globals(globals)
893 }
894}
895
896impl FnValue for ConstValueBuf {
897 type Globals = ();
898
899 #[inline]
900 fn globals(_: &Self::Globals) -> Globals {
901 Globals::empty()
902 }
903
904 #[inline]
905 fn same_globals(_: &Self::Globals, vm: &Vm) -> bool {
906 !vm.globals().is_configured()
907 }
908}
909
910struct FnOffset<V>
911where
912 V: FnValue,
913{
914 context: Arc<RuntimeContext>,
915 /// The unit where the function resides.
916 unit: Arc<Unit>,
917 /// The storage for static items declared by the unit.
918 globals: V::Globals,
919 /// The offset of the function.
920 offset: usize,
921 /// The calling convention.
922 call: Call,
923 /// The number of arguments the function takes.
924 args: usize,
925 /// Hash for the function type
926 hash: Hash,
927}
928
929impl<V> Clone for FnOffset<V>
930where
931 V: FnValue,
932{
933 fn clone(&self) -> Self {
934 Self {
935 context: self.context.clone(),
936 unit: self.unit.clone(),
937 globals: self.globals.clone(),
938 offset: self.offset,
939 call: self.call,
940 args: self.args,
941 hash: self.hash,
942 }
943 }
944}
945
946impl<V> TryClone for FnOffset<V>
947where
948 V: FnValue,
949{
950 #[inline]
951 fn try_clone(&self) -> alloc::Result<Self> {
952 Ok(self.clone())
953 }
954}
955
956impl<V> FnOffset<V>
957where
958 V: FnValue,
959{
960 /// Perform a call into the specified offset and return the produced value.
961 #[tracing::instrument(skip_all, fields(args = args.count(), extra = extra.count(), ?self.offset, ?self.call, ?self.args, ?self.hash))]
962 fn call(&self, args: impl GuardedArgs, extra: impl Args) -> Result<Value, VmError> {
963 check_args(args.count().wrapping_add(extra.count()), self.args)?;
964
965 let mut vm = Vm::new(self.context.clone(), self.unit.clone())
966 .with_globals(V::globals(&self.globals));
967
968 vm.set_ip(self.offset);
969 let _guard = unsafe { args.guarded_into_stack(vm.stack_mut())? };
970 extra.into_stack(vm.stack_mut())?;
971
972 self.call.call_with_vm(vm)
973 }
974
975 /// Perform a potentially optimized call into the specified vm.
976 ///
977 /// This will cause a halt in case the vm being called into isn't the same
978 /// as the context and unit of the function.
979 #[tracing::instrument(skip_all, fields(args, extra = extra.count(), keep, ?self.offset, ?self.call, ?self.args, ?self.hash))]
980 fn call_with_vm(
981 &self,
982 vm: &mut Vm,
983 addr: Address,
984 args: usize,
985 extra: impl Args,
986 out: Output,
987 ) -> Result<Option<VmCall>, VmError> {
988 check_args(args.wrapping_add(extra.count()), self.args)?;
989
990 let same_unit = matches!(self.call, Call::Immediate if vm.is_same_unit(&self.unit));
991 let same_context =
992 matches!(self.call, Call::Immediate if vm.is_same_context(&self.context));
993 let same_globals =
994 matches!(self.call, Call::Immediate if V::same_globals(&self.globals, vm));
995
996 vm.push_call_frame(self.offset, addr, args, Isolated::new(!same_context), out)?;
997 extra.into_stack(vm.stack_mut())?;
998
999 // Fast path, just allocate a call frame and keep running.
1000 if same_context && same_unit && same_globals {
1001 tracing::trace!("same context, unit and globals");
1002 return Ok(None);
1003 }
1004
1005 let call = VmCall::new(
1006 self.call,
1007 (!same_context).then(|| self.context.clone()),
1008 (!same_unit).then(|| self.unit.clone()),
1009 (!same_globals).then(|| V::globals(&self.globals)),
1010 out,
1011 );
1012
1013 Ok(Some(call))
1014 }
1015}
1016
1017impl FnOffset<Value> {
1018 /// Shed the static item storage so that the function can be sent between
1019 /// threads.
1020 ///
1021 /// The storage isn't thread safe, so a [`SyncFunction`] cannot carry it.
1022 /// Reading a static through the resulting function reports that no storage
1023 /// has been configured.
1024 fn into_sync(self) -> FnOffset<ConstValueBuf> {
1025 FnOffset {
1026 context: self.context,
1027 unit: self.unit,
1028 globals: (),
1029 offset: self.offset,
1030 call: self.call,
1031 args: self.args,
1032 hash: self.hash,
1033 }
1034 }
1035}
1036
1037impl<V> fmt::Debug for FnOffset<V>
1038where
1039 V: FnValue,
1040{
1041 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1042 f.debug_struct("FnOffset")
1043 .field("context", &(&self.context as *const _))
1044 .field("unit", &(&self.unit as *const _))
1045 .field("offset", &self.offset)
1046 .field("call", &self.call)
1047 .field("args", &self.args)
1048 .finish()
1049 }
1050}
1051
1052#[derive(Debug)]
1053struct FnClosureOffset<V>
1054where
1055 V: FnValue,
1056{
1057 /// The offset in the associated unit that the function lives.
1058 fn_offset: FnOffset<V>,
1059 /// Captured environment.
1060 environment: Box<[V]>,
1061}
1062
1063impl<V> TryClone for FnClosureOffset<V>
1064where
1065 V: FnValue,
1066{
1067 #[inline]
1068 fn try_clone(&self) -> alloc::Result<Self> {
1069 Ok(Self {
1070 fn_offset: self.fn_offset.clone(),
1071 environment: self.environment.try_clone()?,
1072 })
1073 }
1074}
1075
1076#[derive(Debug, Clone, TryClone)]
1077struct FnUnitStruct {
1078 /// The type of the empty.
1079 rtti: Arc<Rtti>,
1080}
1081
1082#[derive(Debug, Clone, TryClone)]
1083struct FnTupleStruct {
1084 /// The type of the tuple.
1085 rtti: Arc<Rtti>,
1086 /// The number of arguments the tuple takes.
1087 args: usize,
1088}
1089
1090impl FromValue for SyncFunction {
1091 #[inline]
1092 fn from_value(value: Value) -> Result<Self, RuntimeError> {
1093 value.downcast::<Function>()?.into_sync()
1094 }
1095}
1096
1097#[inline]
1098fn check_args(actual: usize, expected: usize) -> Result<(), VmError> {
1099 if actual != expected {
1100 return Err(VmError::new(VmErrorKind::BadArgumentCount {
1101 expected,
1102 actual,
1103 }));
1104 }
1105
1106 Ok(())
1107}