about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/value/thunk.rs46
1 files changed, 45 insertions, 1 deletions
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs
index 9f1415989335..420c5fe039aa 100644
--- a/tvix/eval/src/value/thunk.rs
+++ b/tvix/eval/src/value/thunk.rs
@@ -27,9 +27,10 @@ use std::{
 use codemap::Span;
 
 use crate::{
+    chunk::Chunk,
     errors::{Error, ErrorKind},
     upvalues::Upvalues,
-    value::Closure,
+    value::{Builtin, Closure},
     vm::VM,
     Value,
 };
@@ -84,6 +85,49 @@ impl Thunk {
         })))
     }
 
+    /// Create a new thunk from suspended Rust code.
+    ///
+    /// The suspended code will be executed and expected to return a
+    /// value whenever the thunk is forced like any other thunk.
+    pub fn new_suspended_native(
+        native: Rc<Box<dyn Fn(&mut VM) -> Result<Value, ErrorKind>>>,
+    ) -> Self {
+        let span = codemap::CodeMap::new()
+            .add_file("<internal>".to_owned(), "<internal>".to_owned())
+            .span;
+        let builtin = Builtin::new(
+            "Thunk::new_suspended_native()",
+            &[crate::value::builtin::BuiltinArgument {
+                strict: true,
+                name: "fake",
+            }],
+            None,
+            move |v: Vec<Value>, vm: &mut VM| {
+                // sanity check that only the dummy argument was popped
+                assert_eq!(v.len(), 1);
+                assert!(matches!(v[0], Value::Null));
+                native(vm)
+            },
+        );
+        let mut chunk = Chunk::default();
+        let constant_idx = chunk.push_constant(Value::Builtin(builtin));
+        // Tvix doesn't have "0-ary" builtins, so we have to push a fake argument
+        chunk.push_op(crate::opcode::OpCode::OpNull, span);
+        chunk.push_op(crate::opcode::OpCode::OpConstant(constant_idx), span);
+        chunk.push_op(crate::opcode::OpCode::OpCall, span);
+        let lambda = Lambda {
+            name: None,
+            formals: None,
+            upvalue_count: 0,
+            chunk,
+        };
+        Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended {
+            lambda: Rc::new(lambda),
+            upvalues: Rc::new(Upvalues::with_capacity(0)),
+            span: span,
+        })))
+    }
+
     /// Evaluate the content of a thunk, potentially repeatedly, until a
     /// non-thunk value is returned.
     ///