1use std::{borrow::Cow, fs::File, sync::Arc};
2
3use nu_path::{dots::expand_ndots_safe, expand_path, expand_path_with, expand_tilde};
4#[cfg(feature = "os")]
5use nu_protocol::process::check_exit_status_future;
6use nu_protocol::{
7 CompareTypes, DeclId, ENV_VARIABLE_ID, Flag, IntoPipelineData, IntoSpanned, LabeledError,
8 ListStream, OutDest, PipelineData, PipelineExecutionData, PositionalArg, Range, Record, RegId,
9 ShellError, Signals, Signature, Span, Spanned, Type, Value, VarId,
10 ast::{Bits, Block, Boolean, CellPath, Comparison, Math, Operator},
11 combined_type_string,
12 debugger::DebugContext,
13 engine::{
14 Argument, Closure, EngineState, EnvName, ErrorHandler, Matcher, Redirection, Stack,
15 StateWorkingSet,
16 },
17 ir::{Call, DataSlice, Instruction, IrAstRef, IrBlock, Literal, RedirectMode},
18 shell_error::{generic::GenericError, io::IoError},
19};
20use nu_utils::IgnoreCaseExt;
21
22use crate::{
23 ENV_CONVERSIONS, convert_env_vars, eval::is_automatic_env_var, eval_block_with_early_return,
24};
25
26fn expand_external_glob_arg(val: Value) -> Value {
32 if let Value::Glob {
33 val: ref s,
34 no_expand,
35 internal_span,
36 ..
37 } = val
38 && !no_expand
39 && !nu_glob::is_glob(s)
40 {
41 let expanded = expand_ndots_safe(expand_tilde(s.as_str()));
42 return Value::string(expanded.to_string_lossy().into_owned(), internal_span);
43 }
44 val
45}
46
47pub fn eval_ir_block<D: DebugContext>(
48 engine_state: &EngineState,
49 stack: &mut Stack,
50 block: &Block,
51 input: PipelineData,
52) -> Result<PipelineExecutionData, ShellError> {
53 let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
58 if stack.recursion_count > maximum_call_stack_depth {
59 return Err(ShellError::RecursionLimitReached {
60 recursion_limit: maximum_call_stack_depth,
61 span: block.span,
62 });
63 }
64
65 let pushed_scope = if let Some(bindings) = &block.scope_bindings {
67 stack.push_scope_bindings(bindings.clone());
68 true
69 } else {
70 false
71 };
72
73 let saved_regions = std::mem::take(&mut stack.ir_scope_regions);
76 let saved_pc = stack.ir_instruction_index.take();
77
78 let result = eval_ir_block_inner::<D>(engine_state, stack, block, input);
79
80 stack.ir_scope_regions = saved_regions;
81 stack.ir_instruction_index = saved_pc;
82 if pushed_scope {
83 stack.pop_scope_bindings();
84 }
85 result
86}
87
88fn eval_ir_block_inner<D: DebugContext>(
89 engine_state: &EngineState,
90 stack: &mut Stack,
91 block: &Block,
92 input: PipelineData,
93) -> Result<PipelineExecutionData, ShellError> {
94 if let Some(ir_block) = &block.ir_block {
95 D::enter_block(engine_state, block);
96
97 stack.ir_scope_regions = ir_block.scope_regions.clone();
98 stack.ir_instruction_index = None;
99
100 let args_base = stack.arguments.get_base();
101 let error_handler_base = stack.error_handlers.get_base();
102 let finally_handler_base = stack.finally_run_handlers.get_base();
103
104 let mut registers = Vec::with_capacity(ir_block.register_count as usize);
107 for _ in 0..ir_block.register_count {
108 registers.push(PipelineExecutionData::empty());
109 }
110
111 let mut files = vec![None; ir_block.file_count as usize];
113
114 let result = eval_ir_block_impl::<D>(
115 &mut EvalContext {
116 engine_state,
117 stack,
118 data: &ir_block.data,
119 block_span: &block.span,
120 args_base,
121 error_handler_base,
122 finally_handler_base,
123 redirect_out: None,
124 redirect_err: None,
125 matches: vec![],
126 registers: &mut registers[..],
127 files: &mut files[..],
128 },
129 ir_block,
130 input,
131 );
132
133 stack.error_handlers.leave_frame(error_handler_base);
134 stack.finally_run_handlers.leave_frame(finally_handler_base);
135 stack.arguments.leave_frame(args_base);
136 stack.ir_instruction_index = None;
137
138 D::leave_block(engine_state, block);
139
140 result
141 } else {
142 let error = if let Some(span) = block.span {
144 ShellError::Generic(
145 GenericError::new(
146 "Can't evaluate block in IR mode",
147 "block is missing compiled representation",
148 span,
149 )
150 .with_help("the IrBlock is probably missing due to a compilation error"),
151 )
152 } else {
153 ShellError::Generic(
154 GenericError::new_internal(
155 "Can't evaluate block in IR mode",
156 "block is missing compiled representation",
157 )
158 .with_help("the IrBlock is probably missing due to a compilation error"),
159 )
160 };
161 Err(error)
162 }
163}
164
165struct EvalContext<'a> {
167 engine_state: &'a EngineState,
168 stack: &'a mut Stack,
169 data: &'a Arc<[u8]>,
170 block_span: &'a Option<Span>,
172 args_base: usize,
174 error_handler_base: usize,
176 finally_handler_base: usize,
178 redirect_out: Option<Redirection>,
180 redirect_err: Option<Redirection>,
182 matches: Vec<(VarId, Value)>,
184 registers: &'a mut [PipelineExecutionData],
186 files: &'a mut [Option<Arc<File>>],
188}
189
190impl<'a> EvalContext<'a> {
191 #[inline]
193 fn put_reg(&mut self, reg_id: RegId, new_value: PipelineExecutionData) {
194 self.registers[reg_id.get() as usize] = new_value;
196 }
197
198 #[inline]
200 fn borrow_reg(&self, reg_id: RegId) -> &PipelineData {
201 &self.registers[reg_id.get() as usize]
202 }
203
204 #[inline]
206 fn take_reg(&mut self, reg_id: RegId) -> PipelineExecutionData {
207 std::mem::replace(
209 &mut self.registers[reg_id.get() as usize],
210 PipelineExecutionData::empty(),
211 )
212 }
213
214 fn clone_reg(&mut self, reg_id: RegId, error_span: Span) -> Result<PipelineData, ShellError> {
216 match &self.registers[reg_id.get() as usize].body {
219 PipelineData::Empty => Ok(PipelineData::empty()),
220 PipelineData::Value(val, meta) => Ok(PipelineData::value(val.clone(), meta.clone())),
221 _ => Err(ShellError::IrEvalError {
222 msg: "Must collect to value before using instruction that clones from a register"
223 .into(),
224 span: Some(error_span),
225 }),
226 }
227 }
228
229 fn clone_reg_value(&mut self, reg_id: RegId, fallback_span: Span) -> Result<Value, ShellError> {
231 match self.clone_reg(reg_id, fallback_span)? {
232 PipelineData::Empty => Ok(Value::nothing(fallback_span)),
233 PipelineData::Value(val, _) => Ok(val),
234 _ => unreachable!("clone_reg should never return stream data"),
235 }
236 }
237
238 fn collect_reg(&mut self, reg_id: RegId, fallback_span: Span) -> Result<Value, ShellError> {
242 #[cfg(feature = "os")]
245 let body = {
246 let mut data = self.take_reg(reg_id);
247 data.exit.clear();
248 data.body
249 };
250 #[cfg(not(feature = "os"))]
251 let body = self.take_reg(reg_id).body;
252 let span = body.span().unwrap_or(fallback_span);
253 body.into_value(span)
254 }
255
256 fn get_str(&self, slice: DataSlice, error_span: Span) -> Result<&'a str, ShellError> {
258 std::str::from_utf8(&self.data[slice]).map_err(|_| ShellError::IrEvalError {
259 msg: format!("data slice does not refer to valid UTF-8: {slice:?}"),
260 span: Some(error_span),
261 })
262 }
263}
264
265fn eval_ir_block_impl<D: DebugContext>(
267 ctx: &mut EvalContext<'_>,
268 ir_block: &IrBlock,
269 input: PipelineData,
270) -> Result<PipelineExecutionData, ShellError> {
271 if !ctx.registers.is_empty() {
272 ctx.registers[0] = PipelineExecutionData::from(input);
273 }
274
275 let mut pc = 0;
277 let need_backtrace = ctx.engine_state.get_env_var("NU_BACKTRACE").is_some();
278 let mut ret_val: Option<Result<PipelineExecutionData, ShellError>> = None;
282
283 while pc < ir_block.instructions.len() {
284 let instruction = &ir_block.instructions[pc];
285 let span = &ir_block.spans[pc];
286 let ast = &ir_block.ast[pc];
287
288 ctx.stack.ir_instruction_index = Some(pc);
290
291 D::enter_instruction(ctx.engine_state, ctx.stack, ir_block, pc, ctx.registers);
292
293 let result = eval_instruction::<D>(ctx, instruction, span, ast, need_backtrace);
294
295 D::leave_instruction(
296 ctx.engine_state,
297 ctx.stack,
298 ir_block,
299 pc,
300 ctx.registers,
301 result.as_ref().err(),
302 );
303
304 match result {
305 Ok(InstructionResult::Continue) => {
306 pc += 1;
307 }
308 Ok(InstructionResult::Branch(next_pc)) => {
309 pc = next_pc;
310 }
311 Ok(InstructionResult::Return(reg_id)) => {
312 match ret_val {
315 Some(res) => return res,
316 None => return Ok(ctx.take_reg(reg_id)),
317 }
318 }
319 Ok(InstructionResult::ReturnEarly(reg_id)) => {
320 if let Some(always_run_handler) =
321 ctx.stack.finally_run_handlers.pop(ctx.finally_handler_base)
322 {
323 let data = ctx.take_reg(reg_id);
328 #[cfg(feature = "os")]
329 let collected = collect(data, *span, false);
330 #[cfg(not(feature = "os"))]
331 let collected = collect(data, *span);
332 ret_val = Some(
333 collected.map(|body| PipelineExecutionData::from(body).with_early_return()),
334 );
335 prepare_error_handler(ctx, always_run_handler, None);
336 pc = always_run_handler.handler_index;
337 } else {
338 return Ok(ctx.take_reg(reg_id).with_early_return());
343 }
344 }
345 Err(err @ (ShellError::Continue { .. } | ShellError::Break { .. })) => {
346 return Err(err);
347 }
348 Err(err @ ShellError::Exit { abort: false, .. }) => {
349 if let Some(always_run_handler) =
350 ctx.stack.finally_run_handlers.pop(ctx.finally_handler_base)
351 {
352 prepare_error_handler(ctx, always_run_handler, None);
355 pc = always_run_handler.handler_index;
356 ret_val = Some(Err(err));
357 } else {
358 return Err(err);
360 }
361 }
362 Err(err @ ShellError::Exit { abort: true, .. }) => {
363 return Err(err);
364 }
365 Err(err) => {
366 #[cfg(unix)]
367 let is_terminated_by_signal = matches!(&err, ShellError::TerminatedBySignal { .. });
368 #[cfg(not(unix))]
369 let is_terminated_by_signal = false;
370
371 let is_interrupted =
372 matches!(err, ShellError::Interrupted { .. }) || is_terminated_by_signal;
373 if let Some(error_handler) = ctx.stack.error_handlers.pop(ctx.error_handler_base) {
374 if is_interrupted {
375 ctx.engine_state.signals().reset();
376 }
377 prepare_error_handler(ctx, error_handler, Some(err.into_spanned(*span)));
379 pc = error_handler.handler_index;
380 } else if let Some(always_run_handler) =
381 ctx.stack.finally_run_handlers.pop(ctx.finally_handler_base)
382 {
383 if is_interrupted {
384 ctx.engine_state.signals().reset();
385 }
386 prepare_error_handler(
387 ctx,
388 always_run_handler,
389 Some(err.clone().into_spanned(*span)),
390 );
391 pc = always_run_handler.handler_index;
392 ret_val = Some(Err(err));
393 } else if need_backtrace {
394 let err = ShellError::into_chained(err, *span);
395 return Err(err);
396 } else {
397 return Err(err);
398 }
399 }
400 }
401 }
402
403 Err(ShellError::IrEvalError {
405 msg: format!(
406 "Program counter out of range (pc={pc}, len={len})",
407 len = ir_block.instructions.len(),
408 ),
409 span: *ctx.block_span,
410 })
411}
412
413fn prepare_error_handler(
415 ctx: &mut EvalContext<'_>,
416 error_handler: ErrorHandler,
417 error: Option<Spanned<ShellError>>,
418) {
419 if let Some(reg_id) = error_handler.error_register {
420 if let Some(error) = error {
421 ctx.stack.set_last_error(&error.item);
423 ctx.put_reg(
425 reg_id,
426 PipelineExecutionData::from(
427 error
428 .item
429 .into_full_value(
430 &StateWorkingSet::new(ctx.engine_state),
431 ctx.stack,
432 error.span,
433 )
434 .into_pipeline_data(),
435 ),
436 );
437 } else {
438 ctx.put_reg(reg_id, PipelineExecutionData::empty());
440 }
441 }
442}
443
444#[derive(Debug)]
446enum InstructionResult {
447 Continue,
448 Branch(usize),
449 Return(RegId),
450 ReturnEarly(RegId),
458}
459
460fn eval_instruction<D: DebugContext>(
462 ctx: &mut EvalContext<'_>,
463 instruction: &Instruction,
464 span: &Span,
465 ast: &Option<IrAstRef>,
466 need_backtrace: bool,
467) -> Result<InstructionResult, ShellError> {
468 use self::InstructionResult::*;
469
470 instruction.check_interrupt(ctx.engine_state, span)?;
472
473 match instruction {
476 Instruction::Unreachable => Err(ShellError::IrEvalError {
477 msg: "Reached unreachable code".into(),
478 span: Some(*span),
479 }),
480 Instruction::LoadLiteral { dst, lit } => load_literal(ctx, *dst, lit, *span),
481 Instruction::LoadValue { dst, val } => {
482 ctx.put_reg(
483 *dst,
484 PipelineExecutionData::from(Value::clone(val).into_pipeline_data()),
485 );
486 Ok(Continue)
487 }
488 Instruction::Move { dst, src } => {
489 let val = ctx.take_reg(*src);
490 ctx.put_reg(*dst, val);
491 Ok(Continue)
492 }
493 Instruction::Clone { dst, src } => {
494 let data = ctx.clone_reg(*src, *span)?;
495 ctx.put_reg(*dst, PipelineExecutionData::from(data));
496 Ok(Continue)
497 }
498 Instruction::Collect { src_dst } => {
499 let data = ctx.take_reg(*src_dst);
500 #[cfg(feature = "os")]
501 let value = collect(data, *span, true)?;
502 #[cfg(not(feature = "os"))]
503 let value = collect(data, *span)?;
504 ctx.put_reg(*src_dst, PipelineExecutionData::from(value));
505 Ok(Continue)
506 }
507 Instruction::TryCollect { src_dst } => {
508 let data = ctx.take_reg(*src_dst);
509 #[cfg(feature = "os")]
510 let value = collect(data, *span, false)?;
511 #[cfg(not(feature = "os"))]
512 let value = collect(data, *span)?;
513 ctx.put_reg(*src_dst, PipelineExecutionData::from(value));
514 Ok(Continue)
515 }
516 Instruction::Span { src_dst } => {
517 let mut data = ctx.take_reg(*src_dst);
518 data.body = data.body.with_span(*span);
519 ctx.put_reg(*src_dst, data);
520 Ok(Continue)
521 }
522 Instruction::Drop { src } => {
523 ctx.take_reg(*src);
524 Ok(Continue)
525 }
526 Instruction::Drain { src } => {
527 let data = ctx.take_reg(*src);
528 drain(ctx, data)
529 }
530 Instruction::DrainIfEnd { src } => {
531 let data = ctx.take_reg(*src);
532 let res = drain_if_end(ctx, data)?;
533 ctx.put_reg(*src, PipelineExecutionData::from(res));
534 Ok(Continue)
535 }
536 Instruction::LoadVariable { dst, var_id } => {
537 let data = if *var_id == nu_protocol::LAST_VARIABLE_ID {
540 ctx.stack.defer_last_result_truncation_warning();
541 ctx.stack.last_result_pipeline_data(*span)
542 } else {
543 let value = get_var(ctx, *var_id, *span)?;
544 value.into_pipeline_data()
545 };
546 ctx.put_reg(*dst, PipelineExecutionData::from(data));
547 Ok(Continue)
548 }
549 Instruction::StoreVariable { var_id, src } => {
550 let value = ctx.collect_reg(*src, *span)?;
551 if nu_experimental::ENFORCE_RUNTIME_ANNOTATIONS.get() {
553 let variable = ctx.engine_state.get_var(*var_id);
554 let converted_value = check_assignment_type(value, &variable.ty, *span)?;
555 ctx.stack.add_var(*var_id, converted_value);
556 } else {
557 ctx.stack.add_var(*var_id, value);
558 }
559 Ok(Continue)
560 }
561 Instruction::DropVariable { var_id } => {
562 ctx.stack.remove_var(*var_id);
563 Ok(Continue)
564 }
565 Instruction::LoadEnv { dst, key } => {
566 let key = ctx.get_str(*key, *span)?;
567 if let Some(value) = get_env_var(ctx, key) {
568 let new_value = value.clone().into_pipeline_data();
569 ctx.put_reg(*dst, PipelineExecutionData::from(new_value));
570 Ok(Continue)
571 } else {
572 Err(ShellError::CantFindColumn {
575 col_name: key.into(),
576 span: Some(*span),
577 src_span: *span,
578 })
579 }
580 }
581 Instruction::LoadEnvOpt { dst, key } => {
582 let key = ctx.get_str(*key, *span)?;
583 let value = get_env_var(ctx, key)
584 .cloned()
585 .unwrap_or(Value::nothing(*span));
586 ctx.put_reg(
587 *dst,
588 PipelineExecutionData::from(value.into_pipeline_data()),
589 );
590 Ok(Continue)
591 }
592 Instruction::StoreEnv { key, src } => {
593 let key = ctx.get_str(*key, *span)?;
594 let value = ctx.collect_reg(*src, *span)?;
595
596 let key = get_env_var_name(ctx, key);
597
598 if !is_automatic_env_var(&key) {
599 let is_config = key == "config";
600 let update_conversions = key == ENV_CONVERSIONS;
601
602 ctx.stack.add_env_var(key.into_owned(), value.clone());
603
604 if is_config {
605 ctx.stack.update_config(ctx.engine_state)?;
606 }
607 if update_conversions {
608 convert_env_vars(ctx.stack, ctx.engine_state, &value)?;
609 }
610 Ok(Continue)
611 } else {
612 Err(ShellError::AutomaticEnvVarSetManually {
613 envvar_name: key.into(),
614 span: *span,
615 })
616 }
617 }
618 Instruction::PushPositional { src } => {
619 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
620 ctx.stack.arguments.push(Argument::Positional {
621 span: *span,
622 val,
623 ast: ast.clone().map(|ast_ref| ast_ref.0),
624 });
625 Ok(Continue)
626 }
627 Instruction::AppendRest { src } => {
628 let vals = ctx.collect_reg(*src, *span)?.with_span(*span);
629 ctx.stack.arguments.push(Argument::Spread {
630 span: *span,
631 vals,
632 ast: ast.clone().map(|ast_ref| ast_ref.0),
633 });
634 Ok(Continue)
635 }
636 Instruction::PushFlag { name } => {
637 let data = ctx.data.clone();
638 ctx.stack.arguments.push(Argument::Flag {
639 data,
640 name: *name,
641 short: DataSlice::empty(),
642 span: *span,
643 });
644 Ok(Continue)
645 }
646 Instruction::PushShortFlag { short } => {
647 let data = ctx.data.clone();
648 ctx.stack.arguments.push(Argument::Flag {
649 data,
650 name: DataSlice::empty(),
651 short: *short,
652 span: *span,
653 });
654 Ok(Continue)
655 }
656 Instruction::PushNamed { name, src } => {
657 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
658 let data = ctx.data.clone();
659 ctx.stack.arguments.push(Argument::Named {
660 data,
661 name: *name,
662 short: DataSlice::empty(),
663 span: *span,
664 val,
665 ast: ast.clone().map(|ast_ref| ast_ref.0),
666 });
667 Ok(Continue)
668 }
669 Instruction::PushShortNamed { short, src } => {
670 let val = ctx.collect_reg(*src, *span)?.with_span(*span);
671 let data = ctx.data.clone();
672 ctx.stack.arguments.push(Argument::Named {
673 data,
674 name: DataSlice::empty(),
675 short: *short,
676 span: *span,
677 val,
678 ast: ast.clone().map(|ast_ref| ast_ref.0),
679 });
680 Ok(Continue)
681 }
682 Instruction::PushParserInfo { name, info } => {
683 let data = ctx.data.clone();
684 ctx.stack.arguments.push(Argument::ParserInfo {
685 data,
686 name: *name,
687 info: info.clone(),
688 });
689 Ok(Continue)
690 }
691 Instruction::RedirectOut { mode } => {
692 ctx.redirect_out = eval_redirection(ctx, mode, *span, RedirectionStream::Out)?;
693 Ok(Continue)
694 }
695 Instruction::RedirectErr { mode } => {
696 ctx.redirect_err = eval_redirection(ctx, mode, *span, RedirectionStream::Err)?;
697 Ok(Continue)
698 }
699 Instruction::CheckErrRedirected { src } => match ctx.borrow_reg(*src) {
700 #[cfg(feature = "os")]
701 PipelineData::ByteStream(stream, _)
702 if matches!(stream.source(), nu_protocol::ByteStreamSource::Child(_)) =>
703 {
704 Ok(Continue)
705 }
706 _ => Err(ShellError::Generic(GenericError::new(
707 "Can't redirect stderr of internal command output",
708 "piping stderr only works on external commands",
709 *span,
710 ))),
711 },
712 Instruction::OpenFile {
713 file_num,
714 path,
715 append,
716 } => {
717 let path = ctx.collect_reg(*path, *span)?;
718 let file = open_file(ctx, &path, *append)?;
719 ctx.files[*file_num as usize] = Some(file);
720 Ok(Continue)
721 }
722 Instruction::WriteFile { file_num, src } => {
723 let src = ctx.take_reg(*src);
724 let file = ctx
725 .files
726 .get(*file_num as usize)
727 .cloned()
728 .flatten()
729 .ok_or_else(|| ShellError::IrEvalError {
730 msg: format!("Tried to write to file #{file_num}, but it is not open"),
731 span: Some(*span),
732 })?;
733 let is_external = if let PipelineData::ByteStream(stream, ..) = &src.body {
734 stream.source().is_external()
735 } else {
736 false
737 };
738 if let Err(err) = src.body.write_to(file.as_ref()) {
739 if is_external {
740 ctx.stack.set_last_error(&err);
741 }
742 Err(err)?
743 } else {
744 Ok(Continue)
745 }
746 }
747 Instruction::CloseFile { file_num } => {
748 if ctx.files[*file_num as usize].take().is_some() {
749 Ok(Continue)
750 } else {
751 Err(ShellError::IrEvalError {
752 msg: format!("Tried to close file #{file_num}, but it is not open"),
753 span: Some(*span),
754 })
755 }
756 }
757 Instruction::Call { decl_id, src_dst } => {
758 let input = ctx.take_reg(*src_dst);
759 let input_data = input.body;
761 let mut result = eval_call::<D>(ctx, *decl_id, *span, input_data)?;
762 if need_backtrace {
763 match &mut result {
764 PipelineData::ByteStream(s, ..) => s.push_caller_span(*span),
765 PipelineData::ListStream(s, ..) => s.push_caller_span(*span),
766 _ => (),
767 };
768 }
769 #[cfg(feature = "os")]
773 {
774 let mut original_exit = input.exit;
775 if ctx.engine_state.get_decl(*decl_id).name() == "complete" {
779 original_exit.clear();
780 }
781 let result_exit_status_future = result
782 .clone_exit_status_future()
783 .map(|f| f.with_span(*span));
784 original_exit.push(result_exit_status_future);
785 ctx.put_reg(
786 *src_dst,
787 PipelineExecutionData {
788 body: result,
789 exit: original_exit,
790 early_return: false,
791 },
792 );
793 }
794 #[cfg(not(feature = "os"))]
795 ctx.put_reg(
796 *src_dst,
797 PipelineExecutionData {
798 body: result,
799 early_return: false,
800 },
801 );
802 Ok(Continue)
803 }
804 Instruction::StringAppend { src_dst, val } => {
805 let string_value = ctx.collect_reg(*src_dst, *span)?;
806 let operand_value = ctx.collect_reg(*val, *span)?;
807 let string_span = string_value.span();
808
809 let mut string = string_value.into_string()?;
810 let operand = if let Value::String { val, .. } = operand_value {
811 val
813 } else {
814 operand_value.to_expanded_string(", ", &ctx.stack.get_config(ctx.engine_state))
815 };
816 string.push_str(&operand);
817
818 let new_string_value = Value::string(string, string_span);
819 ctx.put_reg(
820 *src_dst,
821 PipelineExecutionData::from(new_string_value.into_pipeline_data()),
822 );
823 Ok(Continue)
824 }
825 Instruction::GlobFrom { src_dst, no_expand } => {
826 let string_value = ctx.collect_reg(*src_dst, *span)?;
827 let glob_value = if let Value::Glob { .. } = string_value {
828 string_value
830 } else {
831 let string = string_value.into_string()?;
833 Value::glob(string, *no_expand, *span)
834 };
835 ctx.put_reg(
836 *src_dst,
837 PipelineExecutionData::from(glob_value.into_pipeline_data()),
838 );
839 Ok(Continue)
840 }
841 Instruction::ListPush { src_dst, item } => {
842 let list_value = ctx.collect_reg(*src_dst, *span)?;
843 let item = ctx.collect_reg(*item, *span)?;
844 let list_span = list_value.span();
845 let mut list = list_value.into_list()?;
846 list.push(item);
847 ctx.put_reg(
848 *src_dst,
849 PipelineExecutionData::from(Value::list(list, list_span).into_pipeline_data()),
850 );
851 Ok(Continue)
852 }
853 Instruction::ListSpread { src_dst, items } => {
854 let list_value = ctx.collect_reg(*src_dst, *span)?;
855 let items = ctx.collect_reg(*items, *span)?;
856 let list_span = list_value.span();
857 let items_span = items.span();
858 let items = match items {
859 Value::List { vals, .. } => vals.into_owned(),
860 Value::Nothing { .. } => Vec::new(),
861 _ => return Err(ShellError::CannotSpreadAsList { span: items_span }),
862 };
863 let mut list = list_value.into_list()?;
864 list.extend(items);
865 ctx.put_reg(
866 *src_dst,
867 PipelineExecutionData::from(Value::list(list, list_span).into_pipeline_data()),
868 );
869 Ok(Continue)
870 }
871 Instruction::RecordInsert { src_dst, key, val } => {
872 let record_value = ctx.collect_reg(*src_dst, *span)?;
873 let key = ctx.collect_reg(*key, *span)?;
874 let val = ctx.collect_reg(*val, *span)?;
875 let record_span = record_value.span();
876 let mut record = record_value.into_record()?;
877
878 let key = key.coerce_into_string()?;
879 if let Some(old_value) = record.insert(&key, val) {
880 return Err(ShellError::ColumnDefinedTwice {
881 col_name: key,
882 second_use: *span,
883 first_use: old_value.span(),
884 });
885 }
886
887 ctx.put_reg(
888 *src_dst,
889 PipelineExecutionData::from(
890 Value::record(record, record_span).into_pipeline_data(),
891 ),
892 );
893 Ok(Continue)
894 }
895 Instruction::RecordSpread { src_dst, items } => {
896 let record_value = ctx.collect_reg(*src_dst, *span)?;
897 let items = ctx.collect_reg(*items, *span)?;
898 let record_span = record_value.span();
899 let items_span = items.span();
900 let mut record = record_value.into_record()?;
901 let items = match items {
902 Value::Record { val, .. } => val.into_owned(),
903 Value::Nothing { .. } => Record::new(),
904 _ => return Err(ShellError::CannotSpreadAsRecord { span: items_span }),
905 };
906 for (key, val) in items {
908 if let Some(first_value) = record.insert(&key, val) {
909 return Err(ShellError::ColumnDefinedTwice {
910 col_name: key,
911 second_use: *span,
912 first_use: first_value.span(),
913 });
914 }
915 }
916 ctx.put_reg(
917 *src_dst,
918 PipelineExecutionData::from(
919 Value::record(record, record_span).into_pipeline_data(),
920 ),
921 );
922 Ok(Continue)
923 }
924 Instruction::Not { src_dst } => {
925 let bool = ctx.collect_reg(*src_dst, *span)?;
926 let negated = !bool.as_bool()?;
927 ctx.put_reg(
928 *src_dst,
929 PipelineExecutionData::from(Value::bool(negated, bool.span()).into_pipeline_data()),
930 );
931 Ok(Continue)
932 }
933 Instruction::BinaryOp { lhs_dst, op, rhs } => binary_op(ctx, *lhs_dst, op, *rhs, *span),
934 Instruction::FollowCellPath { src_dst, path } => {
935 let data = ctx.take_reg(*src_dst);
936 let path = ctx.take_reg(*path);
937 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path.body {
938 let from_ans = data.body.metadata_ref().is_some_and(|m| {
943 m.custom
944 .get(nu_protocol::engine::Stack::ANS_LAST_RESULT_METADATA_KEY)
945 .is_some()
946 });
947 let is_ans_last = from_ans
948 && path.members.len() == 1
949 && matches!(
950 &path.members[0],
951 nu_protocol::ast::PathMember::String { val, .. } if val == "last"
952 );
953 let src_meta = data.body.metadata_ref().cloned();
954 let value = data.body.follow_cell_path(&path.members, *span)?;
955 let metadata = if is_ans_last {
956 src_meta.map(|mut m| {
958 m.custom
959 .remove(nu_protocol::engine::Stack::ANS_LAST_RESULT_METADATA_KEY);
960 m
961 })
962 } else {
963 None
964 };
965 ctx.put_reg(
966 *src_dst,
967 PipelineExecutionData::from(PipelineData::value(value, metadata)),
968 );
969 Ok(Continue)
970 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path.body {
971 Err(*error)
972 } else {
973 Err(ShellError::TypeMismatch {
974 err_message: "expected cell path".into(),
975 span: path.span().unwrap_or(*span),
976 })
977 }
978 }
979 Instruction::CloneCellPath { dst, src, path } => {
980 let value = ctx.clone_reg_value(*src, *span)?;
981 let path = ctx.take_reg(*path);
982 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path.body {
983 let value = value.follow_cell_path(&path.members)?;
984 ctx.put_reg(
985 *dst,
986 PipelineExecutionData::from(value.into_owned().into_pipeline_data()),
987 );
988 Ok(Continue)
989 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path.body {
990 Err(*error)
991 } else {
992 Err(ShellError::TypeMismatch {
993 err_message: "expected cell path".into(),
994 span: path.span().unwrap_or(*span),
995 })
996 }
997 }
998 Instruction::UpsertCellPath {
999 src_dst,
1000 path,
1001 new_value,
1002 } => {
1003 let mut data = ctx.take_reg(*src_dst).body;
1004 let metadata = data.take_metadata();
1005 let mut value = data.into_value(*span)?;
1007 let path = ctx.take_reg(*path);
1008 let new_value = ctx.collect_reg(*new_value, *span)?;
1009 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path.body {
1010 value.upsert_data_at_cell_path(&path.members, new_value)?;
1011 ctx.put_reg(
1012 *src_dst,
1013 PipelineExecutionData::from(value.into_pipeline_data_with_metadata(metadata)),
1014 );
1015 Ok(Continue)
1016 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path.body {
1017 Err(*error)
1018 } else {
1019 Err(ShellError::TypeMismatch {
1020 err_message: "expected cell path".into(),
1021 span: path.span().unwrap_or(*span),
1022 })
1023 }
1024 }
1025 Instruction::UpdateVarCellPath {
1026 var_id,
1027 cell_path,
1028 new_value,
1029 } => {
1030 let new_val = ctx.collect_reg(*new_value, *span)?;
1031 let path = ctx.take_reg(*cell_path);
1032 if let PipelineData::Value(Value::CellPath { val: path, .. }, _) = path.body {
1033 let new_val = if nu_experimental::ENFORCE_RUNTIME_ANNOTATIONS.get() {
1034 let variable = ctx.engine_state.get_var(*var_id);
1035 let expected_ty = variable.ty.follow_cell_path(&path.members);
1036 if let Some(expected_ty) = expected_ty {
1037 check_assignment_type(new_val, &expected_ty, *span)?
1038 } else {
1039 new_val
1040 }
1041 } else {
1042 new_val
1043 };
1044 ctx.stack
1045 .upsert_var_cell_path(*var_id, &path.members, new_val, *span)?;
1046 Ok(Continue)
1047 } else if let PipelineData::Value(Value::Error { error, .. }, _) = path.body {
1048 Err(*error)
1049 } else {
1050 Err(ShellError::TypeMismatch {
1051 err_message: "expected cell path".into(),
1052 span: path.span().unwrap_or(*span),
1053 })
1054 }
1055 }
1056 Instruction::Jump { index } => Ok(Branch(*index)),
1057 Instruction::BranchIf { cond, index } => {
1058 let data = ctx.take_reg(*cond);
1059 let data_span = data.span();
1060 let val = match data.body {
1061 PipelineData::Value(Value::Bool { val, .. }, _) => val,
1062 PipelineData::Value(Value::Error { error, .. }, _) => {
1063 return Err(*error);
1064 }
1065 _ => {
1066 return Err(ShellError::TypeMismatch {
1067 err_message: "expected bool".into(),
1068 span: data_span.unwrap_or(*span),
1069 });
1070 }
1071 };
1072 if val {
1073 Ok(Branch(*index))
1074 } else {
1075 Ok(Continue)
1076 }
1077 }
1078 Instruction::BranchIfEmpty { src, index } => {
1079 let is_empty = matches!(
1080 ctx.borrow_reg(*src),
1081 PipelineData::Empty | PipelineData::Value(Value::Nothing { .. }, _)
1082 );
1083
1084 if is_empty {
1085 Ok(Branch(*index))
1086 } else {
1087 Ok(Continue)
1088 }
1089 }
1090 Instruction::Match {
1091 pattern,
1092 src,
1093 index,
1094 } => {
1095 let value = ctx.clone_reg_value(*src, *span)?;
1096 ctx.matches.clear();
1097 if pattern.match_value(&value, &mut ctx.matches) {
1098 for (var_id, match_value) in ctx.matches.drain(..) {
1100 ctx.stack.add_var(var_id, match_value);
1101 }
1102 Ok(Branch(*index))
1103 } else {
1104 ctx.matches.clear();
1106 Ok(Continue)
1107 }
1108 }
1109 Instruction::CheckMatchGuard { src } => {
1110 if matches!(
1111 ctx.borrow_reg(*src),
1112 PipelineData::Value(Value::Bool { .. }, _)
1113 ) {
1114 Ok(Continue)
1115 } else {
1116 Err(ShellError::MatchGuardNotBool { span: *span })
1117 }
1118 }
1119 Instruction::Iterate {
1120 dst,
1121 stream,
1122 end_index,
1123 } => eval_iterate(ctx, *dst, *stream, *end_index, *span),
1124 Instruction::OnError { index } => {
1125 ctx.stack.error_handlers.push(ErrorHandler {
1126 handler_index: *index,
1127 error_register: None,
1128 });
1129 Ok(Continue)
1130 }
1131 Instruction::OnErrorInto { index, dst } => {
1132 ctx.stack.error_handlers.push(ErrorHandler {
1133 handler_index: *index,
1134 error_register: Some(*dst),
1135 });
1136 Ok(Continue)
1137 }
1138 Instruction::Finally { index } => {
1139 ctx.stack.finally_run_handlers.push(ErrorHandler {
1140 handler_index: *index,
1141 error_register: None,
1142 });
1143 Ok(Continue)
1144 }
1145 Instruction::FinallyInto { index, dst } => {
1146 ctx.stack.finally_run_handlers.push(ErrorHandler {
1147 handler_index: *index,
1148 error_register: Some(*dst),
1149 });
1150 Ok(Continue)
1151 }
1152 Instruction::PopErrorHandler => {
1153 ctx.stack.error_handlers.pop(ctx.error_handler_base);
1154 Ok(Continue)
1155 }
1156 Instruction::PopFinallyRun => {
1157 ctx.stack.finally_run_handlers.pop(ctx.finally_handler_base);
1158 Ok(Continue)
1159 }
1160 Instruction::ReturnEarly { src } => Ok(InstructionResult::ReturnEarly(*src)),
1161 Instruction::Return { src } => Ok(Return(*src)),
1162 }
1163}
1164
1165fn load_literal(
1167 ctx: &mut EvalContext<'_>,
1168 dst: RegId,
1169 lit: &Literal,
1170 span: Span,
1171) -> Result<InstructionResult, ShellError> {
1172 if matches!(lit, Literal::Empty) {
1178 ctx.put_reg(dst, PipelineExecutionData::empty());
1179 } else {
1180 let value = literal_value(ctx, lit, span)?;
1181 ctx.put_reg(
1182 dst,
1183 PipelineExecutionData::from(PipelineData::value(value, None)),
1184 );
1185 }
1186 Ok(InstructionResult::Continue)
1187}
1188
1189fn literal_value(
1190 ctx: &mut EvalContext<'_>,
1191 lit: &Literal,
1192 span: Span,
1193) -> Result<Value, ShellError> {
1194 Ok(match lit {
1195 Literal::Bool(b) => Value::bool(*b, span),
1196 Literal::Int(i) => Value::int(*i, span),
1197 Literal::Float(f) => Value::float(*f, span),
1198 Literal::Filesize(q) => Value::filesize(*q, span),
1199 Literal::Duration(q) => Value::duration(*q, span),
1200 Literal::Binary(bin) => Value::binary(&ctx.data[*bin], span),
1201 Literal::Block(block_id) | Literal::RowCondition(block_id) | Literal::Closure(block_id) => {
1202 let block = ctx.engine_state.get_block(*block_id);
1203 let captures = block
1204 .captures
1205 .iter()
1206 .map(|(var_id, span)| get_var(ctx, *var_id, *span).map(|val| (*var_id, val)))
1207 .collect::<Result<Vec<_>, ShellError>>()?;
1208 Value::closure(
1209 Closure {
1210 block_id: *block_id,
1211 captures,
1212 },
1213 span,
1214 )
1215 }
1216 Literal::Range {
1217 start,
1218 step,
1219 end,
1220 inclusion,
1221 } => {
1222 let start = ctx.collect_reg(*start, span)?;
1223 let step = ctx.collect_reg(*step, span)?;
1224 let end = ctx.collect_reg(*end, span)?;
1225 let range = Range::new(start, step, end, *inclusion, span)?;
1226 Value::range(range, span)
1227 }
1228 Literal::List { capacity } => Value::list(Vec::with_capacity(*capacity), span),
1229 Literal::Record { capacity } => Value::record(Record::with_capacity(*capacity), span),
1230 Literal::Filepath {
1231 val: path,
1232 no_expand,
1233 } => {
1234 let path = ctx.get_str(*path, span)?;
1235 if *no_expand {
1236 Value::string(path, span)
1237 } else {
1238 let path = expand_path(path, true);
1239 Value::string(path.to_string_lossy(), span)
1240 }
1241 }
1242 Literal::Directory {
1243 val: path,
1244 no_expand,
1245 } => {
1246 let path = ctx.get_str(*path, span)?;
1247 if path == "-" {
1248 Value::string("-", span)
1249 } else if *no_expand {
1250 Value::string(path, span)
1251 } else {
1252 let path = expand_path(path, true);
1253 Value::string(path.to_string_lossy(), span)
1254 }
1255 }
1256 Literal::GlobPattern { val, no_expand } => {
1257 Value::glob(ctx.get_str(*val, span)?, *no_expand, span)
1258 }
1259 Literal::String(s) => Value::string(ctx.get_str(*s, span)?, span),
1260 Literal::RawString(s) => Value::string(ctx.get_str(*s, span)?, span),
1261 Literal::CellPath(path) => Value::cell_path(CellPath::clone(path), span),
1262 Literal::Date(dt) => Value::date(**dt, span),
1263 Literal::Nothing => Value::nothing(span),
1264 Literal::Empty => Value::nothing(span),
1266 })
1267}
1268
1269fn binary_op(
1270 ctx: &mut EvalContext<'_>,
1271 lhs_dst: RegId,
1272 op: &Operator,
1273 rhs: RegId,
1274 span: Span,
1275) -> Result<InstructionResult, ShellError> {
1276 let lhs_val = ctx.collect_reg(lhs_dst, span)?;
1277 let rhs_val = ctx.collect_reg(rhs, span)?;
1278
1279 if let Value::Error { error, .. } = lhs_val {
1281 return Err(*error);
1282 }
1283 if let Value::Error { error, .. } = rhs_val {
1284 return Err(*error);
1285 }
1286
1287 let op_span = span;
1290
1291 let result = match op {
1292 Operator::Comparison(cmp) => match cmp {
1293 Comparison::Equal => lhs_val.eq(op_span, &rhs_val, span)?,
1294 Comparison::NotEqual => lhs_val.ne(op_span, &rhs_val, span)?,
1295 Comparison::LessThan => lhs_val.lt(op_span, &rhs_val, span)?,
1296 Comparison::GreaterThan => lhs_val.gt(op_span, &rhs_val, span)?,
1297 Comparison::LessThanOrEqual => lhs_val.lte(op_span, &rhs_val, span)?,
1298 Comparison::GreaterThanOrEqual => lhs_val.gte(op_span, &rhs_val, span)?,
1299 Comparison::RegexMatch => {
1300 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, false, span)?
1301 }
1302 Comparison::NotRegexMatch => {
1303 lhs_val.regex_match(ctx.engine_state, op_span, &rhs_val, true, span)?
1304 }
1305 Comparison::In => lhs_val.r#in(op_span, &rhs_val, span)?,
1306 Comparison::NotIn => lhs_val.not_in(op_span, &rhs_val, span)?,
1307 Comparison::Has => lhs_val.has(op_span, &rhs_val, span)?,
1308 Comparison::NotHas => lhs_val.not_has(op_span, &rhs_val, span)?,
1309 Comparison::StartsWith => lhs_val.starts_with(op_span, &rhs_val, span)?,
1310 Comparison::NotStartsWith => lhs_val.not_starts_with(op_span, &rhs_val, span)?,
1311 Comparison::EndsWith => lhs_val.ends_with(op_span, &rhs_val, span)?,
1312 Comparison::NotEndsWith => lhs_val.not_ends_with(op_span, &rhs_val, span)?,
1313 },
1314 Operator::Math(mat) => match mat {
1315 Math::Add => lhs_val.add(op_span, &rhs_val, span)?,
1316 Math::Subtract => lhs_val.sub(op_span, &rhs_val, span)?,
1317 Math::Multiply => lhs_val.mul(op_span, &rhs_val, span)?,
1318 Math::Divide => lhs_val.div(op_span, &rhs_val, span)?,
1319 Math::FloorDivide => lhs_val.floor_div(op_span, &rhs_val, span)?,
1320 Math::Modulo => lhs_val.modulo(op_span, &rhs_val, span)?,
1321 Math::Pow => lhs_val.pow(op_span, &rhs_val, span)?,
1322 Math::Concatenate => lhs_val.concat(op_span, &rhs_val, span)?,
1323 },
1324 Operator::Boolean(bl) => match bl {
1325 Boolean::Or => lhs_val.or(op_span, &rhs_val, span)?,
1326 Boolean::Xor => lhs_val.xor(op_span, &rhs_val, span)?,
1327 Boolean::And => lhs_val.and(op_span, &rhs_val, span)?,
1328 },
1329 Operator::Bits(bit) => match bit {
1330 Bits::BitOr => lhs_val.bit_or(op_span, &rhs_val, span)?,
1331 Bits::BitXor => lhs_val.bit_xor(op_span, &rhs_val, span)?,
1332 Bits::BitAnd => lhs_val.bit_and(op_span, &rhs_val, span)?,
1333 Bits::ShiftLeft => lhs_val.bit_shl(op_span, &rhs_val, span)?,
1334 Bits::ShiftRight => lhs_val.bit_shr(op_span, &rhs_val, span)?,
1335 },
1336 Operator::Assignment(_asg) => {
1337 return Err(ShellError::IrEvalError {
1338 msg: "can't eval assignment with the `binary-op` instruction".into(),
1339 span: Some(span),
1340 });
1341 }
1342 };
1343
1344 ctx.put_reg(
1345 lhs_dst,
1346 PipelineExecutionData::from(PipelineData::value(result, None)),
1347 );
1348
1349 Ok(InstructionResult::Continue)
1350}
1351
1352fn eval_call<D: DebugContext>(
1354 ctx: &mut EvalContext<'_>,
1355 decl_id: DeclId,
1356 head: Span,
1357 mut input: PipelineData,
1358) -> Result<PipelineData, ShellError> {
1359 let EvalContext {
1360 engine_state,
1361 stack: caller_stack,
1362 args_base,
1363 redirect_out,
1364 redirect_err,
1365 ..
1366 } = ctx;
1367
1368 let args_len = caller_stack.arguments.get_len(*args_base);
1369 let decl = engine_state.get_decl(decl_id);
1370 let stderr_pipe_separate = matches!(
1373 redirect_err.as_ref(),
1374 Some(Redirection::Pipe(OutDest::PipeSeparate))
1375 );
1376
1377 let mut caller_stack = caller_stack.push_redirection(redirect_out.take(), redirect_err.take());
1379
1380 let result = (|| {
1381 if let Some(block_id) = decl.block_id() {
1382 let block = engine_state.get_block(block_id);
1384
1385 check_input_types(&input, &block.signature, head)?;
1387
1388 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
1390
1391 gather_arguments(
1392 engine_state,
1393 block,
1394 &mut caller_stack,
1395 &mut callee_stack,
1396 *args_base,
1397 args_len,
1398 head,
1399 )?;
1400
1401 let mut callee_stack =
1406 callee_stack.with_invocation_stdout(caller_stack.stdout().clone());
1407
1408 callee_stack.recursion_count += 1;
1411
1412 let result =
1413 eval_block_with_early_return::<D>(engine_state, &mut callee_stack, block, input)
1414 .map(|p| p.body);
1415
1416 if block.redirect_env {
1418 redirect_env(engine_state, &mut caller_stack, &callee_stack);
1419 }
1420
1421 result
1422 } else {
1423 let allow_error_input = matches!(input, PipelineData::Value(Value::Error { .. }, ..))
1427 && engine_state
1428 .find_decl(b"ignore", &[])
1429 .is_some_and(|ignore_decl_id| ignore_decl_id == decl_id);
1430 if !allow_error_input {
1431 check_input_types(&input, &decl.signature(), head)?;
1432 }
1433 let span = Span::merge_many(
1435 std::iter::once(head).chain(
1436 caller_stack
1437 .arguments
1438 .get_args(*args_base, args_len)
1439 .iter()
1440 .flat_map(|arg| arg.span()),
1441 ),
1442 );
1443
1444 let call = Call {
1445 decl_id,
1446 head,
1447 span,
1448 args_base: *args_base,
1449 args_len,
1450 };
1451
1452 if let PipelineData::Value(v, ..) = &mut input {
1455 v.inject_signals(engine_state);
1456 }
1457 decl.run(engine_state, &mut caller_stack, &(&call).into(), input)
1459 }
1460 })();
1461
1462 drop(caller_stack);
1463
1464 ctx.stack.arguments.leave_frame(ctx.args_base);
1466 ctx.redirect_out = None;
1467 ctx.redirect_err = None;
1468
1469 match result {
1470 Err(err) if stderr_pipe_separate => Ok(PipelineData::Value(Value::error(err, head), None)),
1471 result => result,
1472 }
1473}
1474
1475fn find_named_var_id(
1476 sig: &Signature,
1477 name: &[u8],
1478 short: &[u8],
1479 span: Span,
1480) -> Result<VarId, ShellError> {
1481 sig.named
1482 .iter()
1483 .find(|n| match (n.long_name(), n.short) {
1484 (Some(long), _) => long.as_bytes() == name,
1485 (None, Some(s)) => s.encode_utf8(&mut [0; 4]).as_bytes() == short,
1487 (None, None) => false,
1488 })
1489 .ok_or_else(|| ShellError::IrEvalError {
1490 msg: format!(
1491 "block does not have an argument named `{}`",
1492 String::from_utf8_lossy(name)
1493 ),
1494 span: Some(span),
1495 })
1496 .and_then(|flag| expect_named_var_id(flag, span))
1497}
1498
1499fn expect_named_var_id(arg: &Flag, span: Span) -> Result<VarId, ShellError> {
1500 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1501 msg: format!(
1502 "block signature is missing var id for named arg `{}`",
1503 arg.long
1504 ),
1505 span: Some(span),
1506 })
1507}
1508
1509fn expect_positional_var_id(arg: &PositionalArg, span: Span) -> Result<VarId, ShellError> {
1510 arg.var_id.ok_or_else(|| ShellError::IrEvalError {
1511 msg: format!(
1512 "block signature is missing var id for positional arg `{}`",
1513 arg.name
1514 ),
1515 span: Some(span),
1516 })
1517}
1518
1519fn gather_arguments(
1521 engine_state: &EngineState,
1522 block: &Block,
1523 caller_stack: &mut Stack,
1524 callee_stack: &mut Stack,
1525 args_base: usize,
1526 args_len: usize,
1527 call_head: Span,
1528) -> Result<(), ShellError> {
1529 let mut positional_iter = block
1530 .signature
1531 .required_positional
1532 .iter()
1533 .map(|p| (p, true))
1534 .chain(
1535 block
1536 .signature
1537 .optional_positional
1538 .iter()
1539 .map(|p| (p, false)),
1540 );
1541
1542 let mut rest = vec![];
1544 let mut rest_span: Option<Span> = None;
1545
1546 let expand_glob_args = block.signature.allows_unknown_args;
1553
1554 let mut always_spread = false;
1556
1557 for arg in caller_stack.arguments.drain_args(args_base, args_len) {
1558 match arg {
1559 Argument::Positional { span, val, .. } => {
1560 let next = (!always_spread).then(|| positional_iter.next()).flatten();
1562 if let Some((positional_arg, required)) = next {
1563 let var_id = expect_positional_var_id(positional_arg, span)?;
1564 if required {
1565 let variable = engine_state.get_var(var_id);
1568 check_type(&val, &variable.ty)?;
1569 }
1570 callee_stack.add_var(var_id, val);
1571 } else {
1572 rest_span = Some(rest_span.map_or(val.span(), |s| s.append(val.span())));
1573 let val = if expand_glob_args {
1574 expand_external_glob_arg(val)
1575 } else {
1576 val
1577 };
1578 rest.push(val);
1579 }
1580 }
1581 Argument::Spread {
1582 vals,
1583 span: spread_span,
1584 ..
1585 } => match vals {
1586 Value::List { vals, .. } => {
1587 rest.extend(vals);
1588 rest_span = Some(rest_span.map_or(spread_span, |s| s.append(spread_span)));
1589 always_spread = true;
1590 }
1591 Value::Nothing { .. } => {
1592 rest_span = Some(rest_span.map_or(spread_span, |s| s.append(spread_span)));
1593 always_spread = true;
1594 }
1595 Value::Error { error, .. } => return Err(*error),
1596 _ => return Err(ShellError::CannotSpreadAsList { span: vals.span() }),
1597 },
1598 Argument::Flag {
1599 data,
1600 name,
1601 short,
1602 span,
1603 } => {
1604 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1605 callee_stack.add_var(var_id, Value::bool(true, span))
1606 }
1607 Argument::Named {
1608 data,
1609 name,
1610 short,
1611 span,
1612 val,
1613 ..
1614 } => {
1615 let var_id = find_named_var_id(&block.signature, &data[name], &data[short], span)?;
1616 callee_stack.add_var(var_id, val)
1617 }
1618 Argument::ParserInfo { .. } => (),
1619 }
1620 }
1621
1622 if let Some(rest_arg) = &block.signature.rest_positional {
1624 let rest_span = rest_span.unwrap_or(call_head);
1625 let var_id = expect_positional_var_id(rest_arg, rest_span)?;
1626 callee_stack.add_var(var_id, Value::list(rest, rest_span));
1627 }
1628
1629 for (positional_arg, _) in positional_iter {
1631 let var_id = expect_positional_var_id(positional_arg, call_head)?;
1632 callee_stack.add_var(
1633 var_id,
1634 positional_arg
1635 .default_value
1636 .clone()
1637 .unwrap_or(Value::nothing(call_head)),
1638 );
1639 }
1640
1641 for named_arg in &block.signature.named {
1642 if let Some(var_id) = named_arg.var_id {
1643 if !callee_stack.vars.iter().any(|(id, _)| *id == var_id) {
1647 let val = if named_arg.arg.is_none() {
1648 Value::bool(false, call_head)
1649 } else if let Some(value) = &named_arg.default_value {
1650 value.clone()
1651 } else {
1652 Value::nothing(call_head)
1653 };
1654 callee_stack.add_var(var_id, val);
1655 }
1656 }
1657 }
1658
1659 Ok(())
1660}
1661
1662fn check_type(val: &Value, ty: &Type) -> Result<(), ShellError> {
1664 match val {
1665 Value::Error { error, .. } => Err(*error.clone()),
1666 _ if val.is_assignable_to(ty) => Ok(()),
1667 _ => Err(ShellError::CantConvert {
1668 to_type: ty.to_string(),
1669 from_type: val.get_type().to_string(),
1670 span: val.span(),
1671 help: None,
1672 }),
1673 }
1674}
1675
1676fn check_assignment_type(
1678 val: Value,
1679 target_ty: &Type,
1680 assignment_span: Span,
1681) -> Result<Value, ShellError> {
1682 match val {
1683 Value::Error { error, .. } => Err(*error),
1684 _ if val.is_assignable_to(target_ty) => Ok(val), _ => {
1686 let expected = target_ty.to_string();
1687 let actual = val.get_type().to_string();
1688
1689 let mut err = LabeledError::new("Type mismatch.");
1690 err = err.with_code("nu::shell::type_mismatch");
1691
1692 if !(val.span() == Span::unknown() || val.span() == Span::test_data()) {
1696 err = err.with_label(format!("the value is a {actual}"), val.span());
1697 }
1698
1699 err = err.with_label(
1700 format!("expected {expected}, got {actual}"),
1701 assignment_span,
1702 );
1703
1704 Err(ShellError::LabeledError(err.into()))
1705 }
1706 }
1707}
1708
1709fn check_input_types(
1711 input: &PipelineData,
1712 signature: &Signature,
1713 head: Span,
1714) -> Result<(), ShellError> {
1715 let io_types = &signature.input_output_types;
1716
1717 if io_types.is_empty() {
1719 return Ok(());
1720 }
1721
1722 if io_types.iter().all(|(intype, _)| intype == &Type::Nothing) {
1724 return Ok(());
1725 }
1726
1727 match input {
1728 PipelineData::Value(Value::Error { error, .. }, ..) => return Err(*error.clone()),
1730 PipelineData::Value(Value::Custom { .. }, ..) => return Ok(()),
1732 _ => (),
1733 }
1734
1735 if io_types
1737 .iter()
1738 .any(|(command_type, _)| input.is_assignable_to(command_type))
1739 {
1740 return Ok(());
1741 }
1742
1743 let input_types: Vec<Type> = io_types.iter().map(|(input, _)| input.clone()).collect();
1744 let expected_string = combined_type_string(&input_types, "and");
1745
1746 match (input, expected_string) {
1747 (PipelineData::Empty, _) => Err(ShellError::PipelineEmpty { dst_span: head }),
1748 (_, Some(expected_string)) => Err(ShellError::OnlySupportsThisInputType {
1749 exp_input_type: expected_string,
1750 wrong_type: input.get_type().to_string(),
1751 dst_span: head,
1752 src_span: input.span().unwrap_or(head),
1753 }),
1754 (_, None) => Err(ShellError::NushellFailed {
1756 msg: "Command input type strings is empty, despite being non-zero earlier".to_string(),
1757 }),
1758 }
1759}
1760
1761fn get_var(ctx: &mut EvalContext<'_>, var_id: VarId, span: Span) -> Result<Value, ShellError> {
1763 match var_id {
1764 ENV_VARIABLE_ID => {
1766 let env_vars = ctx.stack.get_env_vars(ctx.engine_state);
1767 let env_columns = env_vars.keys();
1768 let env_values = env_vars.values();
1769
1770 let mut pairs = env_columns
1771 .map(|x| x.to_string())
1772 .zip(env_values.cloned())
1773 .collect::<Vec<(String, Value)>>();
1774
1775 pairs.sort_by(|a, b| a.0.cmp(&b.0));
1776
1777 Ok(Value::record(pairs.into_iter().collect(), span))
1778 }
1779 id if id == nu_protocol::LAST_VARIABLE_ID => {
1780 ctx.stack.defer_last_result_truncation_warning();
1782 ctx.stack.get_var(var_id, span)
1783 }
1784 _ => ctx.stack.get_var(var_id, span).or_else(|err| {
1785 if let Some(const_val) = ctx.engine_state.get_constant(var_id).cloned() {
1787 Ok(const_val.with_span(span))
1788 } else {
1789 Err(err)
1790 }
1791 }),
1792 }
1793}
1794
1795fn get_env_var<'a>(ctx: &'a mut EvalContext<'_>, key: &str) -> Option<&'a Value> {
1797 for overlays in ctx
1799 .stack
1800 .env_vars
1801 .iter()
1802 .rev()
1803 .chain(std::iter::once(&ctx.engine_state.env_vars))
1804 {
1805 for overlay_name in ctx.stack.active_overlays.iter().rev() {
1807 let Some(map) = overlays.get(overlay_name) else {
1808 continue;
1810 };
1811 let hidden = ctx.stack.env_hidden.get(overlay_name);
1812 let is_hidden = |key: &EnvName| hidden.is_some_and(|hidden| hidden.contains(key));
1813
1814 if let Some(val) = map
1815 .get(&EnvName::from(key))
1817 .filter(|_| !is_hidden(&EnvName::from(key)))
1819 {
1820 return Some(val);
1821 }
1822 }
1823 }
1824 None
1826}
1827
1828fn get_env_var_name<'a>(ctx: &mut EvalContext<'_>, key: &'a str) -> Cow<'a, str> {
1832 ctx.stack
1834 .env_vars
1835 .iter()
1836 .rev()
1837 .chain(std::iter::once(&ctx.engine_state.env_vars))
1838 .flat_map(|overlays| {
1839 ctx.stack
1841 .active_overlays
1842 .iter()
1843 .rev()
1844 .filter_map(|name| overlays.get(name))
1845 })
1846 .find_map(|map| {
1847 if map.contains_key(&EnvName::from(key)) {
1849 map.keys()
1851 .find(|k| k.as_str().eq_ignore_case(key))
1852 .map(|k| Cow::Owned(k.as_str().to_owned()))
1853 } else {
1854 None
1855 }
1856 })
1857 .unwrap_or(Cow::Borrowed(key))
1859}
1860
1861fn collect(
1867 pipe: PipelineExecutionData,
1868 fallback_span: Span,
1869 #[cfg(feature = "os")] ignore_error: bool,
1870) -> Result<PipelineData, ShellError> {
1871 let mut data = pipe.body;
1872 let span = data.span().unwrap_or(fallback_span);
1873 let metadata = data.take_metadata().and_then(|m| m.for_collect());
1874 #[cfg(feature = "os")]
1875 if nu_experimental::PIPE_FAIL.get() && !ignore_error {
1876 check_exit_status_future(pipe.exit)?;
1877 }
1878 let value = data.into_value(span)?;
1879 Ok(PipelineData::value(value, metadata))
1880}
1881
1882fn drain(
1884 ctx: &mut EvalContext<'_>,
1885 data: PipelineExecutionData,
1886) -> Result<InstructionResult, ShellError> {
1887 use self::InstructionResult::*;
1888
1889 match data.body {
1890 PipelineData::ByteStream(stream, ..) => {
1891 let span = stream.span();
1892 let callback_spans = stream.get_caller_spans().clone();
1893 if let Err(mut err) = stream.drain() {
1894 ctx.stack.set_last_error(&err);
1895 if callback_spans.is_empty() {
1896 return Err(err);
1897 } else {
1898 for s in callback_spans {
1899 err = ShellError::EvalBlockWithInput {
1900 span: s,
1901 sources: vec![err],
1902 }
1903 }
1904 return Err(err);
1905 }
1906 } else {
1907 ctx.stack.set_last_exit_code(0, span);
1908 }
1909 }
1910 PipelineData::ListStream(stream, ..) => {
1911 let callback_spans = stream.get_caller_spans().clone();
1912 if let Err(mut err) = stream.drain() {
1913 if callback_spans.is_empty() {
1914 return Err(err);
1915 } else {
1916 for s in callback_spans {
1917 err = ShellError::EvalBlockWithInput {
1918 span: s,
1919 sources: vec![err],
1920 }
1921 }
1922 return Err(err);
1923 }
1924 }
1925 }
1926 PipelineData::Value(..) | PipelineData::Empty => {}
1927 }
1928
1929 let pipefail = nu_experimental::PIPE_FAIL.get();
1930 if !pipefail {
1931 return Ok(Continue);
1932 }
1933 #[cfg(feature = "os")]
1934 {
1935 check_exit_status_future(data.exit).map(|_| Continue)
1936 }
1937 #[cfg(not(feature = "os"))]
1938 Ok(Continue)
1939}
1940
1941fn drain_if_end(
1943 ctx: &mut EvalContext<'_>,
1944 data: PipelineExecutionData,
1945) -> Result<PipelineData, ShellError> {
1946 let stack = &mut ctx
1947 .stack
1948 .push_redirection(ctx.redirect_out.clone(), ctx.redirect_err.clone());
1949 let result = data.body.drain_to_out_dests(ctx.engine_state, stack)?;
1950
1951 let pipefail = nu_experimental::PIPE_FAIL.get();
1952 if !pipefail {
1953 return Ok(result);
1954 }
1955 #[cfg(feature = "os")]
1956 {
1957 check_exit_status_future(data.exit).map(|_| result)
1958 }
1959 #[cfg(not(feature = "os"))]
1960 Ok(result)
1961}
1962
1963enum RedirectionStream {
1964 Out,
1965 Err,
1966}
1967
1968fn open_file(ctx: &EvalContext<'_>, path: &Value, append: bool) -> Result<Arc<File>, ShellError> {
1970 let path_expanded =
1971 expand_path_with(path.as_str()?, ctx.engine_state.cwd(Some(ctx.stack))?, true);
1972 let mut options = File::options();
1973 if append {
1974 options.append(true);
1975 } else {
1976 options.write(true).truncate(true);
1977 }
1978 let file = options
1979 .create(true)
1980 .open(&path_expanded)
1981 .map_err(|err| IoError::new(err, path.span(), path_expanded))?;
1982 Ok(Arc::new(file))
1983}
1984
1985fn eval_redirection(
1987 ctx: &mut EvalContext<'_>,
1988 mode: &RedirectMode,
1989 span: Span,
1990 which: RedirectionStream,
1991) -> Result<Option<Redirection>, ShellError> {
1992 match mode {
1993 RedirectMode::Pipe => Ok(Some(Redirection::Pipe(OutDest::Pipe))),
1994 RedirectMode::PipeSeparate => Ok(Some(Redirection::Pipe(OutDest::PipeSeparate))),
1995 RedirectMode::Value => Ok(Some(Redirection::Pipe(OutDest::Value))),
1996 RedirectMode::Null => Ok(Some(Redirection::Pipe(OutDest::Null))),
1997 RedirectMode::Inherit => Ok(Some(Redirection::Pipe(OutDest::Inherit))),
1998 RedirectMode::Print => Ok(Some(Redirection::Pipe(OutDest::Print))),
1999 RedirectMode::File { file_num } => {
2000 let file = ctx
2001 .files
2002 .get(*file_num as usize)
2003 .cloned()
2004 .flatten()
2005 .ok_or_else(|| ShellError::IrEvalError {
2006 msg: format!("Tried to redirect to file #{file_num}, but it is not open"),
2007 span: Some(span),
2008 })?;
2009 Ok(Some(Redirection::File(file)))
2010 }
2011 RedirectMode::Caller => Ok(match which {
2012 RedirectionStream::Out => ctx.stack.pipe_stdout().cloned().map(Redirection::Pipe),
2013 RedirectionStream::Err => ctx.stack.pipe_stderr().cloned().map(Redirection::Pipe),
2014 }),
2015 }
2016}
2017
2018fn eval_iterate(
2020 ctx: &mut EvalContext<'_>,
2021 dst: RegId,
2022 stream: RegId,
2023 end_index: usize,
2024 span: Span,
2025) -> Result<InstructionResult, ShellError> {
2026 let mut data = ctx.take_reg(stream);
2027 if let PipelineData::ListStream(list_stream, _) = &mut data.body {
2028 if let Some(val) = list_stream.next_value() {
2030 ctx.put_reg(dst, PipelineExecutionData::from(val.into_pipeline_data()));
2031 ctx.put_reg(stream, data); Ok(InstructionResult::Continue)
2033 } else {
2034 ctx.put_reg(dst, PipelineExecutionData::empty());
2035 Ok(InstructionResult::Branch(end_index))
2036 }
2037 } else {
2038 let metadata = data.body.take_metadata();
2041 let span = data.span().unwrap_or(span);
2042 ctx.put_reg(
2043 stream,
2044 PipelineExecutionData::from(PipelineData::list_stream(
2045 ListStream::new(data.body.into_iter(), span, Signals::EMPTY),
2046 metadata,
2047 )),
2048 );
2049 eval_iterate(ctx, dst, stream, end_index, span)
2050 }
2051}
2052
2053fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
2055 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
2058
2059 for var in caller_env_vars.iter() {
2062 if !callee_stack.has_env_var(engine_state, var) {
2063 caller_stack.hide_env_var(engine_state, var);
2064 }
2065 }
2066
2067 for (var, value) in callee_stack.get_stack_env_vars() {
2069 caller_stack.add_env_var(var, value);
2070 }
2071
2072 caller_stack.config.clone_from(&callee_stack.config);
2074}