add explicit numbering for allocated files, fix some err redirection stuff
This commit is contained in:
parent
2807717245
commit
38884eec73
|
@ -11,6 +11,7 @@ pub(crate) struct BlockBuilder {
|
|||
pub(crate) data: Vec<u8>,
|
||||
pub(crate) ast: Vec<Option<IrAstRef>>,
|
||||
pub(crate) register_allocation_state: Vec<bool>,
|
||||
pub(crate) file_count: u32,
|
||||
}
|
||||
|
||||
impl BlockBuilder {
|
||||
|
@ -22,6 +23,7 @@ impl BlockBuilder {
|
|||
data: vec![],
|
||||
ast: vec![],
|
||||
register_allocation_state: vec![true],
|
||||
file_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,9 +172,13 @@ impl BlockBuilder {
|
|||
Instruction::PushParserInfo { name: _, info: _ } => Ok(()),
|
||||
Instruction::RedirectOut { mode: _ } => Ok(()),
|
||||
Instruction::RedirectErr { mode: _ } => Ok(()),
|
||||
Instruction::OpenFile { path, append: _ } => allocate(&[*path], &[]),
|
||||
Instruction::WriteFile { src } => allocate(&[*src], &[]),
|
||||
Instruction::CloseFile => Ok(()),
|
||||
Instruction::OpenFile {
|
||||
file_num: _,
|
||||
path,
|
||||
append: _,
|
||||
} => allocate(&[*path], &[]),
|
||||
Instruction::WriteFile { file_num: _, src } => allocate(&[*src], &[]),
|
||||
Instruction::CloseFile { file_num: _ } => Ok(()),
|
||||
Instruction::Call {
|
||||
decl_id: _,
|
||||
src_dst,
|
||||
|
@ -402,6 +408,16 @@ impl BlockBuilder {
|
|||
self.instructions.len()
|
||||
}
|
||||
|
||||
/// Allocate a new file number, for redirection.
|
||||
pub(crate) fn next_file_num(&mut self) -> Result<u32, CompileError> {
|
||||
let next = self.file_count;
|
||||
self.file_count = self
|
||||
.file_count
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| CompileError::FileOverflow)?;
|
||||
Ok(next)
|
||||
}
|
||||
|
||||
/// Consume the builder and produce the final [`IrBlock`].
|
||||
pub(crate) fn finish(self) -> IrBlock {
|
||||
IrBlock {
|
||||
|
@ -409,7 +425,12 @@ impl BlockBuilder {
|
|||
spans: self.spans,
|
||||
data: self.data.into(),
|
||||
ast: self.ast,
|
||||
register_count: self.register_allocation_state.len(),
|
||||
register_count: self
|
||||
.register_allocation_state
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("register count overflowed in finish() despite previous checks"),
|
||||
file_count: self.file_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,9 +113,20 @@ fn compile_pipeline(
|
|||
// element, then it's from whatever is passed in as the mode to use.
|
||||
|
||||
let next_redirect_modes = if let Some(next_element) = iter.peek() {
|
||||
// If there's a next element we always pipe out
|
||||
redirect_modes_of_expression(working_set, &next_element.expr, span)?
|
||||
.with_pipe_out(next_element.pipe.unwrap_or(next_element.expr.span))
|
||||
// If there's a next element we always pipe out *unless* this is a single redirection
|
||||
// to stderr (e>|)
|
||||
let modes = redirect_modes_of_expression(working_set, &next_element.expr, span)?;
|
||||
if matches!(
|
||||
element.redirection,
|
||||
Some(PipelineRedirection::Single {
|
||||
source: RedirectionSource::Stderr,
|
||||
..
|
||||
})
|
||||
) {
|
||||
modes
|
||||
} else {
|
||||
modes.with_pipe_out(next_element.pipe.unwrap_or(next_element.expr.span))
|
||||
}
|
||||
} else {
|
||||
redirect_modes
|
||||
.take()
|
||||
|
|
|
@ -55,6 +55,7 @@ pub(crate) fn redirection_target_to_mode(
|
|||
append,
|
||||
span: redir_span,
|
||||
} => {
|
||||
let file_num = builder.next_file_num()?;
|
||||
let path_reg = builder.next_register()?;
|
||||
compile_expression(
|
||||
working_set,
|
||||
|
@ -66,12 +67,13 @@ pub(crate) fn redirection_target_to_mode(
|
|||
)?;
|
||||
builder.push(
|
||||
Instruction::OpenFile {
|
||||
file_num,
|
||||
path: path_reg,
|
||||
append: *append,
|
||||
}
|
||||
.into_spanned(*redir_span),
|
||||
)?;
|
||||
RedirectMode::File.into_spanned(*redir_span)
|
||||
RedirectMode::File { file_num }.into_spanned(*redir_span)
|
||||
}
|
||||
RedirectionTarget::Pipe { span } => (if separate {
|
||||
RedirectMode::Capture
|
||||
|
@ -108,22 +110,28 @@ pub(crate) fn finish_redirection(
|
|||
) -> Result<(), CompileError> {
|
||||
match modes.out {
|
||||
Some(Spanned {
|
||||
item: RedirectMode::File,
|
||||
item: RedirectMode::File { file_num },
|
||||
span,
|
||||
}) => {
|
||||
builder.push(Instruction::WriteFile { src: out_reg }.into_spanned(span))?;
|
||||
builder.push(
|
||||
Instruction::WriteFile {
|
||||
file_num,
|
||||
src: out_reg,
|
||||
}
|
||||
.into_spanned(span),
|
||||
)?;
|
||||
builder.load_empty(out_reg)?;
|
||||
builder.push(Instruction::CloseFile.into_spanned(span))?;
|
||||
builder.push(Instruction::CloseFile { file_num }.into_spanned(span))?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match modes.err {
|
||||
Some(Spanned {
|
||||
item: RedirectMode::File,
|
||||
item: RedirectMode::File { file_num },
|
||||
span,
|
||||
}) => {
|
||||
builder.push(Instruction::CloseFile.into_spanned(span))?;
|
||||
builder.push(Instruction::CloseFile { file_num }.into_spanned(span))?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
@ -41,9 +41,13 @@ pub fn eval_ir_block<D: DebugContext>(
|
|||
|
||||
// Allocate and initialize registers. I've found that it's not really worth trying to avoid
|
||||
// the heap allocation here by reusing buffers - our allocator is fast enough
|
||||
let mut registers = Vec::with_capacity(ir_block.register_count);
|
||||
let empty = std::iter::repeat_with(|| PipelineData::Empty);
|
||||
registers.extend(empty.take(ir_block.register_count));
|
||||
let mut registers = Vec::with_capacity(ir_block.register_count as usize);
|
||||
for _ in 0..ir_block.register_count {
|
||||
registers.push(PipelineData::Empty);
|
||||
}
|
||||
|
||||
// Initialize file storage.
|
||||
let mut files = vec![None; ir_block.file_count as usize];
|
||||
|
||||
let result = eval_ir_block_impl::<D>(
|
||||
&mut EvalContext {
|
||||
|
@ -55,9 +59,9 @@ pub fn eval_ir_block<D: DebugContext>(
|
|||
error_handler_base,
|
||||
redirect_out: None,
|
||||
redirect_err: None,
|
||||
file_stack: vec![],
|
||||
matches: vec![],
|
||||
registers: &mut registers[..],
|
||||
files: &mut files[..],
|
||||
},
|
||||
ir_block,
|
||||
input,
|
||||
|
@ -96,11 +100,12 @@ struct EvalContext<'a> {
|
|||
redirect_out: Option<Redirection>,
|
||||
/// State set by redirect-err
|
||||
redirect_err: Option<Redirection>,
|
||||
/// Files used for redirection
|
||||
file_stack: Vec<Arc<File>>,
|
||||
/// Scratch space to use for `match`
|
||||
matches: Vec<(VarId, Value)>,
|
||||
/// Intermediate pipeline data storage used by instructions, indexed by RegId
|
||||
registers: &'a mut [PipelineData],
|
||||
/// Holds open files used by redirections
|
||||
files: &'a mut [Option<Arc<File>>],
|
||||
}
|
||||
|
||||
impl<'a> EvalContext<'a> {
|
||||
|
@ -389,20 +394,25 @@ fn eval_instruction<D: DebugContext>(
|
|||
ctx.redirect_err = eval_redirection(ctx, mode, *span, RedirectionStream::Err)?;
|
||||
Ok(Continue)
|
||||
}
|
||||
Instruction::OpenFile { path, append } => {
|
||||
Instruction::OpenFile {
|
||||
file_num,
|
||||
path,
|
||||
append,
|
||||
} => {
|
||||
let path = ctx.collect_reg(*path, *span)?;
|
||||
let file = open_file(ctx, &path, *append)?;
|
||||
ctx.file_stack.push(file);
|
||||
ctx.files[*file_num as usize] = Some(file);
|
||||
Ok(Continue)
|
||||
}
|
||||
Instruction::WriteFile { src } => {
|
||||
Instruction::WriteFile { file_num, src } => {
|
||||
let src = ctx.take_reg(*src);
|
||||
let file = ctx
|
||||
.file_stack
|
||||
.last()
|
||||
.files
|
||||
.get(*file_num as usize)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.ok_or_else(|| ShellError::IrEvalError {
|
||||
msg: "Tried to write file without opening a file first".into(),
|
||||
msg: format!("Tried to write to file #{file_num}, but it is not open"),
|
||||
span: Some(*span),
|
||||
})?;
|
||||
let mut stack = ctx
|
||||
|
@ -411,12 +421,12 @@ fn eval_instruction<D: DebugContext>(
|
|||
src.write_to_out_dests(ctx.engine_state, &mut stack)?;
|
||||
Ok(Continue)
|
||||
}
|
||||
Instruction::CloseFile => {
|
||||
if ctx.file_stack.pop().is_some() {
|
||||
Instruction::CloseFile { file_num } => {
|
||||
if ctx.files[*file_num as usize].take().is_some() {
|
||||
Ok(Continue)
|
||||
} else {
|
||||
Err(ShellError::IrEvalError {
|
||||
msg: "Tried to close file without opening a file first".into(),
|
||||
msg: format!("Tried to close file #{file_num}, but it is not open"),
|
||||
span: Some(*span),
|
||||
})
|
||||
}
|
||||
|
@ -1153,13 +1163,14 @@ fn eval_redirection(
|
|||
RedirectMode::Capture => Ok(Some(Redirection::Pipe(OutDest::Capture))),
|
||||
RedirectMode::Null => Ok(Some(Redirection::Pipe(OutDest::Null))),
|
||||
RedirectMode::Inherit => Ok(Some(Redirection::Pipe(OutDest::Inherit))),
|
||||
RedirectMode::File => {
|
||||
RedirectMode::File { file_num } => {
|
||||
let file = ctx
|
||||
.file_stack
|
||||
.last()
|
||||
.files
|
||||
.get(*file_num as usize)
|
||||
.cloned()
|
||||
.flatten()
|
||||
.ok_or_else(|| ShellError::IrEvalError {
|
||||
msg: "Tried to redirect to file without opening a file first".into(),
|
||||
msg: format!("Tried to redirect to file #{file_num}, but it is not open"),
|
||||
span: Some(span),
|
||||
})?;
|
||||
Ok(Some(Redirection::File(file)))
|
||||
|
|
|
@ -41,6 +41,13 @@ pub enum CompileError {
|
|||
)]
|
||||
DataOverflow,
|
||||
|
||||
#[error("Block contains too many files.")]
|
||||
#[diagnostic(
|
||||
code(nu::compile::register_overflow),
|
||||
help("try using fewer file redirections")
|
||||
)]
|
||||
FileOverflow,
|
||||
|
||||
#[error("Invalid redirect mode: File should not be specified by commands.")]
|
||||
#[diagnostic(code(nu::compile::invalid_redirect_mode), help("this is a command bug. Please report it at https://github.com/nushell/nushell/issues/new"))]
|
||||
InvalidRedirectMode,
|
||||
|
|
|
@ -16,12 +16,20 @@ impl<'a> fmt::Display for FmtIrBlock<'a> {
|
|||
f,
|
||||
"# {} register{}, {} instruction{}, {} byte{} of data",
|
||||
self.ir_block.register_count,
|
||||
plural(self.ir_block.register_count),
|
||||
plural(self.ir_block.register_count as usize),
|
||||
self.ir_block.instructions.len(),
|
||||
plural(self.ir_block.instructions.len()),
|
||||
self.ir_block.data.len(),
|
||||
plural(self.ir_block.data.len()),
|
||||
)?;
|
||||
if self.ir_block.file_count > 0 {
|
||||
writeln!(
|
||||
f,
|
||||
"# {} file{} used for redirection",
|
||||
self.ir_block.file_count,
|
||||
plural(self.ir_block.file_count as usize)
|
||||
)?;
|
||||
}
|
||||
for (index, instruction) in self.ir_block.instructions.iter().enumerate() {
|
||||
writeln!(
|
||||
f,
|
||||
|
@ -119,14 +127,22 @@ impl<'a> fmt::Display for FmtInstruction<'a> {
|
|||
Instruction::RedirectErr { mode } => {
|
||||
write!(f, "{:WIDTH$} {mode}", "redirect-err")
|
||||
}
|
||||
Instruction::OpenFile { path, append } => {
|
||||
write!(f, "{:WIDTH$} {path}, append = {append:?}", "open-file")
|
||||
Instruction::OpenFile {
|
||||
file_num,
|
||||
path,
|
||||
append,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"{:WIDTH$} file({file_num}), {path}, append = {append:?}",
|
||||
"open-file"
|
||||
)
|
||||
}
|
||||
Instruction::WriteFile { src } => {
|
||||
write!(f, "{:WIDTH$} {src}", "write-file")
|
||||
Instruction::WriteFile { file_num, src } => {
|
||||
write!(f, "{:WIDTH$} file({file_num}), {src}", "write-file")
|
||||
}
|
||||
Instruction::CloseFile => {
|
||||
write!(f, "{:WIDTH$}", "close-file")
|
||||
Instruction::CloseFile { file_num } => {
|
||||
write!(f, "{:WIDTH$} file({file_num})", "close-file")
|
||||
}
|
||||
Instruction::Call { decl_id, src_dst } => {
|
||||
let decl = FmtDecl::new(self.engine_state, *decl_id);
|
||||
|
@ -262,7 +278,7 @@ impl fmt::Display for RedirectMode {
|
|||
RedirectMode::Capture => write!(f, "capture"),
|
||||
RedirectMode::Null => write!(f, "null"),
|
||||
RedirectMode::Inherit => write!(f, "inherit"),
|
||||
RedirectMode::File => write!(f, "file"),
|
||||
RedirectMode::File { file_num } => write!(f, "file({file_num})"),
|
||||
RedirectMode::Caller => write!(f, "caller"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ pub struct IrBlock {
|
|||
#[serde(with = "serde_arc_u8_array")]
|
||||
pub data: Arc<[u8]>,
|
||||
pub ast: Vec<Option<IrAstRef>>,
|
||||
pub register_count: usize,
|
||||
pub register_count: u32,
|
||||
pub file_count: u32,
|
||||
}
|
||||
|
||||
impl fmt::Debug for IrBlock {
|
||||
|
@ -33,6 +34,7 @@ impl fmt::Debug for IrBlock {
|
|||
.field("spans", &self.spans)
|
||||
.field("data", &self.data)
|
||||
.field("register_count", &self.register_count)
|
||||
.field("file_count", &self.register_count)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
@ -143,12 +145,16 @@ pub enum Instruction {
|
|||
/// The register for a file redirection is not consumed.
|
||||
RedirectErr { mode: RedirectMode },
|
||||
/// Open a file for redirection, pushing it onto the file stack.
|
||||
OpenFile { path: RegId, append: bool },
|
||||
OpenFile {
|
||||
file_num: u32,
|
||||
path: RegId,
|
||||
append: bool,
|
||||
},
|
||||
/// Write data from the register to a file. This is done to finish a file redirection, in case
|
||||
/// an internal command or expression was evaluated rather than an external one.
|
||||
WriteFile { src: RegId },
|
||||
WriteFile { file_num: u32, src: RegId },
|
||||
/// Pop a file used for redirection from the file stack.
|
||||
CloseFile,
|
||||
CloseFile { file_num: u32 },
|
||||
/// Make a call. The input is taken from `src_dst`, and the output is placed in `src_dst`,
|
||||
/// overwriting it. The argument stack is used implicitly and cleared when the call ends.
|
||||
Call { decl_id: DeclId, src_dst: RegId },
|
||||
|
@ -306,8 +312,10 @@ pub enum RedirectMode {
|
|||
Capture,
|
||||
Null,
|
||||
Inherit,
|
||||
/// Use the file on the top of the file stack.
|
||||
File,
|
||||
/// Use the given numbered file.
|
||||
File {
|
||||
file_num: u32,
|
||||
},
|
||||
/// Use the redirection mode requested by the caller, for a pre-return call.
|
||||
Caller,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user