Skip to content

Commit

Permalink
feat: bytecode disassembly of class methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Hirevo committed Oct 31, 2023
1 parent eef4502 commit c23db27
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 68 deletions.
4 changes: 2 additions & 2 deletions som-interpreter-bc/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ use som_core::bytecode::Bytecode;
use crate::class::Class;
use crate::compiler::Literal;
use crate::frame::Frame;
use crate::interner::Interned;
use crate::method::Method;
use crate::universe::Universe;
use crate::value::Value;
use crate::SOMRef;

#[derive(Clone)]
pub struct BlockInfo {
pub locals: Vec<Value>,
pub locals: Vec<Interned>,
pub literals: Vec<Literal>,
pub body: Vec<Bytecode>,
pub nb_params: usize,
Expand Down
16 changes: 14 additions & 2 deletions som-interpreter-bc/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,14 @@ fn compile_method(outer: &mut dyn GenCtxt, defn: &ast::MethodDef) -> Option<Meth
kind: match &defn.body {
ast::MethodBody::Primitive => MethodKind::NotImplemented(defn.signature.clone()),
ast::MethodBody::Body { .. } => {
let locals = {
let locals = std::mem::take(&mut ctxt.inner.locals);
locals
.into_iter()
.map(|name| ctxt.intern_symbol(&name))
.collect()
};
let body = ctxt.inner.body.unwrap_or_default();
let locals = ctxt.inner.locals.iter().map(|_| Value::Nil).collect();
let literals = ctxt.inner.literals.into_iter().collect();
let inline_cache = RefCell::new(vec![None; body.len()]);

Expand Down Expand Up @@ -450,7 +456,13 @@ fn compile_block(outer: &mut dyn GenCtxt, defn: &ast::Block) -> Option<Block> {
}

let frame = None;
let locals = ctxt.locals.into_iter().map(|_| Value::Nil).collect();
let locals = {
let locals = std::mem::take(&mut ctxt.locals);
locals
.into_iter()
.map(|name| ctxt.intern_symbol(&name))
.collect()
};
let literals = ctxt.literals.into_iter().collect();
let body = ctxt.body.unwrap_or_default();
let nb_params = ctxt.args.len();
Expand Down
159 changes: 159 additions & 0 deletions som-interpreter-bc/src/disassembler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use som_core::bytecode::Bytecode;

use crate::block::Block;
use crate::class::Class;
use crate::compiler::Literal;
use crate::interner::Interned;
use crate::method::MethodEnv;
use crate::universe::Universe;

pub fn disassemble_method_body(universe: &Universe, class: &Class, env: &MethodEnv) {
disassemble_body(universe, class, 1, &mut vec![env])
}

fn disassemble_body(
universe: &Universe,
class: &Class,
level: usize,
env: &mut Vec<&dyn FrameEnv>,
) {
let padding = " |".repeat(level);
let current = env.last().copied().unwrap();
for bytecode in current.get_body().into_iter().copied() {
print!("{padding} {0}", bytecode.padded_name());

match bytecode {
Bytecode::Halt => {
println!();
}
Bytecode::Dup => {
println!();
}
Bytecode::PushLocal(up_idx, idx) | Bytecode::PopLocal(up_idx, idx) => {
print!(" {up_idx}, {idx}");
let maybe_local = (env.iter().rev().nth(usize::from(up_idx)))
.and_then(|env| env.resolve_local(idx));
let Some(local) = maybe_local else {
println!(" (invalid local)");
continue;
};
println!(" (`{0}`)", universe.lookup_symbol(local));
}
Bytecode::PushField(idx) | Bytecode::PopField(idx) => {
print!(" {idx}");
let Some((name, _)) = class.locals.get_index(usize::from(idx)) else {
println!(" (invalid field)");
continue;
};
println!(" (`{0}`)", universe.lookup_symbol(*name));
}
Bytecode::PushArgument(up_idx, idx) => {
println!(" {up_idx}, {idx}");
}
Bytecode::PushBlock(idx) => {
println!(" {idx}");
let Some(Literal::Block(blk)) = current.resolve_literal(idx) else {
println!("{padding} | (invalid block)");
continue;
};
env.push(blk.as_ref());
disassemble_body(universe, class, level + 1, env);
env.pop();
}
Bytecode::PushConstant(idx) => {
print!(" {idx}");
let Some(literal) = current.resolve_literal(idx) else {
println!(" (invalid constant)");
continue;
};
match literal {
Literal::Symbol(symbol) => {
println!(" (Symbol(#{0}))", universe.lookup_symbol(*symbol));
}
_ => {
println!(" ({literal:?})");
}
}
}
Bytecode::PushGlobal(idx) => {
print!(" {idx}");
let Some(Literal::Symbol(signature)) = current.resolve_literal(idx) else {
println!(" (invalid global)");
continue;
};
println!(" (`{0}`)", universe.lookup_symbol(*signature));
}
Bytecode::Pop => {
println!();
}
Bytecode::PopArgument(up_idx, idx) => {
print!(" {up_idx}, {idx}");
// TODO: the following requires to change the parser and interpreter to preserve argument names.
// let maybe_argument = (env.iter().rev().nth(usize::from(up_idx)))
// .and_then(|env| env.resolve_argument(idx));
// let Some(argument) = maybe_argument else {
// println!(" (invalid argument)");
// continue;
// };
// println!(" (`{0}`)", universe.lookup_symbol(argument));
}
Bytecode::Send(idx) | Bytecode::SuperSend(idx) => {
print!(" {idx}");
let Some(Literal::Symbol(signature)) = current.resolve_literal(idx) else {
println!(" (invalid signature)");
continue;
};
println!(" (#{0})", universe.lookup_symbol(*signature));
}
Bytecode::ReturnLocal => {
println!();
}
Bytecode::ReturnNonLocal => {
println!();
}
}
}
}

trait FrameEnv {
fn get_body(&self) -> &[Bytecode];
fn resolve_local(&self, idx: u8) -> Option<Interned>;
fn resolve_literal(&self, idx: u8) -> Option<&Literal>;
fn resolve_argument(&self, idx: u8) -> Option<Interned>;
}

impl FrameEnv for MethodEnv {
fn get_body(&self) -> &[Bytecode] {
&self.body
}

fn resolve_local(&self, idx: u8) -> Option<Interned> {
self.locals.get(usize::from(idx)).copied()
}

fn resolve_literal(&self, idx: u8) -> Option<&Literal> {
self.literals.get(usize::from(idx))
}

fn resolve_argument(&self, _idx: u8) -> Option<Interned> {
todo!()
}
}

impl FrameEnv for Block {
fn get_body(&self) -> &[Bytecode] {
&self.blk_info.body
}

fn resolve_local(&self, idx: u8) -> Option<Interned> {
self.blk_info.locals.get(usize::from(idx)).copied()
}

fn resolve_literal(&self, idx: u8) -> Option<&Literal> {
self.blk_info.literals.get(usize::from(idx))
}

fn resolve_argument(&self, _idx: u8) -> Option<Interned> {
todo!()
}
}
2 changes: 2 additions & 0 deletions som-interpreter-bc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub mod block;
pub mod class;
/// Facilities for compiling code into bytecode.
pub mod compiler;
/// Facilities for disassembling bytecode.
pub mod disassembler;
/// Facilities for manipulating stack frames.
pub mod frame;
/// Facilities for manipulating values.
Expand Down
Loading

0 comments on commit c23db27

Please sign in to comment.