mirror of
https://github.com/nushell/nushell.git
synced 2024-11-25 18:03:51 +01:00
Add more argument types to view ir
(#13343)
# Description Add a few more options to `view ir` for finding blocks, which I found myself wanting while trying to trace through the generated code. If we end up adding support for plugins to call commands that are in scope by name, this will also make it possible for `nu_plugin_explore_ir` to just step through IR automatically (by passing the block/decl ids) without exposing too many internals. With that I could potentially add keys that allow you to step in to closures or decls with the press of a button, just by calling `view ir --json` appropriately. # User-Facing Changes - `view ir` can now take names of custom commands that are in scope. - integer arguments are treated as block IDs, which sometimes show up in IR (closure, block, row condition literals). - `--decl-id` provided to treat the argument as a decl ID instead, which is also sometimes necessary to access something that isn't in scope.
This commit is contained in:
parent
d97512df8e
commit
9de7f931c0
@ -1,5 +1,4 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::engine::Closure;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ViewIr;
|
pub struct ViewIr;
|
||||||
@ -12,15 +11,20 @@ impl Command for ViewIr {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build(self.name())
|
Signature::build(self.name())
|
||||||
.required(
|
.required(
|
||||||
"closure",
|
"target",
|
||||||
SyntaxShape::Closure(None),
|
SyntaxShape::Any,
|
||||||
"The closure to see compiled code for.",
|
"The name or block to view compiled code for.",
|
||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
"json",
|
"json",
|
||||||
"Dump the raw block data as JSON (unstable).",
|
"Dump the raw block data as JSON (unstable).",
|
||||||
Some('j'),
|
Some('j'),
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"decl-id",
|
||||||
|
"Integer is a declaration ID rather than a block ID.",
|
||||||
|
Some('d'),
|
||||||
|
)
|
||||||
.input_output_type(Type::Nothing, Type::String)
|
.input_output_type(Type::Nothing, Type::String)
|
||||||
.category(Category::Debug)
|
.category(Category::Debug)
|
||||||
}
|
}
|
||||||
@ -29,6 +33,20 @@ impl Command for ViewIr {
|
|||||||
"View the compiled IR code for a block of code."
|
"View the compiled IR code for a block of code."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
"
|
||||||
|
The target can be a closure, the name of a custom command, or an internal block
|
||||||
|
ID. Closure literals within IR dumps often reference the block by ID (e.g.
|
||||||
|
`closure(3231)`), so this provides an easy way to read the IR of any embedded
|
||||||
|
closures.
|
||||||
|
|
||||||
|
The --decl-id option is provided to use a declaration ID instead, which can be
|
||||||
|
found on `call` instructions. This is sometimes better than using the name, as
|
||||||
|
the declaration may not be in scope.
|
||||||
|
"
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -36,10 +54,79 @@ impl Command for ViewIr {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let closure: Closure = call.req(engine_state, stack, 0)?;
|
let target: Value = call.req(engine_state, stack, 0)?;
|
||||||
let json = call.has_flag(engine_state, stack, "json")?;
|
let json = call.has_flag(engine_state, stack, "json")?;
|
||||||
|
let is_decl_id = call.has_flag(engine_state, stack, "decl-id")?;
|
||||||
|
|
||||||
|
let block_id = match target {
|
||||||
|
Value::Closure { ref val, .. } => val.block_id,
|
||||||
|
// Decl by name
|
||||||
|
Value::String { ref val, .. } => {
|
||||||
|
if let Some(decl_id) = engine_state.find_decl(val.as_bytes(), &[]) {
|
||||||
|
let decl = engine_state.get_decl(decl_id);
|
||||||
|
decl.block_id().ok_or_else(|| ShellError::GenericError {
|
||||||
|
error: format!("Can't view IR for `{val}`"),
|
||||||
|
msg: "not a custom command".into(),
|
||||||
|
span: Some(target.span()),
|
||||||
|
help: Some("internal commands don't have Nushell source code".into()),
|
||||||
|
inner: vec![],
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
return Err(ShellError::GenericError {
|
||||||
|
error: format!("Can't view IR for `{val}`"),
|
||||||
|
msg: "can't find a command with this name".into(),
|
||||||
|
span: Some(target.span()),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decl by ID - IR dump always shows name of decl, but sometimes it isn't in scope
|
||||||
|
Value::Int { val, .. } if is_decl_id => {
|
||||||
|
let decl_id = val
|
||||||
|
.try_into()
|
||||||
|
.ok()
|
||||||
|
.filter(|id| *id < engine_state.num_decls())
|
||||||
|
.ok_or_else(|| ShellError::IncorrectValue {
|
||||||
|
msg: "not a valid decl id".into(),
|
||||||
|
val_span: target.span(),
|
||||||
|
call_span: call.head,
|
||||||
|
})?;
|
||||||
|
let decl = engine_state.get_decl(decl_id);
|
||||||
|
decl.block_id().ok_or_else(|| ShellError::GenericError {
|
||||||
|
error: format!("Can't view IR for `{}`", decl.name()),
|
||||||
|
msg: "not a custom command".into(),
|
||||||
|
span: Some(target.span()),
|
||||||
|
help: Some("internal commands don't have Nushell source code".into()),
|
||||||
|
inner: vec![],
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
// Block by ID - often shows up in IR
|
||||||
|
Value::Int { val, .. } => val.try_into().map_err(|_| ShellError::IncorrectValue {
|
||||||
|
msg: "not a valid block id".into(),
|
||||||
|
val_span: target.span(),
|
||||||
|
call_span: call.head,
|
||||||
|
})?,
|
||||||
|
// Pass through errors
|
||||||
|
Value::Error { error, .. } => return Err(*error),
|
||||||
|
_ => {
|
||||||
|
return Err(ShellError::TypeMismatch {
|
||||||
|
err_message: "expected closure, string, or int".into(),
|
||||||
|
span: call.head,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(block) = engine_state.try_get_block(block_id) else {
|
||||||
|
return Err(ShellError::GenericError {
|
||||||
|
error: format!("Unknown block ID: {block_id}"),
|
||||||
|
msg: "ensure the block ID is correct and try again".into(),
|
||||||
|
span: Some(target.span()),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
let block = engine_state.get_block(closure.block_id);
|
|
||||||
let ir_block = block
|
let ir_block = block
|
||||||
.ir_block
|
.ir_block
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -63,7 +150,7 @@ impl Command for ViewIr {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
serde_json::to_string_pretty(&serde_json::json!({
|
serde_json::to_string_pretty(&serde_json::json!({
|
||||||
"block_id": closure.block_id,
|
"block_id": block_id,
|
||||||
"span": block.span,
|
"span": block.span,
|
||||||
"ir_block": ir_block,
|
"ir_block": ir_block,
|
||||||
"formatted_instructions": formatted_instructions,
|
"formatted_instructions": formatted_instructions,
|
||||||
|
Loading…
Reference in New Issue
Block a user