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::New { .. }
159 | Instruction::SuperCall { .. }
160 | Instruction::ConcatToString { .. }
161 | Instruction::GetArgument { .. } => {
162 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
163 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
164 }
165 Instruction::CopyDataProperties { .. } => {
166 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
167 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
168 }
169 Instruction::PushScope { .. } => {
170 let random = rand::random();
171
172 graph.add_node(
173 previous_pc,
174 NodeShape::None,
175 label.into(),
176 Color::from_random_number(random),
177 );
178 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
179 }
180 Instruction::PopEnvironment => {
181 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
182 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
183 }
184 Instruction::GetFunction { .. } => {
185 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
186 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
187 }
188 Instruction::DefVar { .. }
189 | Instruction::DefInitVar { .. }
190 | Instruction::PutLexicalValue { .. }
191 | Instruction::GetName { .. }
192 | Instruction::GetNameGlobal { .. }
193 | Instruction::GetLocator { .. }
194 | Instruction::GetNameAndLocator { .. }
195 | Instruction::GetNameOrUndefined { .. }
196 | Instruction::SetName { .. }
197 | Instruction::DeleteName { .. } => {
198 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
199 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
200 }
201 Instruction::GetPropertyByNameWithThis { .. }
202 | Instruction::GetLengthProperty { .. }
203 | Instruction::GetPropertyByName { .. }
204 | Instruction::GetPropertyByValue { .. }
205 | Instruction::GetPropertyByValuePush { .. }
206 | Instruction::SetPropertyByName { .. }
207 | Instruction::SetPropertyByNameWithThis { .. }
208 | Instruction::DefineOwnPropertyByName { .. }
209 | Instruction::DefineClassStaticMethodByName { .. }
210 | Instruction::DefineClassMethodByName { .. }
211 | Instruction::SetPropertyGetterByName { .. }
212 | Instruction::DefineClassStaticGetterByName { .. }
213 | Instruction::DefineClassGetterByName { .. }
214 | Instruction::SetPropertySetterByName { .. }
215 | Instruction::DefineClassStaticSetterByName { .. }
216 | Instruction::DefineClassSetterByName { .. }
217 | Instruction::SetPrivateField { .. }
218 | Instruction::DefinePrivateField { .. }
219 | Instruction::SetPrivateMethod { .. }
220 | Instruction::SetPrivateSetter { .. }
221 | Instruction::SetPrivateGetter { .. }
222 | Instruction::GetPrivateField { .. }
223 | Instruction::DeletePropertyByName { .. }
224 | Instruction::PushClassFieldPrivate { .. }
225 | Instruction::PushClassPrivateGetter { .. }
226 | Instruction::PushClassPrivateSetter { .. }
227 | Instruction::PushClassPrivateMethod { .. }
228 | Instruction::InPrivate { .. }
229 | Instruction::ThrowMutateImmutable { .. } => {
230 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
231 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
232 }
233 Instruction::ThrowNewTypeError { .. }
234 | Instruction::ThrowNewReferenceError { .. } => {
235 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
236 if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
237 graph.add_edge(
238 previous_pc,
239 handler.handler().as_u32() as usize,
240 Some(format!("Handler {i:2}: CAUGHT").into()),
241 Color::None,
242 EdgeStyle::Line,
243 );
244 }
245 }
246 Instruction::Throw { .. } | Instruction::ReThrow => {
247 if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
248 graph.add_node(previous_pc, NodeShape::Record, label.into(), Color::None);
249 graph.add_edge(
250 previous_pc,
251 handler.handler().as_u32() as usize,
252 Some(format!("Handler {i:2}: CAUGHT").into()),
253 Color::None,
254 EdgeStyle::Line,
255 );
256 } else {
257 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
258 }
259 }
260 Instruction::PushPrivateEnvironment { .. } => {
261 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
262 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
263 }
264 Instruction::JumpTable {
265 index: _,
266 addresses,
267 } => {
268 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
269
270 graph.add_edge(
271 previous_pc,
272 pc,
273 Some("DEFAULT".into()),
274 Color::None,
275 EdgeStyle::Line,
276 );
277
278 for (i, address) in addresses.iter().enumerate() {
279 graph.add_edge(
280 previous_pc,
281 address.as_u32() as usize,
282 Some(format!("{i}").into()),
283 Color::None,
284 EdgeStyle::Line,
285 );
286 }
287 }
288 Instruction::Pop
289 | Instruction::StoreZero { .. }
290 | Instruction::StoreOne { .. }
291 | Instruction::StoreNan { .. }
292 | Instruction::StorePositiveInfinity { .. }
293 | Instruction::StoreNegativeInfinity { .. }
294 | Instruction::StoreNull { .. }
295 | Instruction::StoreTrue { .. }
296 | Instruction::StoreFalse { .. }
297 | Instruction::StoreUndefined { .. }
298 | Instruction::StoreEmptyObject { .. }
299 | Instruction::StoreClassPrototype { .. }
300 | Instruction::SetClassPrototype { .. }
301 | Instruction::SetHomeObject { .. }
302 | Instruction::GetHomeObject { .. }
303 | Instruction::TypeOf { .. }
304 | Instruction::LogicalNot { .. }
305 | Instruction::Pos { .. }
306 | Instruction::Neg { .. }
307 | Instruction::SetPropertyByValue { .. }
308 | Instruction::DefineOwnPropertyByValue { .. }
309 | Instruction::DefineClassStaticMethodByValue { .. }
310 | Instruction::DefineClassMethodByValue { .. }
311 | Instruction::SetPropertyGetterByValue { .. }
312 | Instruction::DefineClassStaticGetterByValue { .. }
313 | Instruction::DefineClassGetterByValue { .. }
314 | Instruction::SetPropertySetterByValue { .. }
315 | Instruction::DefineClassStaticSetterByValue { .. }
316 | Instruction::DefineClassSetterByValue { .. }
317 | Instruction::DeletePropertyByValue { .. }
318 | Instruction::DeleteSuperThrow
319 | Instruction::GetMethod { .. }
320 | Instruction::ToPropertyKey { .. }
321 | Instruction::This { .. }
322 | Instruction::ThisForObjectEnvironmentName { .. }
323 | Instruction::GetFunctionObject { .. }
324 | Instruction::IncrementLoopIteration
325 | Instruction::CreateForInIterator { .. }
326 | Instruction::GetIterator { .. }
327 | Instruction::GetAsyncIterator { .. }
328 | Instruction::IteratorNext
329 | Instruction::IteratorPop { .. }
330 | Instruction::IteratorPush { .. }
331 | Instruction::IteratorUpdateResult { .. }
332 | Instruction::IteratorFinishAsyncNext { .. }
333 | Instruction::IteratorValue { .. }
334 | Instruction::IteratorResult { .. }
335 | Instruction::IteratorDone { .. }
336 | Instruction::IteratorToArray { .. }
337 | Instruction::IteratorReturn { .. }
338 | Instruction::IteratorStackEmpty { .. }
339 | Instruction::ValueNotNullOrUndefined { .. }
340 | Instruction::RestParameterInit { .. }
341 | Instruction::PushValueToArray { .. }
342 | Instruction::PushElisionToArray { .. }
343 | Instruction::PushIteratorToArray { .. }
344 | Instruction::StoreNewArray { .. }
345 | Instruction::GeneratorYield { .. }
346 | Instruction::AsyncGeneratorYield { .. }
347 | Instruction::AsyncGeneratorClose
348 | Instruction::CreatePromiseCapability
349 | Instruction::PushClassField { .. }
350 | Instruction::SuperCallDerived
351 | Instruction::Await { .. }
352 | Instruction::NewTarget { .. }
353 | Instruction::ImportMeta { .. }
354 | Instruction::CallEvalSpread { .. }
355 | Instruction::CallSpread
356 | Instruction::NewSpread
357 | Instruction::SuperCallSpread
358 | Instruction::SetPrototype { .. }
359 | Instruction::GetPrototype { .. }
360 | Instruction::IsObject { .. }
361 | Instruction::SetNameByLocator { .. }
362 | Instruction::PushObjectEnvironment { .. }
363 | Instruction::PopPrivateEnvironment
364 | Instruction::ImportCall { .. }
365 | Instruction::Exception { .. }
366 | Instruction::MaybeException { .. }
367 | Instruction::CheckReturn
368 | Instruction::BindThisValue { .. }
369 | Instruction::CreateMappedArgumentsObject { .. }
370 | Instruction::CreateUnmappedArgumentsObject { .. } => {
371 graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
372 graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
373 }
374 Instruction::Return => {
375 graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::Red);
376 }
377 Instruction::Reserved1
378 | Instruction::Reserved2
379 | Instruction::Reserved3
380 | Instruction::Reserved4
381 | Instruction::Reserved5
382 | Instruction::Reserved6
383 | Instruction::Reserved7
384 | Instruction::Reserved8
385 | Instruction::Reserved9
386 | Instruction::Reserved10
387 | Instruction::Reserved11
388 | Instruction::Reserved12
389 | Instruction::Reserved13
390 | Instruction::Reserved14
391 | Instruction::Reserved15
392 | Instruction::Reserved16
393 | Instruction::Reserved17
394 | Instruction::Reserved18
395 | Instruction::Reserved19
396 | Instruction::Reserved20
397 | Instruction::Reserved21
398 | Instruction::Reserved22
399 | Instruction::Reserved23
400 | Instruction::Reserved24
401 | Instruction::Reserved25
402 | Instruction::Reserved26
403 | Instruction::Reserved27
404 | Instruction::Reserved28
405 | Instruction::Reserved29
406 | Instruction::Reserved30
407 | Instruction::Reserved31
408 | Instruction::Reserved32
409 | Instruction::Reserved33
410 | Instruction::Reserved34
411 | Instruction::Reserved35
412 | Instruction::Reserved36
413 | Instruction::Reserved37
414 | Instruction::Reserved38
415 | Instruction::Reserved39
416 | Instruction::Reserved40
417 | Instruction::Reserved41
418 | Instruction::Reserved42
419 | Instruction::Reserved43
420 | Instruction::Reserved44
421 | Instruction::Reserved45
422 | Instruction::Reserved46
423 | Instruction::Reserved47
424 | Instruction::Reserved48
425 | Instruction::Reserved49
426 | Instruction::Reserved50
427 | Instruction::Reserved51
428 | Instruction::Reserved52
429 | Instruction::Reserved53
430 | Instruction::Reserved54
431 | Instruction::Reserved55
432 | Instruction::Reserved56
433 | Instruction::Reserved57
434 | Instruction::Reserved58
435 | Instruction::Reserved59
436 | Instruction::Reserved60 => 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}