Refresh vm docs and fix bytecode trace output (#1921)
It changes the following:
- Refreshes the vm and debugging docs to represent the current state
- Fix some bytecode trace output
- Rename a field in the `CodeBlock`
@ -10,7 +10,7 @@ We use a crate called [measureme](https://github.com/rust-lang/measureme), which
When the "profiler" flag is enabled, you compile with the profiler and it is called throughout the interpreter.
When the "profiler" flag is enabled, you compile with the profiler and it is called throughout the interpreter.
when the feature flag is not enabled, you have an empty dummy implementation that is just no ops. rustc should completely optimize that away. So there should be no performance downgrade from these changes
when the feature flag is not enabled, you have an empty dummy implementation that is just no ops. rustc should completely optimize that away. So there should be no performance downgrade from these changes
By default Boa does not use the VM branch; execution is done via walking the AST. This allows us to work on the VM branch whilst not interrupting any progress made on AST execution.
![image](img/boa_architecture.drawio.png)
You can interpret bytecode by passing the "vm" flag (see below). The diagram below should illustrate how things work today (Jan 2021).
![image](img/boa_architecture.svg)
## Enabling ByteCode interpretation
You need to enable this via a feature flag. If using VSCode you can run `Cargo Run (VM)`. If using the command line you can pass `cargo run --features vm ../tests/js/test.js` from within the boa_cli folder. You can also pass the `--trace` optional flag to print the trace of the code.
-------------------------------------- Vm Start --------------------------------------
------------------------------------------ VM Start ------------------------------------------
Time Opcode Operands Top Of Stack
Time Opcode Operands Top Of Stack
64μs DefLet 0000: 'a' <empty>
3μs PushOne 1
386μs PushOne 1
21μs InitLexical 0000: 'a' <empty>
6μs DefInitLet 0000: 'a' <empty>
32μs DefLet 0001: 'b' <empty>
1μs PushInt8 2 2
2μs PushInt8 2 2
2μs DefInitLet 0001: 'b' <empty>
17μs InitLexical 0001: 'b' <empty>
Stack:
Stack:
<empty>
<empty>
@ -57,38 +50,25 @@ Stack:
undefined
undefined
```
```
The above will output three sections that are divided into subsections:
The above output contains the following information:
- The code that will be executed
- The bytecode and properties of the function that will be executed
- `Code`: The bytecode.
- `Compiled Output`: The bytecode.
- `Location`: Location of the instruction (instructions are not the same size).
- `Location`: Location of the instruction (instructions are not the same size).
- `Count`: Instruction count.
- `Count`: Instruction count.
- `Opcode`: Opcode name.
- `Operands`: The operands of the opcode.
- `Operands`: The operands of the opcode.
- `Literals`: The literals used by the opcode (like strings).
- `Literals`: The literals used by the bytecode (like strings).
- `Names`: Contains variable names.
- `Bindings`: Binding names used by the bytecode.
- The code being executed (marked by `"Vm Start"`).
- `Functions`: Function names use by the bytecode.
- The code being executed (marked by `Vm Start` or `Call Frame`).
- `Time`: The amount of time that instruction took to execute.
- `Time`: The amount of time that instruction took to execute.
- `Opcode`: Opcode name.
- `Opcode`: Opcode name.
- `Operands`: The operands this opcode took.
- `Operands`: The operands of the opcode.
- `Top Of Stack`: The top element of the stack **after** execution of instruction.
- `Top Of Stack`: The top element of the stack **after** execution of instruction.
- `Stack`: The trace of the stack after execution ends.
- `Stack`: The trace of the stack after execution ends.
- The result of the execution (The top element of the stack, if the stack is empty then `undefined` is returned).
- The result of the execution (The top element of the stack, if the stack is empty then `undefined` is returned).
### Instruction
This shows each instruction being executed and how long it took. This is useful for us to see if a particular instruction is taking too long.
Then you have the instruction itself and its operand. Last you have what is on the top of the stack **after** the instruction is executed, followed by the memory address of that same value. We show the memory address to identify if 2 values are the same or different.
### Literals
JSValues can live on the pool, which acts as our heap. Instructions often have an index of where on the pool it refers to a value.
You can use these values to match up with the instructions above. For e.g (using the above output) `DefLet 0` means take the value off the pool at index `0`, which is `a` and define it in the current scope.
### Stack
The stack view shows what the stack looks like for the JS executed.
Using the above output as an exmaple, after `PushOne` has been executed the next instruction (`InitLexical 0`) has a `1` on the top of the stack. This is because `PushOne` puts `1` on the stack.
### Comparing ByteCode output
### Comparing ByteCode output
If you wanted another engine's bytecode output for the same JS, SpiderMonkey's bytecode output is the best to use. You can follow the setup [here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Introduction_to_the_JavaScript_shell). You will need to build from source because the pre-built binarys don't include the debugging utilities which we need.
If you wanted another engine's bytecode output for the same JS, SpiderMonkey's bytecode output is the best to use. You can follow the setup [here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Introduction_to_the_JavaScript_shell). You will need to build from source because the pre-built binarys don't include the debugging utilities which we need.