Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

316 lines
8.9 KiB

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 Handler Opcode Operands
000000 0000 none CreateMappedArgumentsObject
000001 0001 none PutLexicalValue 2: 0
000004 0002 none GetArgument 0
000006 0003 none PutLexicalValue 2: 1
000009 0004 none GetArgument 1
000011 0005 none PutLexicalValue 2: 2
000014 0006 none PushDeclarativeEnvironment 2
000016 0007 none GetName 0000: 'x'
000018 0008 none GetName 0001: 'y'
000020 0009 none Add
000021 0010 none SetReturnValue
000022 0011 none CheckReturn
000023 0012 none Return
000024 0013 none CheckReturn
000025 0014 none Return
Constants:
0000: [ENVIRONMENT] index: 1, bindings: 1
0001: [ENVIRONMENT] index: 2, bindings: 3
0002: [ENVIRONMENT] index: 3, bindings: 0
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
Bindings:
0000: x
0001: y
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
Handlers: <empty>
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.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'
```
## Function `$boa.object.indexedStorageType(object)`
This function returns indexed storage type.
Example:
```JavaScript
let a = [1, 2]
$boa.object.indexedStorageType(a) // 'DenseI32'
a.push(0xdeadbeef)
$boa.object.indexedStorageType(a) // 'DenseI32'
a.push(0.5)
$boa.object.indexedStorageType(a) // 'DenseF64'
a.push("Hello")
$boa.object.indexedStorageType(a) // 'DenseElement'
a[100] = 100 // Make a hole
$boa.object.indexedStorageType(a) // 'SparseElement'
```
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
## 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 an accessor property on the module, its getter returns `true` if enabled or `false` otherwise.
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
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 module 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
```
Implement `Hidden classes` (#2723) This PR implements `Hidden Classes`, I named them as `Shapes` (like Spidermonkey does), calling them maps like v8 seems confusing because there already is a JS builtin, likewise with `Hidden classes` since there are already classes in JS. There are two types of shapes: `shared` shapes that create the transition tree, and are shared between objects, this is mainly intended for user defined objects this makes more sense because shapes can create transitions trees, doing that for the builtins seems wasteful (unless users wanted to creating an object with the same property names and the same property attributes in the same order... which seems unlikely). That's why I added `unique` shapes, only one object has it. This is similar to previous solution, but this architecture enables us to use inline caching. There will probably be a performance hit until we implement inline caching. There still a lot of work that needs to be done, on this: - [x] Move Property Attributes to shape - [x] Move Prototype to shape - [x] ~~Move extensible flag to shape~~, On further evaluation this doesn't give any benefit (at least right now), since it isn't used by inline caching also adding one more transition. - [x] Implement delete for unique shapes. - [x] If the chain is too long we should probably convert it into a `unique` shape - [x] Figure out threshold ~~(maybe more that 256 properties ?)~~ curently set to an arbitrary number (`1024`) - [x] Implement shared property table between shared shapes - [x] Add code Document - [x] Varying size storage for properties (get+set = 2, data = 1) - [x] Add shapes to more object: - [x] ordinary object - [x] Arrays - [x] Functions - [x] Other builtins - [x] Add `shapes.md` doc explaining shapes in depth with mermaid diagrams :) - [x] Add `$boa.shape` module - [x] `$boa.shape.id(o)` - [x] `$boa.shape.type(o)` - [x] `$boa.shape.same(o1, o2)` - [x] add doc to `boa_object.md`
2 years ago
## Module `$boa.shape`
This module contains helpful functions for getting information about a shape of an object.
### Function `$boa.shape.id(object)`
Returns the pointer of the object's shape in memory as a string encoded in hexadecimal format.
```JavaScript
$boa.shape.id(Number) // '0x7FC35A073868'
$boa.shape.id({}) // '0x7FC35A046258'
```
### Function `$boa.shape.type(object)`
Returns the object's shape type.
```JavaScript
$boa.shape.type({x: 3}) // 'shared'
$boa.shape.type(Number) // 'unique'
```
### Function `$boa.shape.same(o1, o2)`
Returns `true` if both objects have the same shape.
```JavaScript
// The values of the properties are not important!
let o1 = { x: 10 }
let o2 = {}
$boa.shape.same(o1, o2) // false
o2.x = 20
$boa.shape.same(o1, o2) // true
o2.y = 200
$boa.shape.same(o1, o2) // false
```
## Module `$boa.limits`
This module contains utilities for changing runtime limits.
### Getter & Setter `$boa.limits.loop`
This is an accessor property on the module, its getter returns the loop iteration limit before an error is thrown.
Its setter can be used to set the loop iteration limit.
```javascript
$boa.limits.loop = 10;
while (true) {} // RuntimeLimit: Maximum loop iteration limit 10 exceeded
```
### Getter & Setter `$boa.limits.stack`
This is an accessor property on the module, its getter returns the value stack limit before an error is thrown.
Its setter can be used to set the recursion limit.
```javascript
$boa.limits.stack = 10;
function x() {
return;
}
x(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // RuntimeLimit: exceeded maximum call stack length
```
### Getter & Setter `$boa.limits.recursion`
This is an accessor property on the module, its getter returns the recursion limit before an error is thrown.
Its setter can be used to set the recursion limit.
```javascript
$boa.limits.recursion = 100;
function x() {
return x();
}
x(); // RuntimeLimit: Maximum recursion limit 100 exceeded
```