-
-
Notifications
You must be signed in to change notification settings - Fork 413
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement instruction flowgraph generator (#2422)
This PR is a WIP implementation of a vm instruction flowgraph generator This aims to make the vm easier to debug and understand for both newcomers and experienced devs. For example if we have the following code: ```js let i = 0; while (i < 10) { if (i == 3) { break; } i++; } ``` It generates the following instructions (which is hard to read, especially jumps): <details> ``` ----------------------Compiled Output: '<main>'----------------------- Location Count Opcode Operands 000000 0000 PushZero 000001 0001 DefInitLet 0000: 'i' 000006 0002 LoopStart 000007 0003 LoopContinue 000008 0004 GetName 0000: 'i' 000013 0005 PushInt8 10 000015 0006 LessThan 000016 0007 JumpIfFalse 78 000021 0008 PushDeclarativeEnvironment 0, 1 000030 0009 GetName 0000: 'i' 000035 0010 PushInt8 3 000037 0011 Eq 000038 0012 JumpIfFalse 58 000043 0013 PushDeclarativeEnvironment 0, 0 000052 0014 Jump 78 000057 0015 PopEnvironment 000058 0016 GetName 0000: 'i' 000063 0017 IncPost 000064 0018 RotateRight 2 000066 0019 SetName 0000: 'i' 000071 0020 Pop 000072 0021 PopEnvironment 000073 0022 Jump 7 000078 0023 LoopEnd Literals: <empty> Bindings: 0000: i Functions: <empty> ``` </details> And the flow graph is generated: ![flowgraph](https://user-images.githubusercontent.com/8566042/200589387-40b36ad7-d2f2-4918-a3e4-5a8fa5eee89b.png) The beginning of the function is marked by the `start` node (in green) and end (in red). In branching the "yes" branch is marked in green and "no" in red. ~~This only generates in [graphviz format](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) (a widely used format) but it would be nice to also generate to a format that `mermaid.js` can understand and that could be put in articles boa-dev/boa-dev.github.io#26 TODO: - [x] Generate graphviz format - [x] Generate mermaid format - [x] Programmatically generate colors push and pop env instructions - [x] Display nested functions in sub-sub-graphs. - [x] Put under a feature (`"flowgraph"`) - [x] Handle try/catch, switch instructions - [x] CLI option for configuring direction of flow (by default it is top down) - [x] Handle `Throw` instruction (requires keeping track of try blocks) - [x] Documentation - [x] Prevent node name collisions (functions with the same name)
- Loading branch information
Showing
12 changed files
with
1,452 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
use std::fmt::Display; | ||
|
||
/// Represents the color of a node or edge. | ||
#[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
pub enum Color { | ||
/// Represents the default color. | ||
None, | ||
/// Represents the color red. | ||
Red, | ||
/// Represents the color green. | ||
Green, | ||
/// Represents the color blue. | ||
Blue, | ||
/// Represents the color yellow. | ||
Yellow, | ||
/// Represents the color purple. | ||
Purple, | ||
/// Represents a RGB color. | ||
Rgb { r: u8, g: u8, b: u8 }, | ||
} | ||
|
||
impl Color { | ||
/// Function for converting HSV to RGB color format. | ||
#[allow(clippy::many_single_char_names)] | ||
#[inline] | ||
pub fn hsv_to_rgb(h: f64, s: f64, v: f64) -> Self { | ||
let h_i = (h * 6.0) as i64; | ||
let f = h * 6.0 - h_i as f64; | ||
let p = v * (1.0 - s); | ||
let q = v * (1.0 - f * s); | ||
let t = v * (1.0 - (1.0 - f) * s); | ||
|
||
let (r, g, b) = match h_i { | ||
0 => (v, t, p), | ||
1 => (q, v, p), | ||
2 => (p, v, t), | ||
3 => (p, q, v), | ||
4 => (t, p, v), | ||
5 => (v, p, q), | ||
_ => unreachable!(), | ||
}; | ||
|
||
let r = (r * 256.0) as u8; | ||
let g = (g * 256.0) as u8; | ||
let b = (b * 256.0) as u8; | ||
|
||
Self::Rgb { r, g, b } | ||
} | ||
|
||
/// This funcition takes a random value and converts it to | ||
/// a pleasant to look at RGB color. | ||
#[inline] | ||
pub fn from_random_number(mut random: f64) -> Self { | ||
const GOLDEN_RATIO_CONJUGATE: f64 = 0.618033988749895; | ||
random += GOLDEN_RATIO_CONJUGATE; | ||
random %= 1.0; | ||
|
||
Self::hsv_to_rgb(random, 0.7, 0.95) | ||
} | ||
|
||
/// Check if the color is [`Self::None`]. | ||
#[inline] | ||
pub fn is_none(&self) -> bool { | ||
*self == Self::None | ||
} | ||
} | ||
|
||
impl Display for Color { | ||
#[inline] | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Color::None => f.write_str(""), | ||
Color::Red => f.write_str("red"), | ||
Color::Green => f.write_str("green"), | ||
Color::Blue => f.write_str("blue"), | ||
Color::Yellow => f.write_str("yellow"), | ||
Color::Purple => f.write_str("purple"), | ||
Color::Rgb { r, g, b } => write!(f, "#{r:02X}{b:02X}{g:02X}"), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
use crate::vm::flowgraph::Color; | ||
|
||
/// Represents the edge (connection) style. | ||
#[derive(Debug, Clone, Copy)] | ||
pub enum EdgeStyle { | ||
/// Represents a solid line. | ||
Line, | ||
/// Represents a dotted line. | ||
Dotted, | ||
/// Represents a dashed line. | ||
Dashed, | ||
} | ||
|
||
/// Represents the edge type. | ||
#[derive(Debug, Clone, Copy)] | ||
pub enum EdgeType { | ||
/// Represents no decoration on the edge line. | ||
None, | ||
/// Represents arrow edge type. | ||
Arrow, | ||
} | ||
|
||
/// Represents an edge/connection in the flowgraph. | ||
#[derive(Debug, Clone)] | ||
pub struct Edge { | ||
/// The location of the source node. | ||
pub(super) from: usize, | ||
/// The location of the destination node. | ||
pub(super) to: usize, | ||
/// The label on top of the edge. | ||
pub(super) label: Option<Box<str>>, | ||
/// The color of the line. | ||
pub(super) color: Color, | ||
/// The style of the line. | ||
pub(super) style: EdgeStyle, | ||
/// The type of the line. | ||
pub(super) type_: EdgeType, | ||
} | ||
|
||
impl Edge { | ||
/// Construct a new edge. | ||
#[inline] | ||
pub(super) fn new( | ||
from: usize, | ||
to: usize, | ||
label: Option<Box<str>>, | ||
color: Color, | ||
style: EdgeStyle, | ||
) -> Self { | ||
Self { | ||
from, | ||
to, | ||
label, | ||
color, | ||
style, | ||
type_: EdgeType::Arrow, | ||
} | ||
} | ||
|
||
/// Set the type of the edge. | ||
#[inline] | ||
pub fn set_type(&mut self, type_: EdgeType) { | ||
self.type_ = type_; | ||
} | ||
} |
Oops, something went wrong.