1use crate::vm::CodeBlock;
4
5mod color;
6mod edge;
7mod graph;
8mod node;
9
10use boa_macros::js_str;
11pub use color::*;
12pub use edge::*;
13pub use graph::*;
14pub use node::*;
15
16use super::{
17 Constant,
18 opcode::{Instruction, InstructionIterator},
19};
20
21impl CodeBlock {
22 #[allow(clippy::match_same_arms)]
24 pub fn to_graph(&self, graph: &mut SubGraph) {
25 let name = if self.name() == &js_str!("<main>") {
27 "__main__".to_string()
28 } else {
29 self.name().to_std_string_escaped()
30 };
31
32 graph.set_label(name);
33
34 let mut iterator = InstructionIterator::new(&self.bytecode);
35 while let Some((previous_pc, opcode, instruction)) = iterator.next() {
36 let opcode_str = opcode.as_str();
37
38 let label = format!("{opcode_str} {}", self.instruction_operands(&instruction));
39
40 let pc = iterator.pc();
41
42 match instruction {
43 Instruction::StrictEq { .. }
44 | Instruction::StrictNotEq { .. }
45 | Instruction::SetRegisterFromAccumulator { .. }
46 | Instruction::Move { .. }
47 | Instruction::PopIntoRegister { .. }
48 | Instruction::PushFromRegister { .. }
49 | Instruction::Add { .. }
50 | Instruction::Sub { .. }
51 | Instruction::Div { .. }
52 | Instruction::Mul { .. }
53 | Instruction::Mod { .. }
54 | Instruction::Pow { .. }
55 | Instruction::ShiftRight { .. }
56 | Instruction::ShiftLeft { .. }
57 | Instruction::UnsignedShiftRight { .. }
58 | Instruction::BitOr { .. }
59 | Instruction::BitAnd { .. }
60 | Instruction::BitXor { .. }
61 | Instruction::BitNot { .. }
62 | Instruction::In { .. }
63 | Instruction::Eq { .. }
64 | Instruction::NotEq { .. }
65 | Instruction::GreaterThan { .. }
66 | Instruction::GreaterThanOrEq { .. }
67 | Instruction::LessThan { .. }
68 | Instruction::LessThanOrEq { .. }
69 | Instruction::InstanceOf { .. }
70 | Instruction::SetAccumulator { .. }
71 | Instruction::SetFunctionName { .. }
72 | Instruction::Inc { .. }
73 | Instruction::Dec { .. }
74 | Instruction::CreateIteratorResult { .. }
75 | Instruction::Generator
76 | Instruction::AsyncGenerator
77 | Instruction::StoreInt8 { .. }
78 | Instruction::StoreInt16 { .. }
79 | Instruction::StoreInt32 { .. }
80 | Instruction::StoreFloat { .. }
81 | Instruction::StoreDouble { .. }
82 | Instruction::StoreLiteral { .. }
83 | Instruction::StoreRegexp { .. } => {
84 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
85 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
86 }
87 Instruction::Jump { address } => {
88 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
89 graph.add_edge(
90 previous_pc,
91 address.as_u32() as usize,
92 None,
93 Color::None,
94 EdgeStyle::Line,
95 );
96 }
97 Instruction::JumpIfFalse { address, .. }
98 | Instruction::JumpIfTrue { address, .. }
99 | Instruction::JumpIfNotUndefined { address, .. }
100 | Instruction::JumpIfNullOrUndefined { address, .. }
101 | Instruction::JumpIfNotLessThan { address, .. }
102 | Instruction::JumpIfNotLessThanOrEqual { address, .. }
103 | Instruction::JumpIfNotGreaterThan { address, .. }
104 | Instruction::JumpIfNotGreaterThanOrEqual { address, .. }
105 | Instruction::JumpIfNotEqual { address, .. } => {
106 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
107 graph.add_edge(
108 previous_pc,
109 address.as_u32() as usize,
110 Some("YES".into()),
111 Color::Green,
112 EdgeStyle::Line,
113 );
114 graph.add_edge(
115 previous_pc,
116 pc,
117 Some("NO".into()),
118 Color::Red,
119 EdgeStyle::Line,
120 );
121 }
122 Instruction::TemplateLookup { .. } | Instruction::TemplateCreate { .. } => {
123 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red);
124 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
125 }
126 Instruction::LogicalAnd { address, .. }
127 | Instruction::LogicalOr { address, .. }
128 | Instruction::Coalesce { address, .. } => {
129 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
130 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
131 graph.add_edge(
132 previous_pc,
133 address.as_u32() as usize,
134 Some("SHORT CIRCUIT".into()),
135 Color::Red,
136 EdgeStyle::Line,
137 );
138 }
139 Instruction::Case { address, .. } => {
140 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
141 graph.add_edge(
142 previous_pc,
143 pc,
144 Some("NO".into()),
145 Color::Red,
146 EdgeStyle::Line,
147 );
148 graph.add_edge(
149 previous_pc,
150 address.as_u32() as usize,
151 Some("YES".into()),
152 Color::Green,
153 EdgeStyle::Line,
154 );
155 }
156 Instruction::CallEval { .. }
157 | Instruction::Call { .. }
158 | Instruction::CallMath { .. }
159 | Instruction::PrepareMath { .. }
160 | Instruction::New { .. }
161 | Instruction::SuperCall { .. }
162 | Instruction::ConcatToString { .. }
163 | Instruction::GetArgument { .. } => {
164 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
165 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
166 }
167 Instruction::CopyDataProperties { .. } => {
168 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
169 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
170 }
171 Instruction::PushScope { .. } => {
172 let random = rand::random();
173
174 graph.add_node(
175 previous_pc,
176 NodeShape::None,
177 label.into(),
178 Color::from_random_number(random),
179 );
180 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
181 }
182 Instruction::PopEnvironment => {
183 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
184 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
185 }
186 Instruction::GetFunction { .. } => {
187 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
188 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
189 }
190 Instruction::DefVar { .. }
191 | Instruction::DefEvalVar { .. }
192 | Instruction::DefInitVar { .. }
193 | Instruction::PutLexicalValue { .. }
194 | Instruction::GetName { .. }
195 | Instruction::GetNameGlobal { .. }
196 | Instruction::GetLocator { .. }
197 | Instruction::GetNameAndLocator { .. }
198 | Instruction::GetNameOrUndefined { .. }
199 | Instruction::SetName { .. }
200 | Instruction::DeleteName { .. } => {
201 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
202 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
203 }
204 Instruction::GetPropertyByNameWithThis { .. }
205 | Instruction::GetLengthProperty { .. }
206 | Instruction::GetPropertyByName { .. }
207 | Instruction::GetPropertyByValue { .. }
208 | Instruction::GetPropertyByValuePush { .. }
209 | Instruction::GetPropertyByIndex { .. }
210 | Instruction::SetPropertyByIndex { .. }
211 | Instruction::SetPropertyByName { .. }
212 | Instruction::SetPropertyByNameWithThis { .. }
213 | Instruction::DefineOwnPropertyByName { .. }
214 | Instruction::DefineClassStaticMethodByName { .. }
215 | Instruction::DefineClassMethodByName { .. }
216 | Instruction::SetPropertyGetterByName { .. }
217 | Instruction::DefineClassStaticGetterByName { .. }
218 | Instruction::DefineClassGetterByName { .. }
219 | Instruction::SetPropertySetterByName { .. }
220 | Instruction::DefineClassStaticSetterByName { .. }
221 | Instruction::DefineClassSetterByName { .. }
222 | Instruction::SetPrivateField { .. }
223 | Instruction::DefinePrivateField { .. }
224 | Instruction::SetPrivateMethod { .. }
225 | Instruction::SetPrivateSetter { .. }
226 | Instruction::SetPrivateGetter { .. }
227 | Instruction::GetPrivateField { .. }
228 | Instruction::DeletePropertyByName { .. }
229 | Instruction::PushClassFieldPrivate { .. }
230 | Instruction::PushClassPrivateGetter { .. }
231 | Instruction::PushClassPrivateSetter { .. }
232 | Instruction::PushClassPrivateMethod { .. }
233 | Instruction::InPrivate { .. }
234 | Instruction::ThrowMutateImmutable { .. } => {
235 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
236 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
237 }
238 Instruction::ThrowNewTypeError { .. }
239 | Instruction::ThrowNewReferenceError { .. } => {
240 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
241 if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
242 graph.add_edge(
243 previous_pc,
244 handler.handler().as_u32() as usize,
245 Some(format!("Handler {i:2}: CAUGHT").into()),
246 Color::None,
247 EdgeStyle::Line,
248 );
249 }
250 }
251 Instruction::Throw { .. } | Instruction::ReThrow => {
252 if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
253 graph.add_node(previous_pc, NodeShape::Record, label.into(), Color::None);
254 graph.add_edge(
255 previous_pc,
256 handler.handler().as_u32() as usize,
257 Some(format!("Handler {i:2}: CAUGHT").into()),
258 Color::None,
259 EdgeStyle::Line,
260 );
261 } else {
262 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
263 }
264 }
265 Instruction::PushPrivateEnvironment { .. } => {
266 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
267 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
268 }
269 Instruction::JumpTable {
270 index: _,
271 addresses,
272 } => {
273 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
274
275 graph.add_edge(
276 previous_pc,
277 pc,
278 Some("DEFAULT".into()),
279 Color::None,
280 EdgeStyle::Line,
281 );
282
283 for (i, address) in addresses.iter().enumerate() {
284 graph.add_edge(
285 previous_pc,
286 address.as_u32() as usize,
287 Some(format!("{i}").into()),
288 Color::None,
289 EdgeStyle::Line,
290 );
291 }
292 }
293 Instruction::Pop
294 | Instruction::StoreZero { .. }
295 | Instruction::StoreOne { .. }
296 | Instruction::StoreNan { .. }
297 | Instruction::StorePositiveInfinity { .. }
298 | Instruction::StoreNegativeInfinity { .. }
299 | Instruction::StoreNull { .. }
300 | Instruction::StoreTrue { .. }
301 | Instruction::StoreFalse { .. }
302 | Instruction::StoreUndefined { .. }
303 | Instruction::StoreEmptyObject { .. }
304 | Instruction::StoreClassPrototype { .. }
305 | Instruction::SetClassPrototype { .. }
306 | Instruction::SetHomeObject { .. }
307 | Instruction::GetHomeObject { .. }
308 | Instruction::TypeOf { .. }
309 | Instruction::LogicalNot { .. }
310 | Instruction::Pos { .. }
311 | Instruction::Neg { .. }
312 | Instruction::SetPropertyByValue { .. }
313 | Instruction::DefineOwnPropertyByValue { .. }
314 | Instruction::DefineClassStaticMethodByValue { .. }
315 | Instruction::DefineClassMethodByValue { .. }
316 | Instruction::SetPropertyGetterByValue { .. }
317 | Instruction::DefineClassStaticGetterByValue { .. }
318 | Instruction::DefineClassGetterByValue { .. }
319 | Instruction::SetPropertySetterByValue { .. }
320 | Instruction::DefineClassStaticSetterByValue { .. }
321 | Instruction::DefineClassSetterByValue { .. }
322 | Instruction::DeletePropertyByValue { .. }
323 | Instruction::DeleteSuperThrow
324 | Instruction::GetMethod { .. }
325 | Instruction::ToPropertyKey { .. }
326 | Instruction::This { .. }
327 | Instruction::ThisForObjectEnvironmentName { .. }
328 | Instruction::GetFunctionObject { .. }
329 | Instruction::IncrementLoopIteration
330 | Instruction::CreateForInIterator { .. }
331 | Instruction::GetIterator { .. }
332 | Instruction::GetAsyncIterator { .. }
333 | Instruction::IteratorNext
334 | Instruction::IteratorPop { .. }
335 | Instruction::IteratorPush { .. }
336 | Instruction::IteratorUpdateResult { .. }
337 | Instruction::IteratorFinishAsyncNext { .. }
338 | Instruction::IteratorValue { .. }
339 | Instruction::IteratorResult { .. }
340 | Instruction::IteratorDone { .. }
341 | Instruction::IteratorToArray { .. }
342 | Instruction::IteratorReturn { .. }
343 | Instruction::IteratorStackEmpty { .. }
344 | Instruction::ValueNotNullOrUndefined { .. }
345 | Instruction::RestParameterInit { .. }
346 | Instruction::PushValueToArray { .. }
347 | Instruction::PushElisionToArray { .. }
348 | Instruction::PushIteratorToArray { .. }
349 | Instruction::StoreNewArray { .. }
350 | Instruction::GeneratorYield { .. }
351 | Instruction::AsyncGeneratorYield { .. }
352 | Instruction::AsyncGeneratorClose
353 | Instruction::CreatePromiseCapability
354 | Instruction::PushClassField { .. }
355 | Instruction::SuperCallDerived
356 | Instruction::Await { .. }
357 | Instruction::NewTarget { .. }
358 | Instruction::ImportMeta { .. }
359 | Instruction::CallEvalSpread { .. }
360 | Instruction::CallSpread
361 | Instruction::NewSpread
362 | Instruction::SuperCallSpread
363 | Instruction::SetPrototype { .. }
364 | Instruction::GetPrototype { .. }
365 | Instruction::IsObject { .. }
366 | Instruction::SetNameByLocator { .. }
367 | Instruction::PushObjectEnvironment { .. }
368 | Instruction::PopPrivateEnvironment
369 | Instruction::ImportCall { .. }
370 | Instruction::Exception { .. }
371 | Instruction::MaybeException { .. }
372 | Instruction::CheckReturn
373 | Instruction::BindThisValue { .. }
374 | Instruction::CreateMappedArgumentsObject { .. }
375 | Instruction::CreateUnmappedArgumentsObject { .. } => {
376 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
377 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
378 }
379 Instruction::Return => {
380 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::Red);
381 }
382 Instruction::Reserved3
383 | Instruction::Reserved4
384 | Instruction::Reserved5
385 | Instruction::Reserved6
386 | Instruction::Reserved7
387 | Instruction::Reserved8
388 | Instruction::Reserved9
389 | Instruction::Reserved10
390 | Instruction::Reserved11
391 | Instruction::Reserved12
392 | Instruction::Reserved13
393 | Instruction::Reserved14
394 | Instruction::Reserved15
395 | Instruction::Reserved16
396 | Instruction::Reserved17
397 | Instruction::Reserved18
398 | Instruction::Reserved19
399 | Instruction::Reserved20
400 | Instruction::Reserved21
401 | Instruction::Reserved22
402 | Instruction::Reserved23
403 | Instruction::Reserved24
404 | Instruction::Reserved25
405 | Instruction::Reserved26
406 | Instruction::Reserved27
407 | Instruction::Reserved28
408 | Instruction::Reserved29
409 | Instruction::Reserved30
410 | Instruction::Reserved31
411 | Instruction::Reserved32
412 | Instruction::Reserved33
413 | Instruction::Reserved34
414 | Instruction::Reserved35
415 | Instruction::Reserved36
416 | Instruction::Reserved37
417 | Instruction::Reserved38
418 | Instruction::Reserved39
419 | Instruction::Reserved40
420 | Instruction::Reserved41
421 | Instruction::Reserved42
422 | Instruction::Reserved43
423 | Instruction::Reserved44
424 | Instruction::Reserved45
425 | Instruction::Reserved46
426 | Instruction::Reserved47
427 | Instruction::Reserved48
428 | Instruction::Reserved49
429 | Instruction::Reserved50
430 | Instruction::Reserved51
431 | Instruction::Reserved52
432 | Instruction::Reserved53
433 | Instruction::Reserved54
434 | Instruction::Reserved55
435 | Instruction::Reserved56
436 | Instruction::Reserved57 => unreachable!("Reserved opcodes are unreachable"),
437 }
438 }
439
440 for constant in &self.constants {
441 if let Constant::Function(function) = constant {
442 let subgraph = graph.subgraph(String::new());
443 function.to_graph(subgraph);
444 }
445 }
446 }
447}