Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Enhancements
* Rhai Grain VM is now at par or faster than the AST interpreter for scripts with callbacks ([`#1159`](https://github.com/rhaiscript/rhai/pull/1159)).
* `rhai-run` now supports loading and executing Rhai Grain bytecodes if the `grain` feature is enabled ([`#1160`](https://github.com/rhaiscript/rhai/pull/1160)).
* The example `grain_dump` is now split into two CLI tools in `bin`: `grain-compile`, which compiles a Rhai script into Rhai Grain bytecodes, and `grain-dump` which dissembles a Rhai Grain bytecodes files ([`#1160`](https://github.com/rhaiscript/rhai/pull/1160)).
* `grain-dump` now disassembles Rhai Grain bytecodes files with more complete information ([`#1168`](https://github.com/rhaiscript/rhai/pull/1168)).


Version 1.26.0
Expand Down
9 changes: 7 additions & 2 deletions src/bin/grain-dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fn dump(program: &Program, code: &[u8], name: &str, chunk: &rhai::grain::bytecod
(Some(line), Some(col)) => format!("{line}:{col}"),
_ => String::new(),
};
println!(" {at:>5} {:<8} {op:?}", where_);
println!(" {at:>5} {where_:<8} {}", op.disassemble(program));
}
}

Expand All @@ -35,7 +35,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
dump(&program, code, "main", program.main());

for f in program.functions() {
let label = format!("fn #{} ({} params)", f.name, f.params.len());
let label = format!(
"fn {} : #{} ({} params)",
f.disassemble(&program),
f.name,
f.params.len(),
);
dump(&program, code, &label, &f.chunk);
}

Expand Down
89 changes: 87 additions & 2 deletions src/grain/bytecode/chain.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use crate::grain::Program;

use bitflags::bitflags;

#[cfg(feature = "no_std")]
use std::prelude::v1::*;

bitflags! {
/// Per-step flags for a chain.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down Expand Up @@ -108,6 +110,57 @@ impl Step {
Step::Property { .. } | Step::Method { .. } => 1,
}
}

/// Dump the disassembly of the step.
pub fn disassemble(&self, program: &Program) -> String {
match self {
Step::Index { operand, flags, .. } => {
format!(
"{}[{operand}]",
if flags.contains(StepFlags::SKIP_IF_UNIT) {
"?"
} else {
""
}
)
}
Step::Property { name, flags, .. } => {
format!(
".{}{}",
if flags.contains(StepFlags::SKIP_IF_UNIT) {
"?"
} else {
""
},
program.name(*name).unwrap()
)
}
Step::Method {
name,
argc,
operand,
flags,
..
} if *argc > 0 => format!(
".{}{}({argc} args from {operand})",
if flags.contains(StepFlags::SKIP_IF_UNIT) {
"?"
} else {
""
},
program.name(*name).unwrap()
),
Step::Method { name, flags, .. } => format!(
".{}{}()",
if flags.contains(StepFlags::SKIP_IF_UNIT) {
"?"
} else {
""
},
program.name(*name).unwrap()
),
}
}
}

/// What a chain does when it gets to the end.
Expand All @@ -123,6 +176,19 @@ pub enum Tail {
},
}

impl Tail {
/// Dump the disassembly of the tail.
pub fn disassemble(&self, program: &Program) -> String {
match self {
Tail::Read => String::new(),
Tail::Assign { op } => match op {
Some(op) => format!("{}", program.assign_op(*op).unwrap().disassemble(program)),
None => "=".to_string(),
},
}
}
}

/// Where a chain starts.
///
/// The distinction is whether the root has an identity to write back into.
Expand Down Expand Up @@ -333,4 +399,23 @@ impl Chain {
.iter()
.any(|step| matches!(step, Step::Method { .. }))
}

/// Dump the disassembly of the entire chain.
pub fn disassemble(&self, program: &Program) -> String {
format!(
"{:?} {} {} {}",
self.root,
match self.root {
Root::Named { name, .. } | Root::Local { name, .. } => program.name(name).unwrap(),
Root::This { .. } => "this",
Root::Temporary => "<temp value>",
},
self.steps
.iter()
.map(|step| step.disassemble(program))
.collect::<Vec<_>>()
.join(" "),
self.tail.disassemble(program)
)
}
}
92 changes: 92 additions & 0 deletions src/grain/bytecode/op.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::grain::Program;
use crate::types::Token;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;

/// What `x op= y` needs to reproduce Rhai's resolution order.
///
Expand All @@ -22,6 +25,19 @@ pub struct AssignOp {
pub op_name: u32,
}

impl AssignOp {
/// Dump the disassembly of the operation.
pub fn disassemble(&self, program: &Program) -> String {
format!(
"{} ({:?}) / {} ({:?})",
program.name(self.op_assign_name).unwrap(),
self.op_assign,
program.name(self.op_name).unwrap(),
self.op,
)
}
}

/// Where [`Op::CallRef`] finds the variable it calls through.
///
/// The two differ in how the variable is reached, not in what happens to it:
Expand Down Expand Up @@ -643,3 +659,79 @@ pub enum Op {
/// End the chunk, yielding the top of the operand stack, or unit if empty.
Return,
}

impl Op {
/// Dump the disassembly of the operation.
pub fn disassemble(&self, program: &Program) -> String {
match self {
Op::Const(idx) => format!("{self:?} = {}", program.constant(*idx).unwrap()),
Op::LoadNamed(name) => format!("{self:?} : {}", program.name(*name).unwrap()),
Op::AssignNamed { name, op } => {
if let Some(op) = op {
format!(
"{self:?} : {} {}",
program.name(*name).unwrap(),
program.assign_op(*op).unwrap().disassemble(program)
)
} else {
format!("{self:?} : {}", program.name(*name).unwrap(),)
}
}

Op::AssignLocal { var_name, op, .. } => {
if let Some(op) = op {
format!(
"{self:?} : {} {}",
program.name(*var_name).unwrap(),
program.assign_op(*op).unwrap().disassemble(program)
)
} else {
format!("{self:?} : {}", program.name(*var_name).unwrap(),)
}
}
Op::DeclareLocal { name, is_const } => {
format!(
"{self:?} : {} {}",
if *is_const { "const" } else { "let" },
program.name(*name).unwrap()
)
}
Op::Call { name, op, .. } => {
if let Some(op) = op {
format!(
"{self:?} : {} ({:?})",
program.name(*name).unwrap(),
program.token(*op).unwrap()
)
} else {
format!("{self:?} : {}", program.name(*name).unwrap(),)
}
}

Op::CallRef { name, .. } => format!("{self:?} : {}", program.name(*name).unwrap(),),
Op::Switch(idx) => format!(
"Switch({idx}) {}",
program.switch(*idx).unwrap().disassemble(program),
),
Op::ShareNamed(name) => format!("{self:?} : {}", program.name(*name).unwrap()),
Op::LoadSharedNamed(name) => format!("{self:?} : {}", program.name(*name).unwrap()),
Op::AssignThis { op, .. } if op.is_some() => {
format!(
"{self:?} : this {:?}",
program.assign_op(op.unwrap()).unwrap().disassemble(program)
)
}
Op::MakeClosure(name) => format!("{self:?} : {}", program.name(*name).unwrap(),),
Op::Chain(idx) => {
let chain = program.chain(*idx).unwrap();
format!(
"Chain({idx}, {} total operands): {}",
chain.operands,
chain.disassemble(program),
)
}

_ => format!("{self:?}"),
}
}
}
35 changes: 33 additions & 2 deletions src/grain/bytecode/switch.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::func::{calc_switch_value_hash, StraightHashMap};
use crate::grain::Program;
use crate::{eval::RangeCase, Dynamic, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
Expand All @@ -25,6 +26,26 @@ pub struct Switch {
pub default: u32,
}

impl Switch {
/// Dump the disassembly of the operation.
pub fn disassemble(&self, program: &Program) -> String {
format!(
"{{ {} }}",
self.cases
.iter()
.flat_map(|cases| cases.iter())
.map(|(_, (target, value))| format!(
"case {} => {target}",
program.constant(*value).unwrap()
))
.chain(self.ranges.iter().map(|r| r.disassemble()))
.chain(std::iter::once(format!("default => {}", self.default)))
.collect::<Vec<_>>()
.join(", ")
)
}
}

/// One `a..b => ...` arm.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SwitchRange {
Expand All @@ -39,8 +60,18 @@ pub struct SwitchRange {
}

impl SwitchRange {
/// Whether a subject falls in this range.
///
/// Dump the disassembly of the switch range.
pub fn disassemble(&self) -> String {
if self.inclusive {
format!("range {}..={} => {}", self.from, self.to, self.target)
} else {
format!("range {}..{} => {}", self.from, self.to, self.target)
}
}
}

impl SwitchRange {
/// Whether a subject falls in this range. ///
/// Delegates to Rhai's own `RangeCase` rather than comparing integers,
/// because a range arm matches more than integers: `switch 5.5 { 0..10 =>
/// .. }` matches, and under the `decimal` feature so does a `Decimal`
Expand Down
16 changes: 16 additions & 0 deletions src/grain/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ pub struct Function {
pub chunk: Chunk,
}

impl Function {
pub fn disassemble(&self, program: &Program) -> String {
format!(
"{}{}{}({})",
self.this_type.map_or("", |t| program.name(t).unwrap()),
if self.this_type.is_some() { "!" } else { "" },
program.name(self.name).unwrap(),
self.params
.iter()
.map(|&p| program.name(p).unwrap())
.collect::<Vec<_>>()
.join(", "),
)
}
}

/// A compiled script, ready to run against an `Engine`.
///
/// Owns everything execution needs that is not the `Engine` itself, so the
Expand Down
Loading