boa_engine 0.21.1

Boa is a Javascript lexer, parser and compiler written in Rust. Currently, it has support for some of the language.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
//! This module is responsible for generating the vm instruction flowgraph.

use crate::vm::CodeBlock;

mod color;
mod edge;
mod graph;
mod node;

use boa_macros::js_str;
pub use color::*;
pub use edge::*;
pub use graph::*;
pub use node::*;

use super::{
    Constant,
    opcode::{Instruction, InstructionIterator},
};

impl CodeBlock {
    /// Output the [`CodeBlock`] VM instructions into a [`Graph`].
    #[allow(clippy::match_same_arms)]
    pub fn to_graph(&self, graph: &mut SubGraph) {
        // Have to remove any invalid graph chars like `<` or `>`.
        let name = if self.name() == &js_str!("<main>") {
            "__main__".to_string()
        } else {
            self.name().to_std_string_escaped()
        };

        graph.set_label(name);

        let mut iterator = InstructionIterator::new(&self.bytecode);
        while let Some((previous_pc, opcode, instruction)) = iterator.next() {
            let opcode_str = opcode.as_str();

            let label = format!("{opcode_str} {}", self.instruction_operands(&instruction));

            let pc = iterator.pc();

            match instruction {
                Instruction::StrictEq { .. }
                | Instruction::StrictNotEq { .. }
                | Instruction::SetRegisterFromAccumulator { .. }
                | Instruction::Move { .. }
                | Instruction::PopIntoRegister { .. }
                | Instruction::PushFromRegister { .. }
                | Instruction::Add { .. }
                | Instruction::Sub { .. }
                | Instruction::Div { .. }
                | Instruction::Mul { .. }
                | Instruction::Mod { .. }
                | Instruction::Pow { .. }
                | Instruction::ShiftRight { .. }
                | Instruction::ShiftLeft { .. }
                | Instruction::UnsignedShiftRight { .. }
                | Instruction::BitOr { .. }
                | Instruction::BitAnd { .. }
                | Instruction::BitXor { .. }
                | Instruction::BitNot { .. }
                | Instruction::In { .. }
                | Instruction::Eq { .. }
                | Instruction::NotEq { .. }
                | Instruction::GreaterThan { .. }
                | Instruction::GreaterThanOrEq { .. }
                | Instruction::LessThan { .. }
                | Instruction::LessThanOrEq { .. }
                | Instruction::InstanceOf { .. }
                | Instruction::SetAccumulator { .. }
                | Instruction::SetFunctionName { .. }
                | Instruction::Inc { .. }
                | Instruction::Dec { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::CreateIteratorResult { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::Generator { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::PushInt8 { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::PushInt16 { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::PushInt32 { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::PushFloat { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::PushDouble { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::PushLiteral { .. }
                | Instruction::PushRegexp { .. }
                | Instruction::HasRestrictedGlobalProperty { .. }
                | Instruction::CanDeclareGlobalFunction { .. }
                | Instruction::CanDeclareGlobalVar { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::Jump { address } => {
                    graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
                    graph.add_edge(
                        previous_pc,
                        address as usize,
                        None,
                        Color::None,
                        EdgeStyle::Line,
                    );
                }
                Instruction::JumpIfFalse { address, .. }
                | Instruction::JumpIfTrue { address, .. }
                | Instruction::JumpIfNotUndefined { address, .. }
                | Instruction::JumpIfNullOrUndefined { address, .. } => {
                    graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
                    graph.add_edge(
                        previous_pc,
                        address as usize,
                        Some("YES".into()),
                        Color::Green,
                        EdgeStyle::Line,
                    );
                    graph.add_edge(
                        previous_pc,
                        pc,
                        Some("NO".into()),
                        Color::Red,
                        EdgeStyle::Line,
                    );
                }
                Instruction::TemplateLookup { .. } | Instruction::TemplateCreate { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::LogicalAnd { address, .. }
                | Instruction::LogicalOr { address, .. }
                | Instruction::Coalesce { address, .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                    graph.add_edge(
                        previous_pc,
                        address as usize,
                        Some("SHORT CIRCUIT".into()),
                        Color::Red,
                        EdgeStyle::Line,
                    );
                }
                Instruction::Case { address, .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(
                        previous_pc,
                        pc,
                        Some("NO".into()),
                        Color::Red,
                        EdgeStyle::Line,
                    );
                    graph.add_edge(
                        previous_pc,
                        address as usize,
                        Some("YES".into()),
                        Color::Green,
                        EdgeStyle::Line,
                    );
                }
                Instruction::GeneratorDelegateNext {
                    return_method_undefined,
                    throw_method_undefined,
                    ..
                } => {
                    graph.add_node(
                        previous_pc,
                        NodeShape::Diamond,
                        opcode_str.into(),
                        Color::None,
                    );
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                    graph.add_edge(
                        previous_pc,
                        throw_method_undefined as usize,
                        Some("`throw` undefined".into()),
                        Color::Red,
                        EdgeStyle::Line,
                    );
                    graph.add_edge(
                        previous_pc,
                        return_method_undefined as usize,
                        Some("`return` undefined".into()),
                        Color::Blue,
                        EdgeStyle::Line,
                    );
                }
                Instruction::GeneratorDelegateResume { r#return, exit, .. } => {
                    graph.add_node(
                        previous_pc,
                        NodeShape::Diamond,
                        opcode_str.into(),
                        Color::None,
                    );
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                    graph.add_edge(
                        previous_pc,
                        r#return as usize,
                        Some("return".into()),
                        Color::Yellow,
                        EdgeStyle::Line,
                    );
                    graph.add_edge(
                        previous_pc,
                        exit as usize,
                        Some("done".into()),
                        Color::Blue,
                        EdgeStyle::Line,
                    );
                }
                Instruction::CallEval { .. }
                | Instruction::Call { .. }
                | Instruction::New { .. }
                | Instruction::SuperCall { .. }
                | Instruction::ConcatToString { .. }
                | Instruction::GetArgument { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::JumpIfNotResumeKind { address, .. } => {
                    graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
                    graph.add_edge(
                        previous_pc,
                        address as usize,
                        Some("EXIT".into()),
                        Color::Red,
                        EdgeStyle::Line,
                    );
                    graph.add_edge(previous_pc, pc, None, Color::Green, EdgeStyle::Line);
                }
                Instruction::CopyDataProperties { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::PushScope { .. } => {
                    let random = rand::random();

                    graph.add_node(
                        previous_pc,
                        NodeShape::None,
                        label.into(),
                        Color::from_random_number(random),
                    );
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::PopEnvironment => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::GetFunction { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::DefVar { .. }
                | Instruction::DefInitVar { .. }
                | Instruction::PutLexicalValue { .. }
                | Instruction::GetName { .. }
                | Instruction::GetNameGlobal { .. }
                | Instruction::GetLocator { .. }
                | Instruction::GetNameAndLocator { .. }
                | Instruction::GetNameOrUndefined { .. }
                | Instruction::SetName { .. }
                | Instruction::DeleteName { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::GetPropertyByName { .. }
                | Instruction::GetPropertyByValue { .. }
                | Instruction::GetPropertyByValuePush { .. }
                | Instruction::SetPropertyByName { .. }
                | Instruction::DefineOwnPropertyByName { .. }
                | Instruction::DefineClassStaticMethodByName { .. }
                | Instruction::DefineClassMethodByName { .. }
                | Instruction::SetPropertyGetterByName { .. }
                | Instruction::DefineClassStaticGetterByName { .. }
                | Instruction::DefineClassGetterByName { .. }
                | Instruction::SetPropertySetterByName { .. }
                | Instruction::DefineClassStaticSetterByName { .. }
                | Instruction::DefineClassSetterByName { .. }
                | Instruction::SetPrivateField { .. }
                | Instruction::DefinePrivateField { .. }
                | Instruction::SetPrivateMethod { .. }
                | Instruction::SetPrivateSetter { .. }
                | Instruction::SetPrivateGetter { .. }
                | Instruction::GetPrivateField { .. }
                | Instruction::DeletePropertyByName { .. }
                | Instruction::PushClassFieldPrivate { .. }
                | Instruction::PushClassPrivateGetter { .. }
                | Instruction::PushClassPrivateSetter { .. }
                | Instruction::PushClassPrivateMethod { .. }
                | Instruction::InPrivate { .. }
                | Instruction::ThrowMutateImmutable { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::ThrowNewTypeError { .. }
                | Instruction::ThrowNewSyntaxError { .. }
                | Instruction::ThrowNewReferenceError { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
                        graph.add_edge(
                            previous_pc,
                            handler.handler() as usize,
                            Some(format!("Handler {i:2}: CAUGHT").into()),
                            Color::None,
                            EdgeStyle::Line,
                        );
                    }
                }
                Instruction::Throw { .. } | Instruction::ReThrow => {
                    if let Some((i, handler)) = self.find_handler(previous_pc as u32) {
                        graph.add_node(previous_pc, NodeShape::Record, label.into(), Color::None);
                        graph.add_edge(
                            previous_pc,
                            handler.handler() as usize,
                            Some(format!("Handler {i:2}: CAUGHT").into()),
                            Color::None,
                            EdgeStyle::Line,
                        );
                    } else {
                        graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::None);
                    }
                }
                Instruction::PushPrivateEnvironment { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::JumpTable {
                    index: _,
                    default,
                    addresses,
                } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(
                        previous_pc,
                        default as usize,
                        Some("DEFAULT".into()),
                        Color::None,
                        EdgeStyle::Line,
                    );

                    for (i, address) in addresses.iter().enumerate() {
                        graph.add_edge(
                            previous_pc,
                            *address as usize,
                            Some(format!("Index: {i}").into()),
                            Color::None,
                            EdgeStyle::Line,
                        );
                    }
                }
                Instruction::Pop
                | Instruction::PushZero { .. }
                | Instruction::PushOne { .. }
                | Instruction::PushNan { .. }
                | Instruction::PushPositiveInfinity { .. }
                | Instruction::PushNegativeInfinity { .. }
                | Instruction::PushNull { .. }
                | Instruction::PushTrue { .. }
                | Instruction::PushFalse { .. }
                | Instruction::PushUndefined { .. }
                | Instruction::PushEmptyObject { .. }
                | Instruction::PushClassPrototype { .. }
                | Instruction::SetClassPrototype { .. }
                | Instruction::SetHomeObject { .. }
                | Instruction::TypeOf { .. }
                | Instruction::LogicalNot { .. }
                | Instruction::Pos { .. }
                | Instruction::Neg { .. }
                | Instruction::SetPropertyByValue { .. }
                | Instruction::DefineOwnPropertyByValue { .. }
                | Instruction::DefineClassStaticMethodByValue { .. }
                | Instruction::DefineClassMethodByValue { .. }
                | Instruction::SetPropertyGetterByValue { .. }
                | Instruction::DefineClassStaticGetterByValue { .. }
                | Instruction::DefineClassGetterByValue { .. }
                | Instruction::SetPropertySetterByValue { .. }
                | Instruction::DefineClassStaticSetterByValue { .. }
                | Instruction::DefineClassSetterByValue { .. }
                | Instruction::DeletePropertyByValue { .. }
                | Instruction::DeleteSuperThrow
                | Instruction::ToPropertyKey { .. }
                | Instruction::This { .. }
                | Instruction::ThisForObjectEnvironmentName { .. }
                | Instruction::Super { .. }
                | Instruction::IncrementLoopIteration
                | Instruction::CreateForInIterator { .. }
                | Instruction::GetIterator { .. }
                | Instruction::GetAsyncIterator { .. }
                | Instruction::IteratorNext
                | Instruction::IteratorFinishAsyncNext { .. }
                | Instruction::IteratorValue { .. }
                | Instruction::IteratorResult { .. }
                | Instruction::IteratorDone { .. }
                | Instruction::IteratorToArray { .. }
                | Instruction::IteratorReturn { .. }
                | Instruction::IteratorStackEmpty { .. }
                | Instruction::ValueNotNullOrUndefined { .. }
                | Instruction::RestParameterInit { .. }
                | Instruction::PushValueToArray { .. }
                | Instruction::PushElisionToArray { .. }
                | Instruction::PushIteratorToArray { .. }
                | Instruction::PushNewArray { .. }
                | Instruction::GeneratorYield { .. }
                | Instruction::AsyncGeneratorYield { .. }
                | Instruction::AsyncGeneratorClose
                | Instruction::CreatePromiseCapability
                | Instruction::CompletePromiseCapability
                | Instruction::GeneratorNext { .. }
                | Instruction::PushClassField { .. }
                | Instruction::SuperCallDerived
                | Instruction::Await { .. }
                | Instruction::NewTarget { .. }
                | Instruction::ImportMeta { .. }
                | Instruction::CallEvalSpread { .. }
                | Instruction::CallSpread
                | Instruction::NewSpread
                | Instruction::SuperCallSpread
                | Instruction::SuperCallPrepare { .. }
                | Instruction::SetPrototype { .. }
                | Instruction::IsObject { .. }
                | Instruction::SetNameByLocator { .. }
                | Instruction::PushObjectEnvironment { .. }
                | Instruction::PopPrivateEnvironment
                | Instruction::ImportCall { .. }
                | Instruction::Exception { .. }
                | Instruction::MaybeException { .. }
                | Instruction::CheckReturn
                | Instruction::BindThisValue { .. }
                | Instruction::CreateMappedArgumentsObject { .. }
                | Instruction::CreateUnmappedArgumentsObject { .. }
                | Instruction::CreateGlobalFunctionBinding { .. }
                | Instruction::CreateGlobalVarBinding { .. } => {
                    graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None);
                    graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line);
                }
                Instruction::Return => {
                    graph.add_node(previous_pc, NodeShape::Diamond, label.into(), Color::Red);
                }
                Instruction::Reserved1
                | Instruction::Reserved2
                | Instruction::Reserved3
                | Instruction::Reserved4
                | Instruction::Reserved5
                | Instruction::Reserved6
                | Instruction::Reserved7
                | Instruction::Reserved8
                | Instruction::Reserved9
                | Instruction::Reserved10
                | Instruction::Reserved11
                | Instruction::Reserved12
                | Instruction::Reserved13
                | Instruction::Reserved14
                | Instruction::Reserved15
                | Instruction::Reserved16
                | Instruction::Reserved17
                | Instruction::Reserved18
                | Instruction::Reserved19
                | Instruction::Reserved20
                | Instruction::Reserved21
                | Instruction::Reserved22
                | Instruction::Reserved23
                | Instruction::Reserved24
                | Instruction::Reserved25
                | Instruction::Reserved26
                | Instruction::Reserved27
                | Instruction::Reserved28
                | Instruction::Reserved29
                | Instruction::Reserved30
                | Instruction::Reserved31
                | Instruction::Reserved32
                | Instruction::Reserved33
                | Instruction::Reserved34
                | Instruction::Reserved35
                | Instruction::Reserved36
                | Instruction::Reserved37
                | Instruction::Reserved38
                | Instruction::Reserved39
                | Instruction::Reserved40
                | Instruction::Reserved41
                | Instruction::Reserved42
                | Instruction::Reserved43
                | Instruction::Reserved44
                | Instruction::Reserved45
                | Instruction::Reserved46
                | Instruction::Reserved47
                | Instruction::Reserved48
                | Instruction::Reserved49
                | Instruction::Reserved50
                | Instruction::Reserved51
                | Instruction::Reserved52
                | Instruction::Reserved53
                | Instruction::Reserved54
                | Instruction::Reserved55
                | Instruction::Reserved56
                | Instruction::Reserved57
                | Instruction::Reserved58
                | Instruction::Reserved59
                | Instruction::Reserved60
                | Instruction::Reserved61
                | Instruction::Reserved62
                | Instruction::Reserved63 => unreachable!("Reserved opcodes are unreachable"),
            }
        }

        for constant in &self.constants {
            if let Constant::Function(function) = constant {
                let subgraph = graph.subgraph(String::new());
                function.to_graph(subgraph);
            }
        }
    }
}