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 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
75 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
76 }
77 Instruction::CreateIteratorResult { .. } => {
78 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
79 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
80 }
81 Instruction::Generator { .. } => {
82 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
83 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
84 }
85 Instruction::PushInt8 { .. } => {
86 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
87 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
88 }
89 Instruction::PushInt16 { .. } => {
90 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
91 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
92 }
93 Instruction::PushInt32 { .. } => {
94 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
95 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
96 }
97 Instruction::PushFloat { .. } => {
98 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
99 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
100 }
101 Instruction::PushDouble { .. } => {
102 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
103 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
104 }
105 Instruction::PushLiteral { .. }
106 | Instruction::PushRegexp { .. }
107 | Instruction::HasRestrictedGlobalProperty { .. }
108 | Instruction::CanDeclareGlobalFunction { .. }
109 | Instruction::CanDeclareGlobalVar { .. } => {
110 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
111 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
112 }
113 Instruction::Jump { address } => {
114 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
115 graph.add_edge(
116 previous_pc,
117 address as usize,
118 None,
119 Color::None,
120 EdgeStyle::Line,
121 );
122 }
123 Instruction::JumpIfFalse { address, .. }
124 | Instruction::JumpIfTrue { address, .. }
125 | Instruction::JumpIfNotUndefined { address, .. }
126 | Instruction::JumpIfNullOrUndefined { address, .. } => {
127 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
128 graph.add_edge(
129 previous_pc,
130 address as usize,
131 Some("YES".into()),
132 Color::Green,
133 EdgeStyle::Line,
134 );
135 graph.add_edge(
136 previous_pc,
137 pc,
138 Some("NO".into()),
139 Color::Red,
140 EdgeStyle::Line,
141 );
142 }
143 Instruction::TemplateLookup { .. } | Instruction::TemplateCreate { .. } => {
144 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red);
145 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
146 }
147 Instruction::LogicalAnd { address, .. }
148 | Instruction::LogicalOr { address, .. }
149 | Instruction::Coalesce { address, .. } => {
150 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
151 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
152 graph.add_edge(
153 previous_pc,
154 address as usize,
155 Some("SHORT CIRCUIT".into()),
156 Color::Red,
157 EdgeStyle::Line,
158 );
159 }
160 Instruction::Case { address, .. } => {
161 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
162 graph.add_edge(
163 previous_pc,
164 pc,
165 Some("NO".into()),
166 Color::Red,
167 EdgeStyle::Line,
168 );
169 graph.add_edge(
170 previous_pc,
171 address as usize,
172 Some("YES".into()),
173 Color::Green,
174 EdgeStyle::Line,
175 );
176 }
177 Instruction::GeneratorDelegateNext {
178 return_method_undefined,
179 throw_method_undefined,
180 ..
181 } => {
182 graph.add_node(
183 previous_pc,
184 NodeShape::Diamond,
185 opcode_str.into(),
186 Color::None,
187 );
188 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
189 graph.add_edge(
190 previous_pc,
191 throw_method_undefined as usize,
192 Some("`throw` undefined".into()),
193 Color::Red,
194 EdgeStyle::Line,
195 );
196 graph.add_edge(
197 previous_pc,
198 return_method_undefined as usize,
199 Some("`return` undefined".into()),
200 Color::Blue,
201 EdgeStyle::Line,
202 );
203 }
204 Instruction::GeneratorDelegateResume { r#return, exit, .. } => {
205 graph.add_node(
206 previous_pc,
207 NodeShape::Diamond,
208 opcode_str.into(),
209 Color::None,
210 );
211 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
212 graph.add_edge(
213 previous_pc,
214 r#return as usize,
215 Some("return".into()),
216 Color::Yellow,
217 EdgeStyle::Line,
218 );
219 graph.add_edge(
220 previous_pc,
221 exit as usize,
222 Some("done".into()),
223 Color::Blue,
224 EdgeStyle::Line,
225 );
226 }
227 Instruction::CallEval { .. }
228 | Instruction::Call { .. }
229 | Instruction::New { .. }
230 | Instruction::SuperCall { .. }
231 | Instruction::ConcatToString { .. }
232 | Instruction::GetArgument { .. } => {
233 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
234 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
235 }
236 Instruction::JumpIfNotResumeKind { address, .. } => {
237 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
238 graph.add_edge(
239 previous_pc,
240 address as usize,
241 Some("EXIT".into()),
242 Color::Red,
243 EdgeStyle::Line,
244 );
245 graph.add_edge(previous_pc, pc, None, Color::Green, EdgeStyle::Line);
246 }
247 Instruction::CopyDataProperties { .. } => {
248 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
249 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
250 }
251 Instruction::PushScope { .. } => {
252 let random = rand::random();
253
254 graph.add_node(
255 previous_pc,
256 NodeShape::None,
257 label.into(),
258 Color::from_random_number(random),
259 );
260 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
261 }
262 Instruction::PopEnvironment => {
263 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
264 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
265 }
266 Instruction::GetFunction { .. } => {
267 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
268 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
269 }
270 Instruction::DefVar { .. }
271 | Instruction::DefInitVar { .. }
272 | Instruction::PutLexicalValue { .. }
273 | Instruction::GetName { .. }
274 | Instruction::GetNameGlobal { .. }
275 | Instruction::GetLocator { .. }
276 | Instruction::GetNameAndLocator { .. }
277 | Instruction::GetNameOrUndefined { .. }
278 | Instruction::SetName { .. }
279 | Instruction::DeleteName { .. } => {
280 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
281 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
282 }
283 Instruction::GetPropertyByName { .. }
284 | Instruction::GetPropertyByValue { .. }
285 | Instruction::GetPropertyByValuePush { .. }
286 | Instruction::SetPropertyByName { .. }
287 | Instruction::DefineOwnPropertyByName { .. }
288 | Instruction::DefineClassStaticMethodByName { .. }
289 | Instruction::DefineClassMethodByName { .. }
290 | Instruction::SetPropertyGetterByName { .. }
291 | Instruction::DefineClassStaticGetterByName { .. }
292 | Instruction::DefineClassGetterByName { .. }
293 | Instruction::SetPropertySetterByName { .. }
294 | Instruction::DefineClassStaticSetterByName { .. }
295 | Instruction::DefineClassSetterByName { .. }
296 | Instruction::SetPrivateField { .. }
297 | Instruction::DefinePrivateField { .. }
298 | Instruction::SetPrivateMethod { .. }
299 | Instruction::SetPrivateSetter { .. }
300 | Instruction::SetPrivateGetter { .. }
301 | Instruction::GetPrivateField { .. }
302 | Instruction::DeletePropertyByName { .. }
303 | Instruction::PushClassFieldPrivate { .. }
304 | Instruction::PushClassPrivateGetter { .. }
305 | Instruction::PushClassPrivateSetter { .. }
306 | Instruction::PushClassPrivateMethod { .. }
307 | Instruction::InPrivate { .. }
308 | Instruction::ThrowMutateImmutable { .. } => {
309 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
310 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
311 }
312 Instruction::ThrowNewTypeError { .. }
313 | Instruction::ThrowNewSyntaxError { .. }
314 | Instruction::ThrowNewReferenceError { .. } => {
315 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
316 if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
317 graph.add_edge(
318 previous_pc,
319 handler.handler() as usize,
320 Some(format!("Handler {i:2}: CAUGHT").into()),
321 Color::None,
322 EdgeStyle::Line,
323 );
324 }
325 }
326 Instruction::Throw { .. } | Instruction::ReThrow => {
327 if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
328 graph.add_node(previous_pc, NodeShape::Record, label.into(), Color::None);
329 graph.add_edge(
330 previous_pc,
331 handler.handler() as usize,
332 Some(format!("Handler {i:2}: CAUGHT").into()),
333 Color::None,
334 EdgeStyle::Line,
335 );
336 } else {
337 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
338 }
339 }
340 Instruction::PushPrivateEnvironment { .. } => {
341 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
342 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
343 }
344 Instruction::JumpTable {
345 index: _,
346 default,
347 addresses,
348 } => {
349 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
350 graph.add_edge(
351 previous_pc,
352 default as usize,
353 Some("DEFAULT".into()),
354 Color::None,
355 EdgeStyle::Line,
356 );
357
358 for (i, address) in addresses.iter().enumerate() {
359 graph.add_edge(
360 previous_pc,
361 *address as usize,
362 Some(format!("Index: {i}").into()),
363 Color::None,
364 EdgeStyle::Line,
365 );
366 }
367 }
368 Instruction::Pop
369 | Instruction::PushZero { .. }
370 | Instruction::PushOne { .. }
371 | Instruction::PushNan { .. }
372 | Instruction::PushPositiveInfinity { .. }
373 | Instruction::PushNegativeInfinity { .. }
374 | Instruction::PushNull { .. }
375 | Instruction::PushTrue { .. }
376 | Instruction::PushFalse { .. }
377 | Instruction::PushUndefined { .. }
378 | Instruction::PushEmptyObject { .. }
379 | Instruction::PushClassPrototype { .. }
380 | Instruction::SetClassPrototype { .. }
381 | Instruction::SetHomeObject { .. }
382 | Instruction::TypeOf { .. }
383 | Instruction::LogicalNot { .. }
384 | Instruction::Pos { .. }
385 | Instruction::Neg { .. }
386 | Instruction::SetPropertyByValue { .. }
387 | Instruction::DefineOwnPropertyByValue { .. }
388 | Instruction::DefineClassStaticMethodByValue { .. }
389 | Instruction::DefineClassMethodByValue { .. }
390 | Instruction::SetPropertyGetterByValue { .. }
391 | Instruction::DefineClassStaticGetterByValue { .. }
392 | Instruction::DefineClassGetterByValue { .. }
393 | Instruction::SetPropertySetterByValue { .. }
394 | Instruction::DefineClassStaticSetterByValue { .. }
395 | Instruction::DefineClassSetterByValue { .. }
396 | Instruction::DeletePropertyByValue { .. }
397 | Instruction::DeleteSuperThrow
398 | Instruction::ToPropertyKey { .. }
399 | Instruction::This { .. }
400 | Instruction::ThisForObjectEnvironmentName { .. }
401 | Instruction::Super { .. }
402 | Instruction::IncrementLoopIteration
403 | Instruction::CreateForInIterator { .. }
404 | Instruction::GetIterator { .. }
405 | Instruction::GetAsyncIterator { .. }
406 | Instruction::IteratorNext
407 | Instruction::IteratorFinishAsyncNext { .. }
408 | Instruction::IteratorValue { .. }
409 | Instruction::IteratorResult { .. }
410 | Instruction::IteratorDone { .. }
411 | Instruction::IteratorToArray { .. }
412 | Instruction::IteratorReturn { .. }
413 | Instruction::IteratorStackEmpty { .. }
414 | Instruction::ValueNotNullOrUndefined { .. }
415 | Instruction::RestParameterInit { .. }
416 | Instruction::PushValueToArray { .. }
417 | Instruction::PushElisionToArray { .. }
418 | Instruction::PushIteratorToArray { .. }
419 | Instruction::PushNewArray { .. }
420 | Instruction::GeneratorYield { .. }
421 | Instruction::AsyncGeneratorYield { .. }
422 | Instruction::AsyncGeneratorClose
423 | Instruction::CreatePromiseCapability
424 | Instruction::CompletePromiseCapability
425 | Instruction::GeneratorNext { .. }
426 | Instruction::PushClassField { .. }
427 | Instruction::SuperCallDerived
428 | Instruction::Await { .. }
429 | Instruction::NewTarget { .. }
430 | Instruction::ImportMeta { .. }
431 | Instruction::CallEvalSpread { .. }
432 | Instruction::CallSpread
433 | Instruction::NewSpread
434 | Instruction::SuperCallSpread
435 | Instruction::SuperCallPrepare { .. }
436 | Instruction::SetPrototype { .. }
437 | Instruction::IsObject { .. }
438 | Instruction::SetNameByLocator { .. }
439 | Instruction::PushObjectEnvironment { .. }
440 | Instruction::PopPrivateEnvironment
441 | Instruction::ImportCall { .. }
442 | Instruction::Exception { .. }
443 | Instruction::MaybeException { .. }
444 | Instruction::CheckReturn
445 | Instruction::BindThisValue { .. }
446 | Instruction::CreateMappedArgumentsObject { .. }
447 | Instruction::CreateUnmappedArgumentsObject { .. }
448 | Instruction::CreateGlobalFunctionBinding { .. }
449 | Instruction::CreateGlobalVarBinding { .. } => {
450 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
451 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
452 }
453 Instruction::Return => {
454 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::Red);
455 }
456 Instruction::Reserved1
457 | Instruction::Reserved2
458 | Instruction::Reserved3
459 | Instruction::Reserved4
460 | Instruction::Reserved5
461 | Instruction::Reserved6
462 | Instruction::Reserved7
463 | Instruction::Reserved8
464 | Instruction::Reserved9
465 | Instruction::Reserved10
466 | Instruction::Reserved11
467 | Instruction::Reserved12
468 | Instruction::Reserved13
469 | Instruction::Reserved14
470 | Instruction::Reserved15
471 | Instruction::Reserved16
472 | Instruction::Reserved17
473 | Instruction::Reserved18
474 | Instruction::Reserved19
475 | Instruction::Reserved20
476 | Instruction::Reserved21
477 | Instruction::Reserved22
478 | Instruction::Reserved23
479 | Instruction::Reserved24
480 | Instruction::Reserved25
481 | Instruction::Reserved26
482 | Instruction::Reserved27
483 | Instruction::Reserved28
484 | Instruction::Reserved29
485 | Instruction::Reserved30
486 | Instruction::Reserved31
487 | Instruction::Reserved32
488 | Instruction::Reserved33
489 | Instruction::Reserved34
490 | Instruction::Reserved35
491 | Instruction::Reserved36
492 | Instruction::Reserved37
493 | Instruction::Reserved38
494 | Instruction::Reserved39
495 | Instruction::Reserved40
496 | Instruction::Reserved41
497 | Instruction::Reserved42
498 | Instruction::Reserved43
499 | Instruction::Reserved44
500 | Instruction::Reserved45
501 | Instruction::Reserved46
502 | Instruction::Reserved47
503 | Instruction::Reserved48
504 | Instruction::Reserved49
505 | Instruction::Reserved50
506 | Instruction::Reserved51
507 | Instruction::Reserved52
508 | Instruction::Reserved53
509 | Instruction::Reserved54
510 | Instruction::Reserved55
511 | Instruction::Reserved56
512 | Instruction::Reserved57
513 | Instruction::Reserved58
514 | Instruction::Reserved59
515 | Instruction::Reserved60
516 | Instruction::Reserved61
517 | Instruction::Reserved62
518 | Instruction::Reserved63 => unreachable!("Reserved opcodes are unreachable"),
519 }
520 }
521
522 for constant in &self.constants {
523 if let Constant::Function(function) = constant {
524 let subgraph = graph.subgraph(String::new());
525 function.to_graph(subgraph);
526 }
527 }
528 }
529}