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 .and_then(|h| if h.is_empty() { None } else { Some(h) })
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(count, self.stack.split_off(self.stack.len() - count * 2))
1037 .with_span(frame, self)?
1038 .map(Value::attrs)
1039 .into();
1040
1041 self.stack.push(attrs);
1042 Ok(())
1043 }
1044
1045 fn last_bytecode_frame(&self) -> Option<&BytecodeFrame> {
1047 for frame in self.frames.iter().rev() {
1048 if let Frame::BytecodeFrame { bytecode_frame, .. } = frame {
1049 return Some(bytecode_frame);
1050 }
1051 }
1052
1053 None
1054 }
1055
1056 pub fn push_warning(&mut self, warning: EvalWarning) {
1058 self.warnings.push(warning);
1059 }
1060
1061 pub fn emit_warning(&mut self, kind: WarningKind) {
1064 self.push_warning(EvalWarning {
1065 kind,
1066 span: self.get_span(),
1067 });
1068 }
1069
1070 fn run_interpolate(&mut self, count: u64, frame: &BytecodeFrame) -> EvalResult<()> {
1074 let mut out = BString::default();
1075 let mut context: NixContext = NixContext::new();
1077
1078 for i in 0..count {
1079 let val = self.stack_pop();
1080 if val.is_catchable() {
1081 for _ in (i + 1)..count {
1082 self.stack.pop();
1083 }
1084 self.stack.push(val);
1085 return Ok(());
1086 }
1087 let mut nix_string = val.to_contextful_str().with_span(frame, self)?;
1088 out.push_str(nix_string.as_bstr());
1089 if let Some(nix_string_ctx) = nix_string.take_context() {
1090 context.extend(nix_string_ctx.into_iter())
1091 }
1092 }
1093
1094 self.stack
1095 .push(Value::String(NixString::new_context_from(context, out)));
1096 Ok(())
1097 }
1098
1099 fn run_interpolate_path(&mut self, count: u64, frame: &BytecodeFrame) -> EvalResult<()> {
1103 let mut path_str = String::new();
1105
1106 for i in 0..count {
1107 let val = self.stack_pop();
1108 if val.is_catchable() {
1109 for _ in (i + 1)..count {
1111 self.stack.pop();
1112 }
1113 self.stack.push(val);
1114 return Ok(());
1115 }
1116
1117 match val {
1119 Value::String(s) => {
1120 path_str.push_str(&s.as_bytes().to_str_lossy());
1121 }
1122 Value::Path(p) => {
1123 path_str.push_str(&p.to_string_lossy());
1124 }
1125 _ => {
1126 return frame.error(
1127 self,
1128 ErrorKind::TypeError {
1129 expected: "string or path",
1130 actual: val.type_of(),
1131 },
1132 );
1133 }
1134 }
1135 }
1136
1137 let path = canon_path(PathBuf::from(path_str));
1139 self.stack.push(Value::Path(Box::new(path)));
1140
1141 Ok(())
1142 }
1143
1144 fn call_builtin(&mut self, span: Span, mut builtin: Builtin) -> EvalResult<()> {
1152 let builtin_name = builtin.name();
1153 self.observer.observe_enter_builtin(builtin_name);
1154
1155 builtin.apply_arg(self.stack_pop());
1156
1157 match builtin.call() {
1158 BuiltinResult::Partial(partial) => self.stack.push(Value::Builtin(partial)),
1160
1161 BuiltinResult::Called(name, generator) => self.frames.push(Frame::Generator {
1163 generator,
1164 span,
1165 name,
1166 state: GeneratorState::Running,
1167 }),
1168 }
1169
1170 Ok(())
1171 }
1172
1173 fn call_value(
1174 &mut self,
1175 span: Span,
1176 parent: Option<(Span, BytecodeFrame)>,
1177 callable: Value,
1178 ) -> EvalResult<()> {
1179 match callable {
1180 Value::Builtin(builtin) => self.call_builtin(span, builtin),
1181 Value::Thunk(thunk) => self.call_value(span, parent, thunk.value().clone()),
1182
1183 Value::Closure(closure) => {
1184 let lambda = closure.lambda();
1185 self.observer.observe_tail_call(self.frames.len(), &lambda);
1186
1187 let stack_offset = self.stack.len() - 1;
1192
1193 if let Some((parent_span, parent_frame)) = parent {
1197 self.push_bytecode_frame(parent_span, parent_frame);
1198 }
1199
1200 self.push_bytecode_frame(
1201 span,
1202 BytecodeFrame {
1203 lambda,
1204 upvalues: closure.upvalues(),
1205 ip: CodeIdx(0),
1206 stack_offset,
1207 },
1208 );
1209
1210 Ok(())
1211 }
1212
1213 val @ Value::Attrs(_) => {
1215 if let Some((parent_span, parent_frame)) = parent {
1216 self.push_bytecode_frame(parent_span, parent_frame);
1217 }
1218
1219 self.enqueue_generator("__functor call", span, |co| call_functor(co, val));
1220 Ok(())
1221 }
1222
1223 val @ Value::Catchable(_) => {
1224 self.stack.pop();
1226 self.stack.push(val);
1229 Ok(())
1230 }
1231
1232 v => Err(ErrorKind::NotCallable(v.type_of())).with_span(span, self),
1233 }
1234 }
1235
1236 fn populate_upvalues(&self, frame: &mut BytecodeFrame) -> EvalResult<Upvalues> {
1241 let data = UpvalueData::from_raw(frame.read_uvarint());
1242 let count = data.count();
1243 let capture_with = data.captures_with();
1244
1245 let mut static_upvalues = vec![];
1246
1247 let with_stack = if capture_with {
1248 let mut captured_with_stack = frame.upvalues.with_stack().clone();
1251 captured_with_stack.reserve_exact(self.with_stack.len());
1253
1254 for idx in &self.with_stack {
1255 captured_with_stack.push(self.stack[*idx].clone());
1256 }
1257
1258 captured_with_stack
1259 } else {
1260 vec![]
1261 };
1262
1263 for _ in 0..count {
1264 let pos = Position(frame.read_uvarint());
1265
1266 if let Some(stack_idx) = pos.runtime_stack_index() {
1267 let idx = frame.stack_offset + stack_idx.0;
1268
1269 let val = match self.stack.get(idx) {
1270 Some(val) => val.clone(),
1271 None => {
1272 return frame.error(
1274 self,
1275 ErrorKind::SnixBug {
1276 msg: "upvalue to be captured was missing on stack",
1277 metadata: Some(Rc::new(json!({
1278 "ip": format!("{:#x}", frame.ip.0 - 1),
1279 "stack_idx(relative)": stack_idx.0,
1280 "stack_idx(absolute)": idx,
1281 }))),
1282 },
1283 );
1284 }
1285 };
1286
1287 static_upvalues.push(val);
1288 } else if let Some(idx) = pos.runtime_deferred_local() {
1289 static_upvalues.push(Value::DeferredUpvalue(idx));
1290 } else if let Some(idx) = pos.runtime_upvalue_index() {
1291 static_upvalues.push(frame.upvalue(idx).clone());
1292 } else {
1293 panic!("Snix bug: invalid capture position emitted")
1294 }
1295 }
1296
1297 Ok(Upvalues::from_raw_parts(static_upvalues, with_stack))
1298 }
1299}
1300
1301async fn resolve_with(
1305 co: GenCo,
1306 ident: BString,
1307 vm_with_len: usize,
1308 upvalue_with_len: usize,
1309) -> Result<Value, ErrorKind> {
1310 async fn fetch_forced_with(co: &GenCo, idx: usize) -> Value {
1312 match co.yield_(VMRequest::WithValue(idx)).await {
1313 VMResponse::Value(value) => value,
1314 msg => panic!("Snix bug: VM responded with incorrect generator message: {msg}"),
1315 }
1316 }
1317
1318 async fn fetch_captured_with(co: &GenCo, idx: usize) -> Value {
1320 match co.yield_(VMRequest::CapturedWithValue(idx)).await {
1321 VMResponse::Value(value) => value,
1322 msg => panic!("Snix bug: VM responded with incorrect generator message: {msg}"),
1323 }
1324 }
1325
1326 for with_stack_idx in (0..vm_with_len).rev() {
1327 let with = fetch_forced_with(&co, with_stack_idx).await;
1329
1330 if with.is_catchable() {
1331 return Ok(with);
1332 }
1333
1334 match with.to_attrs()?.select(&ident) {
1335 None => continue,
1336 Some(val) => return Ok(val.clone()),
1337 }
1338 }
1339
1340 for upvalue_with_idx in (0..upvalue_with_len).rev() {
1341 let with = fetch_captured_with(&co, upvalue_with_idx).await;
1342
1343 if with.is_catchable() {
1344 return Ok(with);
1345 }
1346
1347 match with.to_attrs()?.select(&ident) {
1348 None => continue,
1349 Some(val) => return Ok(val.clone()),
1350 }
1351 }
1352
1353 Err(ErrorKind::UnknownDynamicVariable(ident.to_string()))
1354}
1355
1356async fn add_values(co: GenCo, a: Value, b: Value) -> Result<Value, ErrorKind> {
1358 let result = match (a, b) {
1360 (Value::Path(p), v) => {
1361 let mut path = p.into_os_string();
1362 match generators::request_string_coerce(
1363 &co,
1364 v,
1365 CoercionKind {
1366 strong: false,
1367
1368 import_paths: false,
1375 },
1379 )
1380 .await
1381 {
1382 Ok(vs) => {
1383 path.push(vs.to_os_str()?);
1384 crate::value::canon_path(PathBuf::from(path)).into()
1385 }
1386 Err(c) => Value::Catchable(Box::new(c)),
1387 }
1388 }
1389 (Value::String(s1), Value::String(s2)) => Value::String(s1.concat(&s2)),
1390 (Value::String(s1), v) => generators::request_string_coerce(
1391 &co,
1392 v,
1393 CoercionKind {
1394 strong: false,
1395 import_paths: true,
1397 },
1398 )
1399 .await
1400 .map(|s2| Value::String(s1.concat(&s2)))
1401 .into(),
1402 (a @ Value::Integer(_), b) | (a @ Value::Float(_), b) => arithmetic_op!(&a, &b, +)?,
1403 (a, b) => {
1404 let r1 = generators::request_string_coerce(
1405 &co,
1406 a,
1407 CoercionKind {
1408 strong: false,
1409 import_paths: false,
1410 },
1411 )
1412 .await;
1413 let r2 = generators::request_string_coerce(
1414 &co,
1415 b,
1416 CoercionKind {
1417 strong: false,
1418 import_paths: false,
1419 },
1420 )
1421 .await;
1422 match (r1, r2) {
1423 (Ok(s1), Ok(s2)) => Value::String(s1.concat(&s2)),
1424 (Err(c), _) => return Ok(Value::from(c)),
1425 (_, Err(c)) => return Ok(Value::from(c)),
1426 }
1427 }
1428 };
1429
1430 Ok(result)
1431}
1432
1433pub struct RuntimeResult {
1435 pub value: Value,
1436 pub warnings: Vec<EvalWarning>,
1437}
1438
1439async fn final_deep_force(co: GenCo) -> Result<Value, ErrorKind> {
1443 let value = generators::request_stack_pop(&co).await;
1444 Ok(generators::request_deep_force(&co, value).await)
1445}
1446
1447#[derive(Debug, Clone, Copy, Default)]
1449pub enum EvalMode {
1450 #[default]
1453 Lazy,
1454
1455 Strict,
1457}
1458
1459pub fn run_lambda<IO>(
1460 nix_search_path: NixSearchPath,
1461 io_handle: IO,
1462 observer: Option<&mut dyn RuntimeObserver>,
1463 source: SourceCode,
1464 globals: Rc<GlobalsMap>,
1465 lambda: Rc<Lambda>,
1466 mode: EvalMode,
1467) -> EvalResult<RuntimeResult>
1468where
1469 IO: AsRef<dyn EvalIO> + 'static,
1470{
1471 let root_span = lambda.chunk.get_span(CodeIdx(lambda.chunk.code.len() - 1));
1478
1479 let mut vm = VM::new(
1480 nix_search_path,
1481 io_handle,
1482 OptionalRuntimeObserver(observer),
1483 source,
1484 globals,
1485 root_span,
1486 );
1487
1488 match mode {
1491 EvalMode::Lazy => {}
1492 EvalMode::Strict => vm.enqueue_generator("final_deep_force", root_span, final_deep_force),
1493 }
1494
1495 vm.frames.push(Frame::BytecodeFrame {
1496 span: root_span,
1497 bytecode_frame: BytecodeFrame {
1498 lambda,
1499 upvalues: Rc::new(Upvalues::with_capacity(0)),
1500 ip: CodeIdx(0),
1501 stack_offset: 0,
1502 },
1503 });
1504
1505 vm.execute()
1506}