1pub mod generators;
13mod macros;
14
15use bstr::{BString, ByteSlice, ByteVec};
16use codemap::Span;
17use rustc_hash::FxHashMap;
18use serde_json::json;
19use std::{
20 cmp::Ordering,
21 ffi::OsStr,
22 path::{Path, PathBuf},
23 rc::Rc,
24};
25
26use crate::{
27 NixString, SourceCode, arithmetic_op,
28 chunk::Chunk,
29 cmp_op,
30 compiler::GlobalsMap,
31 errors::{CatchableErrorKind, Error, ErrorKind, EvalResult},
32 io::EvalIO,
33 lifted_pop,
34 nix_search_path::NixSearchPath,
35 observer::{OptionalRuntimeObserver, RuntimeObserver},
36 opcode::{CodeIdx, Op, Position, UpvalueIdx},
37 upvalues::{UpvalueData, Upvalues},
38 value::{
39 Builtin, BuiltinResult, Closure, CoercionKind, Lambda, NixAttrs, NixContext, NixList,
40 PointerEquality, Thunk, Value, canon_path,
41 },
42 vm::generators::GenCo,
43 warnings::{EvalWarning, WarningKind},
44};
45
46use generators::{Generator, GeneratorState, call_functor};
47
48use self::generators::{VMRequest, VMResponse};
49
50trait GetSpan {
53 fn get_span(self) -> Span;
54}
55
56impl<IO> GetSpan for &VM<'_, IO> {
57 fn get_span(self) -> Span {
58 self.reasonable_span
59 }
60}
61
62impl GetSpan for &CallFrame {
63 fn get_span(self) -> Span {
64 self.current_span()
65 }
66}
67
68impl GetSpan for &Span {
69 fn get_span(self) -> Span {
70 *self
71 }
72}
73
74impl GetSpan for Span {
75 fn get_span(self) -> Span {
76 self
77 }
78}
79
80trait WithSpan<T, S: GetSpan, IO> {
84 fn with_span(self, top_span: S, vm: &VM<IO>) -> Result<T, Error>;
85}
86
87impl<T, S: GetSpan, IO> WithSpan<T, S, IO> for Result<T, ErrorKind> {
88 fn with_span(self, top_span: S, vm: &VM<IO>) -> Result<T, Error> {
89 match self {
90 Ok(something) => Ok(something),
91 Err(kind) => {
92 let mut error = Error::new(kind, top_span.get_span(), vm.source.clone());
93
94 for frame in vm.frames.iter().rev() {
97 match frame {
98 Frame::CallFrame { span, .. } => {
99 error = Error::new(
100 ErrorKind::BytecodeError(Box::new(error)),
101 *span,
102 vm.source.clone(),
103 );
104 }
105 Frame::Generator { name, span, .. } => {
106 error = Error::new(
107 ErrorKind::NativeError {
108 err: Box::new(error),
109 gen_type: name,
110 },
111 *span,
112 vm.source.clone(),
113 );
114 }
115 }
116 }
117
118 Err(error)
119 }
120 }
121 }
122}
123
124struct CallFrame {
125 lambda: Rc<Lambda>,
127
128 upvalues: Rc<Upvalues>,
131
132 ip: CodeIdx,
135
136 stack_offset: usize,
138}
139
140impl CallFrame {
141 fn upvalue(&self, idx: UpvalueIdx) -> &Value {
143 &self.upvalues[idx]
144 }
145
146 fn chunk(&self) -> &Chunk {
148 &self.lambda.chunk
149 }
150
151 fn inc_ip(&mut self) -> Op {
154 debug_assert!(
155 self.ip.0 < self.chunk().code.len(),
156 "out of bounds code at IP {} in {:p}",
157 self.ip.0,
158 self.lambda
159 );
160
161 let op = self.chunk().code[self.ip.0];
162 self.ip += 1;
163 op.into()
164 }
165
166 fn read_uvarint(&mut self) -> u64 {
169 let (arg, size) = self.chunk().read_uvarint(self.ip.0);
170 self.ip += size;
171 arg
172 }
173
174 fn read_u16(&mut self) -> u16 {
176 let arg = self.chunk().read_u16(self.ip.0);
177 self.ip += 2;
178 arg
179 }
180
181 pub fn error<T, IO>(&self, vm: &VM<IO>, kind: ErrorKind) -> Result<T, Error> {
184 Err(kind).with_span(self, vm)
185 }
186
187 pub fn current_span(&self) -> Span {
190 self.chunk().get_span(self.ip - 1)
191 }
192}
193
194enum Frame {
201 CallFrame {
204 call_frame: CallFrame,
207
208 span: Span,
210 },
211
212 Generator {
219 name: &'static str,
221
222 span: Span,
224
225 state: GeneratorState,
226
227 generator: Generator,
229 },
230}
231
232impl Frame {
233 pub fn span(&self) -> Span {
234 match self {
235 Frame::CallFrame { span, .. } | Frame::Generator { span, .. } => *span,
236 }
237 }
238}
239
240#[derive(Default)]
241struct ImportCache(FxHashMap<PathBuf, Value>);
255
256impl ImportCache {
257 fn get(&self, path: impl AsRef<Path>) -> Option<&Value> {
258 let path = path.as_ref();
259 let path = match std::fs::canonicalize(path).map_err(ErrorKind::from) {
260 Ok(path) => path,
261 Err(_) => path.to_owned(),
262 };
263 self.0.get(&path)
264 }
265
266 fn insert(&mut self, path: PathBuf, value: Value) -> Option<Value> {
267 self.0.insert(
268 match std::fs::canonicalize(path.as_path()).map_err(ErrorKind::from) {
269 Ok(path) => path,
270 Err(_) => path,
271 },
272 value,
273 )
274 }
275}
276
277#[derive(Default)]
279struct PathImportCache(FxHashMap<PathBuf, PathBuf>);
280impl PathImportCache {
281 fn get(&self, path: impl AsRef<Path>) -> Option<PathBuf> {
282 self.0.get(path.as_ref()).cloned()
283 }
284
285 fn insert(&mut self, path: PathBuf, imported_path: PathBuf) -> Option<PathBuf> {
286 self.0.insert(path, imported_path)
287 }
288}
289
290struct VM<'o, IO> {
291 frames: Vec<Frame>,
295
296 pub(crate) stack: Vec<Value>,
301
302 with_stack: Vec<usize>,
306
307 warnings: Vec<EvalWarning>,
309
310 pub import_cache: ImportCache,
314
315 path_import_cache: PathImportCache,
318
319 source: SourceCode,
322
323 nix_search_path: NixSearchPath,
326
327 io_handle: IO,
330
331 observer: OptionalRuntimeObserver<'o>,
333
334 #[allow(dead_code)]
342 globals: Rc<GlobalsMap>,
343
344 reasonable_span: Span,
350
351 try_eval_frames: Vec<usize>,
371}
372
373impl<'o, IO> VM<'o, IO>
374where
375 IO: AsRef<dyn EvalIO> + 'static,
376{
377 pub fn new(
378 nix_search_path: NixSearchPath,
379 io_handle: IO,
380 observer: OptionalRuntimeObserver<'o>,
381 source: SourceCode,
382 globals: Rc<GlobalsMap>,
383 reasonable_span: Span,
384 ) -> Self {
385 Self {
386 nix_search_path,
387 io_handle,
388 observer,
389 globals,
390 reasonable_span,
391 source,
392 frames: vec![],
393 stack: vec![],
394 with_stack: vec![],
395 warnings: vec![],
396 import_cache: Default::default(),
397 path_import_cache: Default::default(),
398 try_eval_frames: vec![],
399 }
400 }
401
402 fn push_call_frame(&mut self, span: Span, call_frame: CallFrame) {
404 self.frames.push(Frame::CallFrame { span, call_frame })
405 }
406
407 fn execute(mut self) -> EvalResult<RuntimeResult> {
410 while let Some(frame) = self.frames.pop() {
411 self.reasonable_span = frame.span();
412 let frame_id = self.frames.len();
413
414 match frame {
415 Frame::CallFrame { call_frame, span } => {
416 self.observer
417 .observe_enter_call_frame(0, &call_frame.lambda, frame_id);
418
419 match self.execute_bytecode(span, call_frame) {
420 Ok(true) => {
421 self.observer.observe_exit_call_frame(frame_id, &self.stack);
422 }
423 Ok(false) => {
424 self.observer
425 .observe_suspend_call_frame(frame_id, &self.stack);
426 }
427 Err(err) => return Err(err),
428 };
429 }
430
431 Frame::Generator {
434 name,
435 span,
436 state,
437 generator,
438 } => {
439 self.observer
440 .observe_enter_generator(frame_id, name, &self.stack);
441
442 match self.run_generator(name, span, frame_id, state, generator, None) {
443 Ok(true) => {
444 self.observer
445 .observe_exit_generator(frame_id, name, &self.stack)
446 }
447 Ok(false) => {
448 self.observer
449 .observe_suspend_generator(frame_id, name, &self.stack)
450 }
451
452 Err(err) => return Err(err),
453 };
454 }
455 }
456 }
457
458 let value = self
461 .stack
462 .pop()
463 .expect("Snix bug: runtime stack empty after execution");
464 Ok(RuntimeResult {
465 value,
466 warnings: self.warnings,
467 })
468 }
469
470 fn execute_bytecode(&mut self, span: Span, mut frame: CallFrame) -> EvalResult<bool> {
488 loop {
489 let op = frame.inc_ip();
490 self.observer.observe_execute_op(frame.ip, &op, &self.stack);
491
492 match op {
493 Op::ThunkSuspended | Op::ThunkClosure => {
494 let idx = frame.read_uvarint() as usize;
495
496 let blueprint = match &frame.chunk().constants[idx] {
497 Value::Blueprint(lambda) => lambda.clone(),
498 _ => panic!("compiler bug: non-blueprint in blueprint slot"),
499 };
500
501 let upvalues = self.populate_upvalues(&mut frame)?;
502 debug_assert!(
503 upvalues.len() == blueprint.upvalue_count,
504 "TODO: new upvalue count not correct",
505 );
506
507 let thunk = if op == Op::ThunkClosure {
508 debug_assert!(
509 ((upvalues.len() > 0) || (upvalues.with_stack_len() > 0)),
510 "OpThunkClosure should not be called for plain lambdas",
511 );
512 Thunk::new_closure(blueprint)
513 } else {
514 Thunk::new_suspended(blueprint, frame.current_span())
515 };
516
517 self.stack.push(Value::Thunk(thunk.clone()));
518
519 *thunk.upvalues_mut() = upvalues;
525 }
526
527 Op::Force => {
528 if let Some(Value::Thunk(_)) = self.stack.last() {
529 let thunk = match self.stack_pop() {
530 Value::Thunk(t) => t,
531 _ => unreachable!(),
532 };
533
534 let gen_span = frame.current_span();
535
536 self.push_call_frame(span, frame);
537 self.enqueue_generator("force", gen_span, |co| {
538 Thunk::force(thunk, co, gen_span)
539 });
540
541 return Ok(false);
542 }
543 }
544
545 Op::GetUpvalue => {
546 let idx = UpvalueIdx(frame.read_uvarint() as usize);
547 let value = frame.upvalue(idx).clone();
548 self.stack.push(value);
549 }
550
551 Op::Return => {
553 debug_assert!(self.stack.len() - 1 <= frame.stack_offset);
556 return Ok(true);
557 }
558
559 Op::Constant => {
560 let idx = frame.read_uvarint() as usize;
561
562 debug_assert!(
563 idx < frame.chunk().constants.len(),
564 "out of bounds constant at IP {} in {:p}",
565 frame.ip.0,
566 frame.lambda
567 );
568
569 let c = frame.chunk().constants[idx].clone();
570 self.stack.push(c);
571 }
572
573 Op::Call => {
574 let callable = self.stack_pop();
575 self.call_value(frame.current_span(), Some((span, frame)), callable)?;
576
577 return Ok(true);
579 }
580
581 Op::CloseScope => {
584 let count = frame.read_uvarint() as usize;
585 let target_idx = self.stack.len() - 1 - count;
588 self.stack[target_idx] = self.stack_pop();
589
590 for _ in 0..(count - 1) {
592 self.stack.pop();
593 }
594 }
595
596 Op::Closure => {
597 let idx = frame.read_uvarint() as usize;
598 let blueprint = match &frame.chunk().constants[idx] {
599 Value::Blueprint(lambda) => lambda.clone(),
600 _ => panic!("compiler bug: non-blueprint in blueprint slot"),
601 };
602
603 let upvalues = self.populate_upvalues(&mut frame)?;
604 let upvalue_count = upvalues.len();
605 debug_assert!(
606 upvalue_count == blueprint.upvalue_count,
607 "TODO: new upvalue count not correct in closure",
608 );
609
610 debug_assert!(
611 (upvalue_count > 0 || upvalues.with_stack_len() > 0),
612 "OpClosure should not be called for plain lambdas"
613 );
614
615 self.stack
616 .push(Value::Closure(Rc::new(Closure::new_with_upvalues(
617 Rc::new(upvalues),
618 blueprint,
619 ))));
620 }
621
622 Op::AttrsSelect => lifted_pop! {
623 self(key, attrs) => {
624 let key = key.to_str().with_span(&frame, self)?;
625 let attrs = attrs.to_attrs().with_span(&frame, self)?;
626
627 match attrs.select(&key) {
628 Some(value) => self.stack.push(value.clone()),
629
630 None => {
631 return frame.error(
632 self,
633 ErrorKind::AttributeNotFound {
634 name: key.to_str_lossy().into_owned()
635 },
636 );
637 }
638 }
639 }
640 },
641
642 Op::JumpIfFalse => {
643 let offset = frame.read_u16() as usize;
644 debug_assert!(offset != 0);
645 if !self.stack_peek(0).as_bool().with_span(&frame, self)? {
646 frame.ip += offset;
647 }
648 }
649
650 Op::JumpIfCatchable => {
651 let offset = frame.read_u16() as usize;
652 debug_assert!(offset != 0);
653 if self.stack_peek(0).is_catchable() {
654 frame.ip += offset;
655 }
656 }
657
658 Op::JumpIfNoFinaliseRequest => {
659 let offset = frame.read_u16() as usize;
660 debug_assert!(offset != 0);
661 match self.stack_peek(0) {
662 Value::FinaliseRequest(finalise) => {
663 if !finalise {
664 frame.ip += offset;
665 }
666 }
667 val => panic!(
668 "Snix bug: OpJumIfNoFinaliseRequest: expected FinaliseRequest, but got {}",
669 val.type_of()
670 ),
671 }
672 }
673
674 Op::Pop => {
675 self.stack.pop();
676 }
677
678 Op::AttrsTrySelect => {
679 let key = self.stack_pop().to_str().with_span(&frame, self)?;
680 let value = match self.stack_pop() {
681 Value::Attrs(attrs) => match attrs.select(&key) {
682 Some(value) => value.clone(),
683 None => Value::AttrNotFound,
684 },
685
686 _ => Value::AttrNotFound,
687 };
688
689 self.stack.push(value);
690 }
691
692 Op::GetLocal => {
693 let local_idx = frame.read_uvarint() as usize;
694 let idx = frame.stack_offset + local_idx;
695 self.stack.push(self.stack[idx].clone());
696 }
697
698 Op::JumpIfNotFound => {
699 let offset = frame.read_u16() as usize;
700 debug_assert!(offset != 0);
701 if matches!(self.stack_peek(0), Value::AttrNotFound) {
702 self.stack_pop();
703 frame.ip += offset;
704 }
705 }
706
707 Op::Jump => {
708 let offset = frame.read_u16() as usize;
709 debug_assert!(offset != 0);
710 frame.ip += offset;
711 }
712
713 Op::Equal => lifted_pop! {
714 self(b, a) => {
715 let gen_span = frame.current_span();
716 self.push_call_frame(span, frame);
717 self.enqueue_generator("nix_eq", gen_span, |co| {
718 a.nix_eq_owned_genco(b, co, PointerEquality::ForbidAll, gen_span)
719 });
720 return Ok(false);
721 }
722 },
723
724 Op::AssertBool => {
729 let val = self.stack_peek(0);
730 if !val.is_catchable() && !val.is_bool() {
732 return frame.error(
733 self,
734 ErrorKind::TypeError {
735 expected: "bool",
736 actual: val.type_of(),
737 },
738 );
739 }
740 }
741
742 Op::AssertAttrs => {
743 let val = self.stack_peek(0);
744 if !val.is_catchable() && !val.is_attrs() {
746 return frame.error(
747 self,
748 ErrorKind::TypeError {
749 expected: "set",
750 actual: val.type_of(),
751 },
752 );
753 }
754 }
755
756 Op::Attrs => self.run_attrset(frame.read_uvarint() as usize, &frame)?,
757
758 Op::AttrsUpdate => lifted_pop! {
759 self(rhs, lhs) => {
760 let rhs = rhs.to_attrs().with_span(&frame, self)?;
761 let lhs = lhs.to_attrs().with_span(&frame, self)?;
762 self.stack.push(Value::attrs(lhs.update(rhs)))
763 }
764 },
765
766 Op::Invert => lifted_pop! {
767 self(v) => {
768 let v = v.as_bool().with_span(&frame, self)?;
769 self.stack.push(Value::Bool(!v));
770 }
771 },
772
773 Op::List => {
774 let count = frame.read_uvarint() as usize;
775 let list =
776 NixList::construct(count, self.stack.split_off(self.stack.len() - count));
777
778 self.stack.push(Value::List(list));
779 }
780
781 Op::JumpIfTrue => {
782 let offset = frame.read_u16() as usize;
783 debug_assert!(offset != 0);
784 if self.stack_peek(0).as_bool().with_span(&frame, self)? {
785 frame.ip += offset;
786 }
787 }
788
789 Op::HasAttr => lifted_pop! {
790 self(key, attrs) => {
791 let key = key.to_str().with_span(&frame, self)?;
792 let result = match attrs {
793 Value::Attrs(attrs) => attrs.contains(&key),
794
795 _ => false,
798 };
799
800 self.stack.push(Value::Bool(result));
801 }
802 },
803
804 Op::Concat => lifted_pop! {
805 self(rhs, lhs) => {
806 let rhs = rhs.to_list().with_span(&frame, self)?.into_inner();
807 let mut lhs = lhs.to_list().with_span(&frame, self)?.into_inner();
808 lhs.extend(rhs.into_iter());
809 self.stack.push(Value::List(lhs.into()))
810 }
811 },
812
813 Op::ResolveWith => {
814 let ident = self.stack_pop().to_str().with_span(&frame, self)?;
815
816 let op_span = frame.current_span();
818 self.push_call_frame(span, frame);
819
820 let with_stack_len = self.with_stack.len();
823 let closed_with_stack_len = self
824 .last_call_frame()
825 .map(|frame| frame.upvalues.with_stack_len())
826 .unwrap_or(0);
827
828 self.enqueue_generator("resolve_with", op_span, |co| {
829 resolve_with(co, ident.into(), with_stack_len, closed_with_stack_len)
830 });
831
832 return Ok(false);
833 }
834
835 Op::Finalise => {
836 let idx = frame.read_uvarint() as usize;
837 match &self.stack[frame.stack_offset + idx] {
838 Value::Closure(_) => panic!("attempted to finalise a closure"),
839 Value::Thunk(thunk) => thunk.finalise(&self.stack[frame.stack_offset..]),
840 _ => panic!("attempted to finalise a non-thunk"),
841 }
842 }
843
844 Op::CoerceToString => {
845 let kind: CoercionKind = frame.chunk().code[frame.ip.0].into();
846 frame.ip.0 += 1;
847
848 let value = self.stack_pop();
849 let gen_span = frame.current_span();
850 self.push_call_frame(span, frame);
851
852 self.enqueue_generator("coerce_to_string", gen_span, |co| {
853 value.coerce_to_string(co, kind, gen_span)
854 });
855
856 return Ok(false);
857 }
858
859 Op::Interpolate => self.run_interpolate(frame.read_uvarint(), &frame)?,
860
861 Op::ValidateClosedFormals => {
862 let formals = frame.lambda.formals.as_ref().expect(
863 "OpValidateClosedFormals called within the frame of a lambda without formals",
864 );
865
866 let peeked = self.stack_peek(0);
867 if peeked.is_catchable() {
868 continue;
869 }
870
871 let args = peeked.to_attrs().with_span(&frame, self)?;
872 for arg in args.keys() {
873 if !formals.contains(arg) {
874 return frame.error(
875 self,
876 ErrorKind::UnexpectedArgumentFormals {
877 arg: arg.clone(),
878 formals_span: formals.span,
879 },
880 );
881 }
882 }
883 }
884
885 Op::Add => lifted_pop! {
886 self(b, a) => {
887 let gen_span = frame.current_span();
888 self.push_call_frame(span, frame);
889
890 self.enqueue_generator("add_values", gen_span, |co| add_values(co, a, b));
894 return Ok(false);
895 }
896 },
897
898 Op::Sub => lifted_pop! {
899 self(b, a) => {
900 let result = arithmetic_op!(&a, &b, -).with_span(&frame, self)?;
901 self.stack.push(result);
902 }
903 },
904
905 Op::Mul => lifted_pop! {
906 self(b, a) => {
907 let result = arithmetic_op!(&a, &b, *).with_span(&frame, self)?;
908 self.stack.push(result);
909 }
910 },
911
912 Op::Div => lifted_pop! {
913 self(b, a) => {
914 match b {
915 Value::Integer(0) => return frame.error(self, ErrorKind::DivisionByZero),
916 Value::Float(0.0_f64) => {
917 return frame.error(self, ErrorKind::DivisionByZero)
918 }
919 _ => {}
920 };
921
922 let result = arithmetic_op!(&a, &b, /).with_span(&frame, self)?;
923 self.stack.push(result);
924 }
925 },
926
927 Op::Negate => match self.stack_pop() {
928 Value::Integer(i) => self.stack.push(Value::Integer(-i)),
929 Value::Float(f) => self.stack.push(Value::Float(-f)),
930 Value::Catchable(cex) => self.stack.push(Value::Catchable(cex)),
931 v => {
932 return frame.error(
933 self,
934 ErrorKind::TypeError {
935 expected: "number (either int or float)",
936 actual: v.type_of(),
937 },
938 );
939 }
940 },
941
942 Op::Less => cmp_op!(self, frame, span, <),
943 Op::LessOrEq => cmp_op!(self, frame, span, <=),
944 Op::More => cmp_op!(self, frame, span, >),
945 Op::MoreOrEq => cmp_op!(self, frame, span, >=),
946
947 Op::FindFile => match self.stack_pop() {
948 Value::UnresolvedPath(path) => {
949 let resolved = self
950 .nix_search_path
951 .resolve(&self.io_handle, *path)
952 .with_span(&frame, self)?;
953 self.stack.push(resolved.into());
954 }
955
956 _ => panic!("Snix bug: OpFindFile called on non-UnresolvedPath"),
957 },
958
959 Op::ResolveHomePath => match self.stack_pop() {
960 Value::UnresolvedPath(path) => {
961 let home_dir = self
963 .io_handle
964 .as_ref()
965 .get_env(OsStr::new("HOME"))
966 .and_then(|h| if h.is_empty() { None } else { Some(h) })
967 .map(PathBuf::from);
968
969 match home_dir {
970 None => {
971 return frame.error(
972 self,
973 ErrorKind::RelativePathResolution(
974 "failed to determine home directory".into(),
975 ),
976 );
977 }
978 Some(mut buf) => {
979 buf.push(*path);
980 self.stack.push(buf.into());
981 }
982 };
983 }
984
985 _ => {
986 panic!("Snix bug: OpResolveHomePath called on non-UnresolvedPath")
987 }
988 },
989
990 Op::InterpolatePath => self.run_interpolate_path(frame.read_uvarint(), &frame)?,
991
992 Op::PushWith => self
993 .with_stack
994 .push(frame.stack_offset + frame.read_uvarint() as usize),
995
996 Op::PopWith => {
997 self.with_stack.pop();
998 }
999
1000 Op::AssertFail => {
1001 self.stack
1002 .push(Value::from(CatchableErrorKind::AssertionFailed));
1003 }
1004
1005 Op::Invalid => {
1008 panic!("Snix bug: attempted to execute invalid opcode")
1009 }
1010 }
1011 }
1012 }
1013}
1014
1015impl<IO> VM<'_, IO>
1017where
1018 IO: AsRef<dyn EvalIO> + 'static,
1019{
1020 pub(crate) fn stack_pop(&mut self) -> Value {
1021 self.stack.pop().expect("runtime stack empty")
1022 }
1023
1024 fn stack_peek(&self, offset: usize) -> &Value {
1025 &self.stack[self.stack.len() - 1 - offset]
1026 }
1027
1028 fn run_attrset(&mut self, count: usize, frame: &CallFrame) -> EvalResult<()> {
1029 let attrs = NixAttrs::construct(count, self.stack.split_off(self.stack.len() - count * 2))
1030 .with_span(frame, self)?
1031 .map(Value::attrs)
1032 .into();
1033
1034 self.stack.push(attrs);
1035 Ok(())
1036 }
1037
1038 fn last_call_frame(&self) -> Option<&CallFrame> {
1040 for frame in self.frames.iter().rev() {
1041 if let Frame::CallFrame { call_frame, .. } = frame {
1042 return Some(call_frame);
1043 }
1044 }
1045
1046 None
1047 }
1048
1049 pub fn push_warning(&mut self, warning: EvalWarning) {
1051 self.warnings.push(warning);
1052 }
1053
1054 pub fn emit_warning(&mut self, kind: WarningKind) {
1057 self.push_warning(EvalWarning {
1058 kind,
1059 span: self.get_span(),
1060 });
1061 }
1062
1063 fn run_interpolate(&mut self, count: u64, frame: &CallFrame) -> EvalResult<()> {
1067 let mut out = BString::default();
1068 let mut context: NixContext = NixContext::new();
1070
1071 for i in 0..count {
1072 let val = self.stack_pop();
1073 if val.is_catchable() {
1074 for _ in (i + 1)..count {
1075 self.stack.pop();
1076 }
1077 self.stack.push(val);
1078 return Ok(());
1079 }
1080 let mut nix_string = val.to_contextful_str().with_span(frame, self)?;
1081 out.push_str(nix_string.as_bstr());
1082 if let Some(nix_string_ctx) = nix_string.take_context() {
1083 context.extend(nix_string_ctx.into_iter())
1084 }
1085 }
1086
1087 self.stack
1088 .push(Value::String(NixString::new_context_from(context, out)));
1089 Ok(())
1090 }
1091
1092 fn run_interpolate_path(&mut self, count: u64, frame: &CallFrame) -> EvalResult<()> {
1096 let mut path_str = String::new();
1098
1099 for i in 0..count {
1100 let val = self.stack_pop();
1101 if val.is_catchable() {
1102 for _ in (i + 1)..count {
1104 self.stack.pop();
1105 }
1106 self.stack.push(val);
1107 return Ok(());
1108 }
1109
1110 match val {
1112 Value::String(s) => {
1113 path_str.push_str(&s.as_bytes().to_str_lossy());
1114 }
1115 Value::Path(p) => {
1116 path_str.push_str(&p.to_string_lossy());
1117 }
1118 _ => {
1119 return frame.error(
1120 self,
1121 ErrorKind::TypeError {
1122 expected: "string or path",
1123 actual: val.type_of(),
1124 },
1125 );
1126 }
1127 }
1128 }
1129
1130 let path = canon_path(PathBuf::from(path_str));
1132 self.stack.push(Value::Path(Box::new(path)));
1133
1134 Ok(())
1135 }
1136
1137 fn call_builtin(&mut self, span: Span, mut builtin: Builtin) -> EvalResult<()> {
1145 let builtin_name = builtin.name();
1146 self.observer.observe_enter_builtin(builtin_name);
1147
1148 builtin.apply_arg(self.stack_pop());
1149
1150 match builtin.call() {
1151 BuiltinResult::Partial(partial) => self.stack.push(Value::Builtin(partial)),
1153
1154 BuiltinResult::Called(name, generator) => self.frames.push(Frame::Generator {
1156 generator,
1157 span,
1158 name,
1159 state: GeneratorState::Running,
1160 }),
1161 }
1162
1163 Ok(())
1164 }
1165
1166 fn call_value(
1167 &mut self,
1168 span: Span,
1169 parent: Option<(Span, CallFrame)>,
1170 callable: Value,
1171 ) -> EvalResult<()> {
1172 match callable {
1173 Value::Builtin(builtin) => self.call_builtin(span, builtin),
1174 Value::Thunk(thunk) => self.call_value(span, parent, thunk.value().clone()),
1175
1176 Value::Closure(closure) => {
1177 let lambda = closure.lambda();
1178 self.observer.observe_tail_call(self.frames.len(), &lambda);
1179
1180 let stack_offset = self.stack.len() - 1;
1185
1186 if let Some((parent_span, parent_frame)) = parent {
1190 self.push_call_frame(parent_span, parent_frame);
1191 }
1192
1193 self.push_call_frame(
1194 span,
1195 CallFrame {
1196 lambda,
1197 upvalues: closure.upvalues(),
1198 ip: CodeIdx(0),
1199 stack_offset,
1200 },
1201 );
1202
1203 Ok(())
1204 }
1205
1206 val @ Value::Attrs(_) => {
1208 if let Some((parent_span, parent_frame)) = parent {
1209 self.push_call_frame(parent_span, parent_frame);
1210 }
1211
1212 self.enqueue_generator("__functor call", span, |co| call_functor(co, val));
1213 Ok(())
1214 }
1215
1216 val @ Value::Catchable(_) => {
1217 self.stack.pop();
1219 self.stack.push(val);
1222 Ok(())
1223 }
1224
1225 v => Err(ErrorKind::NotCallable(v.type_of())).with_span(span, self),
1226 }
1227 }
1228
1229 fn populate_upvalues(&self, frame: &mut CallFrame) -> EvalResult<Upvalues> {
1234 let data = UpvalueData::from_raw(frame.read_uvarint());
1235 let count = data.count();
1236 let capture_with = data.captures_with();
1237
1238 let mut static_upvalues = vec![];
1239
1240 let with_stack = if capture_with {
1241 let mut captured_with_stack = frame.upvalues.with_stack().clone();
1244 captured_with_stack.reserve_exact(self.with_stack.len());
1246
1247 for idx in &self.with_stack {
1248 captured_with_stack.push(self.stack[*idx].clone());
1249 }
1250
1251 captured_with_stack
1252 } else {
1253 vec![]
1254 };
1255
1256 for _ in 0..count {
1257 let pos = Position(frame.read_uvarint());
1258
1259 if let Some(stack_idx) = pos.runtime_stack_index() {
1260 let idx = frame.stack_offset + stack_idx.0;
1261
1262 let val = match self.stack.get(idx) {
1263 Some(val) => val.clone(),
1264 None => {
1265 return frame.error(
1267 self,
1268 ErrorKind::SnixBug {
1269 msg: "upvalue to be captured was missing on stack",
1270 metadata: Some(Rc::new(json!({
1271 "ip": format!("{:#x}", frame.ip.0 - 1),
1272 "stack_idx(relative)": stack_idx.0,
1273 "stack_idx(absolute)": idx,
1274 }))),
1275 },
1276 );
1277 }
1278 };
1279
1280 static_upvalues.push(val);
1281 } else if let Some(idx) = pos.runtime_deferred_local() {
1282 static_upvalues.push(Value::DeferredUpvalue(idx));
1283 } else if let Some(idx) = pos.runtime_upvalue_index() {
1284 static_upvalues.push(frame.upvalue(idx).clone());
1285 } else {
1286 panic!("Snix bug: invalid capture position emitted")
1287 }
1288 }
1289
1290 Ok(Upvalues::from_raw_parts(static_upvalues, with_stack))
1291 }
1292}
1293
1294async fn resolve_with(
1298 co: GenCo,
1299 ident: BString,
1300 vm_with_len: usize,
1301 upvalue_with_len: usize,
1302) -> Result<Value, ErrorKind> {
1303 async fn fetch_forced_with(co: &GenCo, idx: usize) -> Value {
1305 match co.yield_(VMRequest::WithValue(idx)).await {
1306 VMResponse::Value(value) => value,
1307 msg => panic!("Snix bug: VM responded with incorrect generator message: {msg}"),
1308 }
1309 }
1310
1311 async fn fetch_captured_with(co: &GenCo, idx: usize) -> Value {
1313 match co.yield_(VMRequest::CapturedWithValue(idx)).await {
1314 VMResponse::Value(value) => value,
1315 msg => panic!("Snix bug: VM responded with incorrect generator message: {msg}"),
1316 }
1317 }
1318
1319 for with_stack_idx in (0..vm_with_len).rev() {
1320 let with = fetch_forced_with(&co, with_stack_idx).await;
1322
1323 if with.is_catchable() {
1324 return Ok(with);
1325 }
1326
1327 match with.to_attrs()?.select(&ident) {
1328 None => continue,
1329 Some(val) => return Ok(val.clone()),
1330 }
1331 }
1332
1333 for upvalue_with_idx in (0..upvalue_with_len).rev() {
1334 let with = fetch_captured_with(&co, upvalue_with_idx).await;
1335
1336 if with.is_catchable() {
1337 return Ok(with);
1338 }
1339
1340 match with.to_attrs()?.select(&ident) {
1341 None => continue,
1342 Some(val) => return Ok(val.clone()),
1343 }
1344 }
1345
1346 Err(ErrorKind::UnknownDynamicVariable(ident.to_string()))
1347}
1348
1349async fn add_values(co: GenCo, a: Value, b: Value) -> Result<Value, ErrorKind> {
1351 let result = match (a, b) {
1353 (Value::Path(p), v) => {
1354 let mut path = p.into_os_string();
1355 match generators::request_string_coerce(
1356 &co,
1357 v,
1358 CoercionKind {
1359 strong: false,
1360
1361 import_paths: false,
1368 },
1372 )
1373 .await
1374 {
1375 Ok(vs) => {
1376 path.push(vs.to_os_str()?);
1377 crate::value::canon_path(PathBuf::from(path)).into()
1378 }
1379 Err(c) => Value::Catchable(Box::new(c)),
1380 }
1381 }
1382 (Value::String(s1), Value::String(s2)) => Value::String(s1.concat(&s2)),
1383 (Value::String(s1), v) => generators::request_string_coerce(
1384 &co,
1385 v,
1386 CoercionKind {
1387 strong: false,
1388 import_paths: true,
1390 },
1391 )
1392 .await
1393 .map(|s2| Value::String(s1.concat(&s2)))
1394 .into(),
1395 (a @ Value::Integer(_), b) | (a @ Value::Float(_), b) => arithmetic_op!(&a, &b, +)?,
1396 (a, b) => {
1397 let r1 = generators::request_string_coerce(
1398 &co,
1399 a,
1400 CoercionKind {
1401 strong: false,
1402 import_paths: false,
1403 },
1404 )
1405 .await;
1406 let r2 = generators::request_string_coerce(
1407 &co,
1408 b,
1409 CoercionKind {
1410 strong: false,
1411 import_paths: false,
1412 },
1413 )
1414 .await;
1415 match (r1, r2) {
1416 (Ok(s1), Ok(s2)) => Value::String(s1.concat(&s2)),
1417 (Err(c), _) => return Ok(Value::from(c)),
1418 (_, Err(c)) => return Ok(Value::from(c)),
1419 }
1420 }
1421 };
1422
1423 Ok(result)
1424}
1425
1426pub struct RuntimeResult {
1428 pub value: Value,
1429 pub warnings: Vec<EvalWarning>,
1430}
1431
1432async fn final_deep_force(co: GenCo) -> Result<Value, ErrorKind> {
1436 let value = generators::request_stack_pop(&co).await;
1437 Ok(generators::request_deep_force(&co, value).await)
1438}
1439
1440#[derive(Debug, Clone, Copy, Default)]
1442pub enum EvalMode {
1443 #[default]
1446 Lazy,
1447
1448 Strict,
1450}
1451
1452pub fn run_lambda<IO>(
1453 nix_search_path: NixSearchPath,
1454 io_handle: IO,
1455 observer: Option<&mut dyn RuntimeObserver>,
1456 source: SourceCode,
1457 globals: Rc<GlobalsMap>,
1458 lambda: Rc<Lambda>,
1459 mode: EvalMode,
1460) -> EvalResult<RuntimeResult>
1461where
1462 IO: AsRef<dyn EvalIO> + 'static,
1463{
1464 let root_span = lambda.chunk.get_span(CodeIdx(lambda.chunk.code.len() - 1));
1471
1472 let mut vm = VM::new(
1473 nix_search_path,
1474 io_handle,
1475 OptionalRuntimeObserver(observer),
1476 source,
1477 globals,
1478 root_span,
1479 );
1480
1481 match mode {
1484 EvalMode::Lazy => {}
1485 EvalMode::Strict => vm.enqueue_generator("final_deep_force", root_span, final_deep_force),
1486 }
1487
1488 vm.frames.push(Frame::CallFrame {
1489 span: root_span,
1490 call_frame: CallFrame {
1491 lambda,
1492 upvalues: Rc::new(Upvalues::with_capacity(0)),
1493 ip: CodeIdx(0),
1494 stack_offset: 0,
1495 },
1496 });
1497
1498 vm.execute()
1499}