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 &BytecodeFrame {
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::BytecodeFrame { 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 BytecodeFrame {
125 lambda: Rc<Lambda>,
127
128 upvalues: Rc<Upvalues>,
131
132 ip: CodeIdx,
135
136 stack_offset: usize,
138}
139
140impl BytecodeFrame {
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 BytecodeFrame {
204 bytecode_frame: BytecodeFrame,
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::BytecodeFrame { 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_bytecode_frame(&mut self, span: Span, bytecode_frame: BytecodeFrame) {
404 self.frames.push(Frame::BytecodeFrame {
405 span,
406 bytecode_frame,
407 })
408 }
409
410 fn execute(mut self) -> EvalResult<RuntimeResult> {
413 while let Some(frame) = self.frames.pop() {
414 self.reasonable_span = frame.span();
415 let frame_id = self.frames.len();
416
417 match frame {
418 Frame::BytecodeFrame {
419 bytecode_frame,
420 span,
421 } => {
422 self.observer
423 .observe_enter_bytecode_frame(0, &bytecode_frame.lambda, frame_id);
424
425 match self.execute_bytecode(span, bytecode_frame) {
426 Ok(true) => {
427 self.observer
428 .observe_exit_bytecode_frame(frame_id, &self.stack);
429 }
430 Ok(false) => {
431 self.observer
432 .observe_suspend_bytecode_frame(frame_id, &self.stack);
433 }
434 Err(err) => return Err(err),
435 };
436 }
437
438 Frame::Generator {
441 name,
442 span,
443 state,
444 generator,
445 } => {
446 self.observer
447 .observe_enter_generator(frame_id, name, &self.stack);
448
449 match self.run_generator(name, span, frame_id, state, generator, None) {
450 Ok(true) => {
451 self.observer
452 .observe_exit_generator(frame_id, name, &self.stack)
453 }
454 Ok(false) => {
455 self.observer
456 .observe_suspend_generator(frame_id, name, &self.stack)
457 }
458
459 Err(err) => return Err(err),
460 };
461 }
462 }
463 }
464
465 let value = self
468 .stack
469 .pop()
470 .expect("Snix bug: runtime stack empty after execution");
471 Ok(RuntimeResult {
472 value,
473 warnings: self.warnings,
474 })
475 }
476
477 fn execute_bytecode(&mut self, span: Span, mut frame: BytecodeFrame) -> EvalResult<bool> {
495 loop {
496 let op = frame.inc_ip();
497 self.observer.observe_execute_op(frame.ip, &op, &self.stack);
498
499 match op {
500 Op::ThunkSuspended | Op::ThunkClosure => {
501 let idx = frame.read_uvarint() as usize;
502
503 let blueprint = match &frame.chunk().constants[idx] {
504 Value::Blueprint(lambda) => lambda.clone(),
505 _ => panic!("compiler bug: non-blueprint in blueprint slot"),
506 };
507
508 let upvalues = self.populate_upvalues(&mut frame)?;
509 debug_assert!(
510 upvalues.len() == blueprint.upvalue_count,
511 "TODO: new upvalue count not correct",
512 );
513
514 let thunk = if op == Op::ThunkClosure {
515 debug_assert!(
516 ((upvalues.len() > 0) || (upvalues.with_stack_len() > 0)),
517 "OpThunkClosure should not be called for plain lambdas",
518 );
519 Thunk::new_closure(blueprint)
520 } else {
521 Thunk::new_suspended(blueprint, frame.current_span())
522 };
523
524 self.stack.push(Value::Thunk(thunk.clone()));
525
526 *thunk.upvalues_mut() = upvalues;
532 }
533
534 Op::Force => {
535 if let Some(Value::Thunk(_)) = self.stack.last() {
536 let thunk = match self.stack_pop() {
537 Value::Thunk(t) => t,
538 _ => unreachable!(),
539 };
540
541 let gen_span = frame.current_span();
542
543 self.push_bytecode_frame(span, frame);
544 self.enqueue_generator("force", gen_span, |co| {
545 Thunk::force(thunk, co, gen_span)
546 });
547
548 return Ok(false);
549 }
550 }
551
552 Op::GetUpvalue => {
553 let idx = UpvalueIdx(frame.read_uvarint() as usize);
554 let value = frame.upvalue(idx).clone();
555 self.stack.push(value);
556 }
557
558 Op::Return => {
560 debug_assert!(self.stack.len() - 1 <= frame.stack_offset);
563 return Ok(true);
564 }
565
566 Op::Constant => {
567 let idx = frame.read_uvarint() as usize;
568
569 debug_assert!(
570 idx < frame.chunk().constants.len(),
571 "out of bounds constant at IP {} in {:p}",
572 frame.ip.0,
573 frame.lambda
574 );
575
576 let c = frame.chunk().constants[idx].clone();
577 self.stack.push(c);
578 }
579
580 Op::Call => {
581 let callable = self.stack_pop();
582 self.call_value(frame.current_span(), Some((span, frame)), callable)?;
583
584 return Ok(true);
586 }
587
588 Op::CloseScope => {
591 let count = frame.read_uvarint() as usize;
592 let target_idx = self.stack.len() - 1 - count;
595 self.stack[target_idx] = self.stack_pop();
596
597 for _ in 0..(count - 1) {
599 self.stack.pop();
600 }
601 }
602
603 Op::Closure => {
604 let idx = frame.read_uvarint() as usize;
605 let blueprint = match &frame.chunk().constants[idx] {
606 Value::Blueprint(lambda) => lambda.clone(),
607 _ => panic!("compiler bug: non-blueprint in blueprint slot"),
608 };
609
610 let upvalues = self.populate_upvalues(&mut frame)?;
611 let upvalue_count = upvalues.len();
612 debug_assert!(
613 upvalue_count == blueprint.upvalue_count,
614 "TODO: new upvalue count not correct in closure",
615 );
616
617 debug_assert!(
618 (upvalue_count > 0 || upvalues.with_stack_len() > 0),
619 "OpClosure should not be called for plain lambdas"
620 );
621
622 self.stack
623 .push(Value::Closure(Rc::new(Closure::new_with_upvalues(
624 Rc::new(upvalues),
625 blueprint,
626 ))));
627 }
628
629 Op::AttrsSelect => lifted_pop! {
630 self(key, attrs) => {
631 let key = key.to_str().with_span(&frame, self)?;
632 let attrs = attrs.to_attrs().with_span(&frame, self)?;
633
634 match attrs.select(&key) {
635 Some(value) => self.stack.push(value.clone()),
636
637 None => {
638 return frame.error(
639 self,
640 ErrorKind::AttributeNotFound {
641 name: key.to_str_lossy().into_owned()
642 },
643 );
644 }
645 }
646 }
647 },
648
649 Op::JumpIfFalse => {
650 let offset = frame.read_u16() as usize;
651 debug_assert!(offset != 0);
652 if !self.stack_peek(0).as_bool().with_span(&frame, self)? {
653 frame.ip += offset;
654 }
655 }
656
657 Op::JumpIfCatchable => {
658 let offset = frame.read_u16() as usize;
659 debug_assert!(offset != 0);
660 if self.stack_peek(0).is_catchable() {
661 frame.ip += offset;
662 }
663 }
664
665 Op::JumpIfNoFinaliseRequest => {
666 let offset = frame.read_u16() as usize;
667 debug_assert!(offset != 0);
668 match self.stack_peek(0) {
669 Value::FinaliseRequest(finalise) => {
670 if !finalise {
671 frame.ip += offset;
672 }
673 }
674 val => panic!(
675 "Snix bug: OpJumIfNoFinaliseRequest: expected FinaliseRequest, but got {}",
676 val.type_of()
677 ),
678 }
679 }
680
681 Op::Pop => {
682 self.stack.pop();
683 }
684
685 Op::AttrsTrySelect => {
686 let key = self.stack_pop().to_str().with_span(&frame, self)?;
687 let value = match self.stack_pop() {
688 Value::Attrs(attrs) => match attrs.select(&key) {
689 Some(value) => value.clone(),
690 None => Value::AttrNotFound,
691 },
692
693 _ => Value::AttrNotFound,
694 };
695
696 self.stack.push(value);
697 }
698
699 Op::GetLocal => {
700 let local_idx = frame.read_uvarint() as usize;
701 let idx = frame.stack_offset + local_idx;
702 self.stack.push(self.stack[idx].clone());
703 }
704
705 Op::JumpIfNotFound => {
706 let offset = frame.read_u16() as usize;
707 debug_assert!(offset != 0);
708 if matches!(self.stack_peek(0), Value::AttrNotFound) {
709 self.stack_pop();
710 frame.ip += offset;
711 }
712 }
713
714 Op::Jump => {
715 let offset = frame.read_u16() as usize;
716 debug_assert!(offset != 0);
717 frame.ip += offset;
718 }
719
720 Op::Equal => lifted_pop! {
721 self(b, a) => {
722 let gen_span = frame.current_span();
723 self.push_bytecode_frame(span, frame);
724 self.enqueue_generator("nix_eq", gen_span, |co| {
725 a.nix_eq_owned_genco(b, co, PointerEquality::ForbidAll, gen_span)
726 });
727 return Ok(false);
728 }
729 },
730
731 Op::AssertBool => {
736 let val = self.stack_peek(0);
737 if !val.is_catchable() && !val.is_bool() {
739 return frame.error(
740 self,
741 ErrorKind::TypeError {
742 expected: "bool",
743 actual: val.type_of(),
744 },
745 );
746 }
747 }
748
749 Op::AssertAttrs => {
750 let val = self.stack_peek(0);
751 if !val.is_catchable() && !val.is_attrs() {
753 return frame.error(
754 self,
755 ErrorKind::TypeError {
756 expected: "set",
757 actual: val.type_of(),
758 },
759 );
760 }
761 }
762
763 Op::Attrs => self.run_attrset(frame.read_uvarint() as usize, &frame)?,
764
765 Op::AttrsUpdate => lifted_pop! {
766 self(rhs, lhs) => {
767 let rhs = rhs.to_attrs().with_span(&frame, self)?;
768 let lhs = lhs.to_attrs().with_span(&frame, self)?;
769 self.stack.push(Value::attrs(lhs.update(rhs)))
770 }
771 },
772
773 Op::Invert => lifted_pop! {
774 self(v) => {
775 let v = v.as_bool().with_span(&frame, self)?;
776 self.stack.push(Value::Bool(!v));
777 }
778 },
779
780 Op::List => {
781 let count = frame.read_uvarint() as usize;
782 let list =
783 NixList::construct(count, self.stack.split_off(self.stack.len() - count));
784
785 self.stack.push(Value::List(list));
786 }
787
788 Op::JumpIfTrue => {
789 let offset = frame.read_u16() as usize;
790 debug_assert!(offset != 0);
791 if self.stack_peek(0).as_bool().with_span(&frame, self)? {
792 frame.ip += offset;
793 }
794 }
795
796 Op::HasAttr => lifted_pop! {
797 self(key, attrs) => {
798 let key = key.to_str().with_span(&frame, self)?;
799 let result = match attrs {
800 Value::Attrs(attrs) => attrs.contains(&key),
801
802 _ => false,
805 };
806
807 self.stack.push(Value::Bool(result));
808 }
809 },
810
811 Op::Concat => lifted_pop! {
812 self(rhs, lhs) => {
813 let rhs = rhs.to_list().with_span(&frame, self)?.into_inner();
814 let mut lhs = lhs.to_list().with_span(&frame, self)?.into_inner();
815 lhs.extend(rhs.into_iter());
816 self.stack.push(Value::List(lhs.into()))
817 }
818 },
819
820 Op::ResolveWith => {
821 let ident = self.stack_pop().to_str().with_span(&frame, self)?;
822
823 let op_span = frame.current_span();
825 self.push_bytecode_frame(span, frame);
826
827 let with_stack_len = self.with_stack.len();
830 let closed_with_stack_len = self
831 .last_bytecode_frame()
832 .map(|frame| frame.upvalues.with_stack_len())
833 .unwrap_or(0);
834
835 self.enqueue_generator("resolve_with", op_span, |co| {
836 resolve_with(co, ident.into(), with_stack_len, closed_with_stack_len)
837 });
838
839 return Ok(false);
840 }
841
842 Op::Finalise => {
843 let idx = frame.read_uvarint() as usize;
844 match &self.stack[frame.stack_offset + idx] {
845 Value::Closure(_) => panic!("attempted to finalise a closure"),
846 Value::Thunk(thunk) => thunk.finalise(&self.stack[frame.stack_offset..]),
847 _ => panic!("attempted to finalise a non-thunk"),
848 }
849 }
850
851 Op::CoerceToString => {
852 let kind: CoercionKind = frame.chunk().code[frame.ip.0].into();
853 frame.ip.0 += 1;
854
855 let value = self.stack_pop();
856 let gen_span = frame.current_span();
857 self.push_bytecode_frame(span, frame);
858
859 self.enqueue_generator("coerce_to_string", gen_span, |co| {
860 value.coerce_to_string(co, kind, gen_span)
861 });
862
863 return Ok(false);
864 }
865
866 Op::Interpolate => self.run_interpolate(frame.read_uvarint(), &frame)?,
867
868 Op::ValidateClosedFormals => {
869 let formals = frame.lambda.formals.as_ref().expect(
870 "OpValidateClosedFormals called within the frame of a lambda without formals",
871 );
872
873 let peeked = self.stack_peek(0);
874 if peeked.is_catchable() {
875 continue;
876 }
877
878 let args = peeked.to_attrs().with_span(&frame, self)?;
879 for arg in args.keys() {
880 if !formals.contains(arg) {
881 return frame.error(
882 self,
883 ErrorKind::UnexpectedArgumentFormals {
884 arg: arg.clone(),
885 formals_span: formals.span,
886 },
887 );
888 }
889 }
890 }
891
892 Op::Add => lifted_pop! {
893 self(b, a) => {
894 let gen_span = frame.current_span();
895 self.push_bytecode_frame(span, frame);
896
897 self.enqueue_generator("add_values", gen_span, |co| add_values(co, a, b));
901 return Ok(false);
902 }
903 },
904
905 Op::Sub => 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::Mul => lifted_pop! {
913 self(b, a) => {
914 let result = arithmetic_op!(&a, &b, *).with_span(&frame, self)?;
915 self.stack.push(result);
916 }
917 },
918
919 Op::Div => lifted_pop! {
920 self(b, a) => {
921 match b {
922 Value::Integer(0) => return frame.error(self, ErrorKind::DivisionByZero),
923 Value::Float(0.0_f64) => {
924 return frame.error(self, ErrorKind::DivisionByZero)
925 }
926 _ => {}
927 };
928
929 let result = arithmetic_op!(&a, &b, /).with_span(&frame, self)?;
930 self.stack.push(result);
931 }
932 },
933
934 Op::Negate => match self.stack_pop() {
935 Value::Integer(i) => self.stack.push(Value::Integer(-i)),
936 Value::Float(f) => self.stack.push(Value::Float(-f)),
937 Value::Catchable(cex) => self.stack.push(Value::Catchable(cex)),
938 v => {
939 return frame.error(
940 self,
941 ErrorKind::TypeError {
942 expected: "number (either int or float)",
943 actual: v.type_of(),
944 },
945 );
946 }
947 },
948
949 Op::Less => cmp_op!(self, frame, span, <),
950 Op::LessOrEq => cmp_op!(self, frame, span, <=),
951 Op::More => cmp_op!(self, frame, span, >),
952 Op::MoreOrEq => cmp_op!(self, frame, span, >=),
953
954 Op::FindFile => match self.stack_pop() {
955 Value::UnresolvedPath(path) => {
956 let resolved = self
957 .nix_search_path
958 .resolve(&self.io_handle, *path)
959 .with_span(&frame, self)?;
960 self.stack.push(resolved.into());
961 }
962
963 _ => panic!("Snix bug: OpFindFile called on non-UnresolvedPath"),
964 },
965
966 Op::ResolveHomePath => match self.stack_pop() {
967 Value::UnresolvedPath(path) => {
968 let home_dir = self
970 .io_handle
971 .as_ref()
972 .get_env(OsStr::new("HOME"))
973 .filter(|h| !h.is_empty())
974 .map(PathBuf::from);
975
976 match home_dir {
977 None => {
978 return frame.error(
979 self,
980 ErrorKind::RelativePathResolution(
981 "failed to determine home directory".into(),
982 ),
983 );
984 }
985 Some(mut buf) => {
986 buf.push(*path);
987 self.stack.push(buf.into());
988 }
989 };
990 }
991
992 _ => {
993 panic!("Snix bug: OpResolveHomePath called on non-UnresolvedPath")
994 }
995 },
996
997 Op::InterpolatePath => self.run_interpolate_path(frame.read_uvarint(), &frame)?,
998
999 Op::PushWith => self
1000 .with_stack
1001 .push(frame.stack_offset + frame.read_uvarint() as usize),
1002
1003 Op::PopWith => {
1004 self.with_stack.pop();
1005 }
1006
1007 Op::AssertFail => {
1008 self.stack
1009 .push(Value::from(CatchableErrorKind::AssertionFailed));
1010 }
1011
1012 Op::Invalid => {
1015 panic!("Snix bug: attempted to execute invalid opcode")
1016 }
1017 }
1018 }
1019 }
1020}
1021
1022impl<IO> VM<'_, IO>
1024where
1025 IO: AsRef<dyn EvalIO> + 'static,
1026{
1027 pub(crate) fn stack_pop(&mut self) -> Value {
1028 self.stack.pop().expect("runtime stack empty")
1029 }
1030
1031 fn stack_peek(&self, offset: usize) -> &Value {
1032 &self.stack[self.stack.len() - 1 - offset]
1033 }
1034
1035 fn run_attrset(&mut self, count: usize, frame: &BytecodeFrame) -> EvalResult<()> {
1036 let attrs = NixAttrs::construct(
1037 count,
1038 self.stack.split_off(self.stack.len() - count * 2),
1039 &frame.chunk().attrsets_pos_spans,
1040 )
1041 .with_span(frame, self)?
1042 .map(Value::attrs)
1043 .into();
1044
1045 self.stack.push(attrs);
1046 Ok(())
1047 }
1048
1049 fn last_bytecode_frame(&self) -> Option<&BytecodeFrame> {
1051 for frame in self.frames.iter().rev() {
1052 if let Frame::BytecodeFrame { bytecode_frame, .. } = frame {
1053 return Some(bytecode_frame);
1054 }
1055 }
1056
1057 None
1058 }
1059
1060 pub fn push_warning(&mut self, warning: EvalWarning) {
1062 self.warnings.push(warning);
1063 }
1064
1065 pub fn emit_warning(&mut self, kind: WarningKind) {
1068 self.push_warning(EvalWarning {
1069 kind,
1070 span: self.get_span(),
1071 });
1072 }
1073
1074 fn run_interpolate(&mut self, count: u64, frame: &BytecodeFrame) -> EvalResult<()> {
1078 let mut out = BString::default();
1079 let mut context: NixContext = NixContext::new();
1081
1082 for i in 0..count {
1083 let val = self.stack_pop();
1084 if val.is_catchable() {
1085 for _ in (i + 1)..count {
1086 self.stack.pop();
1087 }
1088 self.stack.push(val);
1089 return Ok(());
1090 }
1091 let mut nix_string = val.to_contextful_str().with_span(frame, self)?;
1092 out.push_str(nix_string.as_bstr());
1093 if let Some(nix_string_ctx) = nix_string.take_context() {
1094 context.extend(*nix_string_ctx)
1095 }
1096 }
1097
1098 self.stack
1099 .push(Value::String(NixString::new_context_from(context, out)));
1100 Ok(())
1101 }
1102
1103 fn run_interpolate_path(&mut self, count: u64, frame: &BytecodeFrame) -> EvalResult<()> {
1107 let mut path_str = String::new();
1109
1110 for i in 0..count {
1111 let val = self.stack_pop();
1112 if val.is_catchable() {
1113 for _ in (i + 1)..count {
1115 self.stack.pop();
1116 }
1117 self.stack.push(val);
1118 return Ok(());
1119 }
1120
1121 match val {
1123 Value::String(s) => {
1124 path_str.push_str(&s.as_bytes().to_str_lossy());
1125 }
1126 Value::Path(p) => {
1127 path_str.push_str(&p.to_string_lossy());
1128 }
1129 _ => {
1130 return frame.error(
1131 self,
1132 ErrorKind::TypeError {
1133 expected: "string or path",
1134 actual: val.type_of(),
1135 },
1136 );
1137 }
1138 }
1139 }
1140
1141 let path = canon_path(PathBuf::from(path_str));
1143 self.stack.push(Value::Path(Box::new(path)));
1144
1145 Ok(())
1146 }
1147
1148 fn call_builtin(&mut self, span: Span, mut builtin: Builtin) -> EvalResult<()> {
1156 let builtin_name = builtin.name();
1157 self.observer.observe_enter_builtin(builtin_name);
1158
1159 builtin.apply_arg(self.stack_pop());
1160
1161 match builtin.call() {
1162 BuiltinResult::Partial(partial) => self.stack.push(Value::Builtin(partial)),
1164
1165 BuiltinResult::Called(name, generator) => self.frames.push(Frame::Generator {
1167 generator,
1168 span,
1169 name,
1170 state: GeneratorState::Running,
1171 }),
1172 }
1173
1174 Ok(())
1175 }
1176
1177 fn call_value(
1178 &mut self,
1179 span: Span,
1180 parent: Option<(Span, BytecodeFrame)>,
1181 callable: Value,
1182 ) -> EvalResult<()> {
1183 match callable {
1184 Value::Builtin(builtin) => self.call_builtin(span, builtin),
1185 Value::Thunk(thunk) => self.call_value(span, parent, thunk.value().clone()),
1186
1187 Value::Closure(closure) => {
1188 let lambda = closure.lambda();
1189 self.observer.observe_tail_call(self.frames.len(), &lambda);
1190
1191 let stack_offset = self.stack.len() - 1;
1196
1197 if let Some((parent_span, parent_frame)) = parent {
1201 self.push_bytecode_frame(parent_span, parent_frame);
1202 }
1203
1204 self.push_bytecode_frame(
1205 span,
1206 BytecodeFrame {
1207 lambda,
1208 upvalues: closure.upvalues(),
1209 ip: CodeIdx(0),
1210 stack_offset,
1211 },
1212 );
1213
1214 Ok(())
1215 }
1216
1217 val @ Value::Attrs(_) => {
1219 if let Some((parent_span, parent_frame)) = parent {
1220 self.push_bytecode_frame(parent_span, parent_frame);
1221 }
1222
1223 self.enqueue_generator("__functor call", span, |co| call_functor(co, val));
1224 Ok(())
1225 }
1226
1227 val @ Value::Catchable(_) => {
1228 self.stack.pop();
1230 self.stack.push(val);
1233 Ok(())
1234 }
1235
1236 v => Err(ErrorKind::NotCallable(v.type_of())).with_span(span, self),
1237 }
1238 }
1239
1240 fn populate_upvalues(&self, frame: &mut BytecodeFrame) -> EvalResult<Upvalues> {
1245 let data = UpvalueData::from_raw(frame.read_uvarint());
1246 let count = data.count();
1247 let capture_with = data.captures_with();
1248
1249 let mut static_upvalues = vec![];
1250
1251 let with_stack = if capture_with {
1252 let mut captured_with_stack = frame.upvalues.with_stack().clone();
1255 captured_with_stack.reserve_exact(self.with_stack.len());
1257
1258 for idx in &self.with_stack {
1259 captured_with_stack.push(self.stack[*idx].clone());
1260 }
1261
1262 captured_with_stack
1263 } else {
1264 vec![]
1265 };
1266
1267 for _ in 0..count {
1268 let pos = Position(frame.read_uvarint());
1269
1270 if let Some(stack_idx) = pos.runtime_stack_index() {
1271 let idx = frame.stack_offset + stack_idx.0;
1272
1273 let val = match self.stack.get(idx) {
1274 Some(val) => val.clone(),
1275 None => {
1276 return frame.error(
1278 self,
1279 ErrorKind::SnixBug {
1280 msg: "upvalue to be captured was missing on stack",
1281 metadata: Some(Rc::new(json!({
1282 "ip": format!("{:#x}", frame.ip.0 - 1),
1283 "stack_idx(relative)": stack_idx.0,
1284 "stack_idx(absolute)": idx,
1285 }))),
1286 },
1287 );
1288 }
1289 };
1290
1291 static_upvalues.push(val);
1292 } else if let Some(idx) = pos.runtime_deferred_local() {
1293 static_upvalues.push(Value::DeferredUpvalue(idx));
1294 } else if let Some(idx) = pos.runtime_upvalue_index() {
1295 static_upvalues.push(frame.upvalue(idx).clone());
1296 } else {
1297 panic!("Snix bug: invalid capture position emitted")
1298 }
1299 }
1300
1301 Ok(Upvalues::from_raw_parts(static_upvalues, with_stack))
1302 }
1303}
1304
1305async fn resolve_with(
1309 co: GenCo,
1310 ident: BString,
1311 vm_with_len: usize,
1312 upvalue_with_len: usize,
1313) -> Result<Value, ErrorKind> {
1314 async fn fetch_forced_with(co: &GenCo, idx: usize) -> Value {
1316 match co.yield_(VMRequest::WithValue(idx)).await {
1317 VMResponse::Value(value) => value,
1318 msg => panic!("Snix bug: VM responded with incorrect generator message: {msg}"),
1319 }
1320 }
1321
1322 async fn fetch_captured_with(co: &GenCo, idx: usize) -> Value {
1324 match co.yield_(VMRequest::CapturedWithValue(idx)).await {
1325 VMResponse::Value(value) => value,
1326 msg => panic!("Snix bug: VM responded with incorrect generator message: {msg}"),
1327 }
1328 }
1329
1330 for with_stack_idx in (0..vm_with_len).rev() {
1331 let with = fetch_forced_with(&co, with_stack_idx).await;
1333
1334 if with.is_catchable() {
1335 return Ok(with);
1336 }
1337
1338 match with.to_attrs()?.select(&ident) {
1339 None => continue,
1340 Some(val) => return Ok(val.clone()),
1341 }
1342 }
1343
1344 for upvalue_with_idx in (0..upvalue_with_len).rev() {
1345 let with = fetch_captured_with(&co, upvalue_with_idx).await;
1346
1347 if with.is_catchable() {
1348 return Ok(with);
1349 }
1350
1351 match with.to_attrs()?.select(&ident) {
1352 None => continue,
1353 Some(val) => return Ok(val.clone()),
1354 }
1355 }
1356
1357 Err(ErrorKind::UnknownDynamicVariable(ident.to_string()))
1358}
1359
1360async fn add_values(co: GenCo, a: Value, b: Value) -> Result<Value, ErrorKind> {
1362 let result = match (a, b) {
1364 (Value::Path(p), v) => {
1365 let mut path = p.into_os_string();
1366 match generators::request_string_coerce(
1367 &co,
1368 v,
1369 CoercionKind {
1370 strong: false,
1371
1372 import_paths: false,
1379 },
1383 )
1384 .await
1385 {
1386 Ok(vs) => {
1387 path.push(vs.to_os_str()?);
1388 crate::value::canon_path(PathBuf::from(path)).into()
1389 }
1390 Err(c) => Value::Catchable(Box::new(c)),
1391 }
1392 }
1393 (Value::String(s1), Value::String(s2)) => Value::String(s1.concat(&s2)),
1394 (Value::String(s1), v) => generators::request_string_coerce(
1395 &co,
1396 v,
1397 CoercionKind {
1398 strong: false,
1399 import_paths: true,
1401 },
1402 )
1403 .await
1404 .map(|s2| Value::String(s1.concat(&s2)))
1405 .into(),
1406 (a @ Value::Integer(_), b) | (a @ Value::Float(_), b) => arithmetic_op!(&a, &b, +)?,
1407 (a, b) => {
1408 let r1 = generators::request_string_coerce(
1409 &co,
1410 a,
1411 CoercionKind {
1412 strong: false,
1413 import_paths: false,
1414 },
1415 )
1416 .await;
1417 let r2 = generators::request_string_coerce(
1418 &co,
1419 b,
1420 CoercionKind {
1421 strong: false,
1422 import_paths: false,
1423 },
1424 )
1425 .await;
1426 match (r1, r2) {
1427 (Ok(s1), Ok(s2)) => Value::String(s1.concat(&s2)),
1428 (Err(c), _) => return Ok(Value::from(c)),
1429 (_, Err(c)) => return Ok(Value::from(c)),
1430 }
1431 }
1432 };
1433
1434 Ok(result)
1435}
1436
1437pub struct RuntimeResult {
1439 pub value: Value,
1440 pub warnings: Vec<EvalWarning>,
1441}
1442
1443async fn final_deep_force(co: GenCo) -> Result<Value, ErrorKind> {
1447 let value = generators::request_stack_pop(&co).await;
1448 Ok(generators::request_deep_force(&co, value).await)
1449}
1450
1451#[derive(Debug, Clone, Copy, Default)]
1453pub enum EvalMode {
1454 #[default]
1457 Lazy,
1458
1459 Strict,
1461}
1462
1463pub fn run_lambda<IO>(
1464 nix_search_path: NixSearchPath,
1465 io_handle: IO,
1466 observer: Option<&mut dyn RuntimeObserver>,
1467 source: SourceCode,
1468 globals: Rc<GlobalsMap>,
1469 lambda: Rc<Lambda>,
1470 mode: EvalMode,
1471) -> EvalResult<RuntimeResult>
1472where
1473 IO: AsRef<dyn EvalIO> + 'static,
1474{
1475 let root_span = lambda.chunk.get_span(CodeIdx(lambda.chunk.code.len() - 1));
1482
1483 let mut vm = VM::new(
1484 nix_search_path,
1485 io_handle,
1486 OptionalRuntimeObserver(observer),
1487 source,
1488 globals,
1489 root_span,
1490 );
1491
1492 match mode {
1495 EvalMode::Lazy => {}
1496 EvalMode::Strict => vm.enqueue_generator("final_deep_force", root_span, final_deep_force),
1497 }
1498
1499 vm.frames.push(Frame::BytecodeFrame {
1500 span: root_span,
1501 bytecode_frame: BytecodeFrame {
1502 lambda,
1503 upvalues: Rc::new(Upvalues::with_capacity(0)),
1504 ip: CodeIdx(0),
1505 stack_offset: 0,
1506 },
1507 });
1508
1509 vm.execute()
1510}