Implement debug object for CLI (#2772)
Currently some debugging stuff in JavaScript land is difficult to impossible, like triggering a GC collect, this is not impossible to do in JavaScript the way I triggered it was by creating a huge amount of object `for (let i = 0; i < 100000; ++i) { ({}) }` but this is cumbersome and not guaranteed to trigger a gc.
This PR implements `--debug-object` flag that injects the `$boa` debug object in the context, the object is separated into modules currently `gc`, `function`, `object`.
We can now do `$boa.gc.collect()`, which force triggers a GC collect.
Or sometimes I wanted a trace (the current solution is great, you can trace stuff like `>>> 1 + 1` but that is also it's limitation), it traces everything, I sometimes have a scenario and just want to trace a single function in that scenario, that's why I added the `$boa.function.trace(func, this, ...args)` It only traces the function.
```js
>> $boa.function.trace((a, b) => a + b, undefined, 1, 2)
-------------------------Compiled Output: ''--------------------------
Location Count Opcode Operands
000000 0000 DefInitArg 0000: 'a'
000005 0001 DefInitArg 0001: 'b'
000010 0002 RestParameterPop
000011 0003 GetName 0000: 'a'
000016 0004 GetName 0001: 'b'
000021 0005 Add
000022 0006 Return
000023 0007 PushUndefined
000024 0008 Return
... (cut for brevity) ...
```
It also implements `$boa.function.flowgraph(func, options)`:
```js
$boa.function.flowgraph(func, 'graphviz')
$boa.function.flowgraph(func, { format: 'mermaid', direction: 'TopBottom' })
```
Printing the object pointer:
```js
$boa.object.id({}) // '0x566464F33'
```
It currently implements some functionality which we can grow it with our debugging needs since we are not restricted by a spec we can add whatever we want :)
I was originally going to implement this in #2723 (but the PR is too big), for shapes having functions like:
```js
$boa.shape.type({}) // Shared shape
$boa.shape.id({}) // 0x8578FG355 (objects, shape pointer)
$boa.shape.flowgraph({}) // printing the shape transition chain, like $boa.function.flowgraph
```
Shapes chains are very hard to debug once they are big... so having this type of debugging capability would make it much easier.
2 years ago
|
|
|
# Boa Debug Object
|
|
|
|
|
|
|
|
The `$boa` object contains useful utilities that can be used to debug JavaScript in JavaScript.
|
|
|
|
|
|
|
|
It's injected into the context as global variable with the `--debug-object` command-line flag,
|
|
|
|
the object is separated into modules.
|
|
|
|
|
|
|
|
## Module `$boa.gc`
|
|
|
|
|
|
|
|
This module contains functions that are related the garbage collector. It currently has the `.collect()` method.
|
|
|
|
|
|
|
|
```JavaScript
|
|
|
|
$boa.gc.collect()
|
|
|
|
```
|
|
|
|
|
|
|
|
This force triggers the GC to scan the heap and collect garbage.
|
|
|
|
|
|
|
|
## Module `$boa.function`
|
|
|
|
|
|
|
|
In this module are untility functions related to execution and debugging function.
|
|
|
|
|
|
|
|
### Function `$boa.function.bytecode(func)`
|
|
|
|
|
|
|
|
This function returns the compiled bytecode of a function as a string,
|
|
|
|
|
|
|
|
```JavaScript
|
|
|
|
>> function add(x, y) {
|
|
|
|
return x + y
|
|
|
|
}
|
|
|
|
>> $boa.function.bytecode(add)
|
|
|
|
"
|
|
|
|
------------------------Compiled Output: 'add'------------------------
|
|
|
|
Location Count Opcode Operands
|
|
|
|
|
|
|
|
000000 0000 DefInitArg 0000: 'a'
|
|
|
|
000005 0001 DefInitArg 0001: 'b'
|
|
|
|
000010 0002 RestParameterPop
|
|
|
|
000011 0003 GetName 0000: 'a'
|
|
|
|
000016 0004 GetName 0001: 'b'
|
|
|
|
000021 0005 Add
|
|
|
|
000022 0006 Return
|
|
|
|
000023 0007 PushUndefined
|
|
|
|
000024 0008 Return
|
|
|
|
|
|
|
|
Literals:
|
|
|
|
<empty>
|
|
|
|
|
|
|
|
Bindings:
|
|
|
|
0000: a
|
|
|
|
0001: b
|
|
|
|
|
|
|
|
Functions:
|
|
|
|
<empty>
|
|
|
|
"
|
|
|
|
>>
|
|
|
|
```
|
|
|
|
|
|
|
|
### Function `$boa.function.trace(func, this, ...args)`
|
|
|
|
|
|
|
|
It only traces the specified function. If the specified function calls other functions,
|
|
|
|
their instructions aren't traced.
|
|
|
|
|
|
|
|
```JavaScript
|
|
|
|
>> const add = (a, b) => a + b
|
|
|
|
>> $boa.function.trace(add, undefined, 1, 2)
|
|
|
|
5μs DefInitArg 0000: 'a' 2
|
|
|
|
4μs DefInitArg 0001: 'b' <empty>
|
|
|
|
0μs RestParameterPop <empty>
|
|
|
|
3μs GetName 0000: 'a' 1
|
|
|
|
1μs GetName 0001: 'b' 2
|
|
|
|
2μs Add 3
|
|
|
|
1μs Return 3
|
|
|
|
3
|
|
|
|
>>
|
|
|
|
```
|
|
|
|
|
|
|
|
The `this` value can be changed as well as the arguments that are passed to the function.
|
|
|
|
|
|
|
|
### Function `$boa.function.traceable(func, mode)`
|
|
|
|
|
|
|
|
Marks a single function as traceable on all future executions of the function. Both useful to mark
|
|
|
|
several functions as traceable and to trace functions that suspend their execution (async functions,
|
|
|
|
generators, async generators).
|
|
|
|
|
|
|
|
#### Input
|
|
|
|
|
|
|
|
```Javascript
|
|
|
|
function* g() {
|
|
|
|
yield 1;
|
|
|
|
yield 2;
|
|
|
|
yield 3;
|
|
|
|
}
|
|
|
|
$boa.function.traceable(g, true);
|
|
|
|
var iter = g();
|
|
|
|
iter.next();
|
|
|
|
iter.next();
|
|
|
|
iter.next();
|
|
|
|
```
|
|
|
|
|
|
|
|
#### Output
|
|
|
|
|
|
|
|
```bash
|
|
|
|
1μs RestParameterPop <empty>
|
|
|
|
1μs PushUndefined undefined
|
|
|
|
2μs Yield undefined
|
|
|
|
4μs GetName 0000: 'a' 1
|
|
|
|
0μs Yield 1
|
|
|
|
1μs GeneratorNext undefined
|
|
|
|
1μs Pop <empty>
|
|
|
|
15μs GetName 0001: 'b' 2
|
|
|
|
1μs Yield 2
|
|
|
|
1μs GeneratorNext undefined
|
|
|
|
1μs Pop <empty>
|
|
|
|
4μs GetName 0002: 'c' 3
|
|
|
|
1μs Yield 3
|
|
|
|
```
|
|
|
|
|
Implement debug object for CLI (#2772)
Currently some debugging stuff in JavaScript land is difficult to impossible, like triggering a GC collect, this is not impossible to do in JavaScript the way I triggered it was by creating a huge amount of object `for (let i = 0; i < 100000; ++i) { ({}) }` but this is cumbersome and not guaranteed to trigger a gc.
This PR implements `--debug-object` flag that injects the `$boa` debug object in the context, the object is separated into modules currently `gc`, `function`, `object`.
We can now do `$boa.gc.collect()`, which force triggers a GC collect.
Or sometimes I wanted a trace (the current solution is great, you can trace stuff like `>>> 1 + 1` but that is also it's limitation), it traces everything, I sometimes have a scenario and just want to trace a single function in that scenario, that's why I added the `$boa.function.trace(func, this, ...args)` It only traces the function.
```js
>> $boa.function.trace((a, b) => a + b, undefined, 1, 2)
-------------------------Compiled Output: ''--------------------------
Location Count Opcode Operands
000000 0000 DefInitArg 0000: 'a'
000005 0001 DefInitArg 0001: 'b'
000010 0002 RestParameterPop
000011 0003 GetName 0000: 'a'
000016 0004 GetName 0001: 'b'
000021 0005 Add
000022 0006 Return
000023 0007 PushUndefined
000024 0008 Return
... (cut for brevity) ...
```
It also implements `$boa.function.flowgraph(func, options)`:
```js
$boa.function.flowgraph(func, 'graphviz')
$boa.function.flowgraph(func, { format: 'mermaid', direction: 'TopBottom' })
```
Printing the object pointer:
```js
$boa.object.id({}) // '0x566464F33'
```
It currently implements some functionality which we can grow it with our debugging needs since we are not restricted by a spec we can add whatever we want :)
I was originally going to implement this in #2723 (but the PR is too big), for shapes having functions like:
```js
$boa.shape.type({}) // Shared shape
$boa.shape.id({}) // 0x8578FG355 (objects, shape pointer)
$boa.shape.flowgraph({}) // printing the shape transition chain, like $boa.function.flowgraph
```
Shapes chains are very hard to debug once they are big... so having this type of debugging capability would make it much easier.
2 years ago
|
|
|
## Function `$boa.function.flowgraph(func, options)`
|
|
|
|
|
|
|
|
It can be used to get the instruction flowgraph, like the command-line flag.
|
|
|
|
This works on the function level, allows getting the flow graph without
|
|
|
|
quiting the boa shell and adding the specified flags.
|
|
|
|
|
|
|
|
Besides the function it also takes an argument that, can be a string or an object.
|
|
|
|
If it is a string it represets the flowgraph format, otherwire if it's an object:
|
|
|
|
|
|
|
|
```JavaScript
|
|
|
|
// These are the defaults, if not specified.
|
|
|
|
{
|
|
|
|
format: 'mermaid'
|
|
|
|
direction: 'LeftRight' // or 'LR' shorthand.
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
```JavaScript
|
|
|
|
$boa.function.flowgraph(func, 'graphviz')
|
|
|
|
$boa.function.flowgraph(func, { format: 'mermaid', direction: 'TopBottom' })
|
|
|
|
```
|
|
|
|
|
|
|
|
## Module `$boa.object`
|
|
|
|
|
|
|
|
Contains utility functions for getting internal information about an object.
|
|
|
|
|
|
|
|
## Function `$boa.object.id(object)`
|
|
|
|
|
|
|
|
This function returns memory address of the given object, as a string.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
```JavaScript
|
|
|
|
let o = { x: 10, y: 20 }
|
|
|
|
$boa.object.id(o) // '0x7F5B3251B718'
|
|
|
|
|
|
|
|
// Geting the address of the $boa object in memory
|
|
|
|
$boa.object.id($boa) // '0x7F5B3251B5D8'
|
|
|
|
```
|
|
|
|
|
|
|
|
## Module `$boa.optimizer`
|
|
|
|
|
|
|
|
This modules contains getters and setters for enabling and disabling optimizations.
|
|
|
|
|
|
|
|
### Getter & Setter `$boa.optimizer.constantFolding`
|
|
|
|
|
|
|
|
This is and accessor property on the module, its getter returns `true` if enabled or `false` otherwise.
|
|
|
|
Its setter can be used to enable/disable the constant folding optimization.
|
|
|
|
|
|
|
|
```JavaScript
|
|
|
|
$boa.optimizer.constantFolding = true
|
|
|
|
$boa.optimizer.constantFolding // true
|
|
|
|
```
|
|
|
|
|
|
|
|
### Getter & Setter `$boa.optimizer.statistics`
|
|
|
|
|
|
|
|
This is and accessor property on the module, its getter returns `true` if enabled or `false` otherwise.
|
|
|
|
Its setter can be used to enable/disable optimization statistics, which are printed to `stdout`.
|
|
|
|
|
|
|
|
```JavaScript
|
|
|
|
>> $boa.optimizer.constantFolding = true
|
|
|
|
>> $boa.optimizer.statistics = true
|
|
|
|
>> 1 + 1
|
|
|
|
Optimizer {
|
|
|
|
constant folding: 1 run(s), 2 pass(es) (1 mutating, 1 checking)
|
|
|
|
}
|
|
|
|
|
|
|
|
2
|
|
|
|
>>
|
|
|
|
```
|
|
|
|
|
|
|
|
## Module `$boa.realm`
|
|
|
|
|
|
|
|
This modules contains realm utilities to test cross-realm behaviour.
|
|
|
|
|
|
|
|
### `$boa.realm.create`
|
|
|
|
|
|
|
|
Creates a new realm with a new set of builtins and returns its global object.
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
let global = $boa.realm.create();
|
|
|
|
|
|
|
|
Object != global.Object; // true
|
|
|
|
```
|