What is V8
V8 is an open source JavaScript engine developed by Google and currently used in The Chrome browser and Node.js. Its core function is to execute JavaScript code. Its main core process is divided into two steps: compilation and execution. The JavaScript code needs to be converted to low-level intermediate code or machine code that the machine can understand, and then the converted code is executed and the execution results are printed.
1. The virtual machine
V8 can be regarded as an imaginary computer, also known as a virtual machine. The virtual machine implements code execution by simulating various functions of the real computer, such as simulating the CPU, stack, register, etc. The virtual machine also has its own set of instruction system. When V8 executes JavaScript code, you don’t need to worry about real-world differences between operating systems, and you don’t need to worry about differences between computers with different architectures. You just need to write the code according to the specifications of the virtual machine.
2. Why is high-level code compiled before it is executed
(1) Machine language
In order to accomplish complex tasks, engineers provide the CPU with a large set of Instructions to perform various functions. We call these Instructions Instructions, or machine language.
(2) Assembly instruction set
CPU can only recognize binary instructions, but for programmers, binary code is difficult to read and remember, so we convert the binary instruction set into human can recognize and remember symbols, this is the assembly instruction set. Machines cannot understand assembly language directly. So, you write a program in assembly, and you need an assembly compiler, which programs assembly code into machine code.
(3) High-level languages
Assembly language makes a layer of abstraction to machine language, reducing the complexity of understanding machine language, but assembly language is still complex and tedious. This is mainly reflected in the following two points
Different cpus have different instruction sets
If you want to implement a function in machine language or assembly language, you need to write specific assembly code for the CPU of each architecture.
When writing assembly code, we also need to understand the hardware related to the processor architecture
For example, you need to use registers, memory, operation CPU, etc. Most programmers, when writing applications, just want to focus on business logic and don’t want to learn too much about processor architecture. So we needed a language that masked the details of computer architecture, a language that could accommodate many different CPU architectures, and a language that could focus on business logic.
(4) High-level language implementation
Interpretation execution requires compiling the input source code into intermediate code through the parser, and then directly using the interpreter to interpret and execute the intermediate code, and then directly output the results.
Compilation execution also requires the conversion of source code into intermediate code, which the compiler then compiles into machine code. Usually compiled machine code is stored in a binary file, which is executed directly when the program needs to be executed. You can also use a virtual machine to store compiled machine code in memory and then execute the in-memory binaries directly.
How does V8 execute JavaScript
Instead of a single technique, V8 uses a mixture of compiler and interpreter execution known as JIT (Just In Time). This is a tradeoff strategy, as both approaches have their own pros and cons: explain execution starts fast but executes slowly, whereas compile execution starts slow but executes fast. For the general process, please refer to the following figure:
1. Prepare the basic environment
As you can see on the left, before V8 can start executing JavaScript, it needs to prepare some basic environments for executing JavaScript, including “heap space”, “stack space”, “global execution context”, “global scope”, “message loop system”, “Built-in functions,” and so on, are all things you need to use when executing JavaScript.
2. Take JavaScript source code and structure it
First, V8 receives the JavaScript source code to execute, but it’s just a bunch of strings to V8, and V8 doesn’t directly understand what the string means; it needs to structure it.
Structuralization means that information can be decomposed into multiple interrelated components after analysis. Each component has a clear hierarchical structure, which is convenient for use and maintenance, and has certain operation specifications.
The structuring of V8 source code generates an abstract syntax tree (AST), called the AST, which is a structure that V8 can easily understand. As well as generating the AST, V8 also generates the relevant scopes, which hold the relevant variables.
3. Generate bytecode
Bytecode is intermediate code between AST and machine code. But regardless of a particular type of machine code, the interpreter can interpret and execute bytecode directly, or the compiler can compile and execute bytecode into binary machine code.
4. Explain execution
The interpreter interprets the execution of the bytecode sequentially and prints the execution results.
5. Optimize hotspot code
V8 also has a module that monitors the state of the interpreter. In the process of interpreting the execution of bytecode, if it is found that a certain piece of code will be executed more than once, it is marked as hot code.
When a piece of code is marked as hot code, V8 throws the bytecode to the optimized compiler, which compiles the bytecode into binary code behind the scenes and optimizes the compiled binary. The optimized binary machine code executes much more efficiently. If this code is executed again, V8 will preferentially select the optimized binary code, which will make the code execution much faster.
6. The optimization
JavaScript is a very flexible dynamic language, structure and properties of objects can be arbitrary changes at runtime, and optimized the compiler optimized code can only be aimed at a fixed structure, once in the process of execution, the structure of the object is dynamic modification, after the optimization of code are bound to become invalid, The optimized compiler then needs to de-optimize, and the de-optimized code falls back to the interpreter the next time it is executed.
Install the V8
1. Use official documents to install the software
V8 is Google’s open source JavaScript engine and is also mirrored on Github. The V8 website provides a way to install V8 from source
Google resources need scientific Internet access, and I failed to configure git HTTP. proxy in the CMder on my computer, so I could not use VPN proxy traffic all the time, so this method failed
2. Use jSVU for installation
Jsvu is an NPM package. Jsvu stands for JavaScript (Engine) Version Updater. Is a toolkit for installing versions of JavaScript engines directly on the machine. It can be installed directly without compiling the source code.
(1) Install jSVU
npm i jsvu -g
Copy the code
(2) Run jSVU
Jsvu -h = jSVu v1 jSVu -h = jSVu v1 jSVu -h = jSVu v113.2.- the JavaScript engine Version Updater � [< engine > @ < Version >] [- OS = {mac64 mac64arm, linux32, on, win32, win64.default}] [- engines = {available, graaljs, hermes, javascriptcore, quickjs, spidermonkey, v8, v8 - debug, xs},... Complete documentation is online: https://github.com/GoogleChromeLabs/jsvu#readmeJsvu -- OS =win64 --engines= V8, V8-debugCopy the code
(3) View v8 and V8-Debug help information
After installing V8, v8-debug, switch to C:\ Users %USERPROFILE%.jsvu and you can see the installed CMD file. When viewing help documents, you can redirect the output to a file because the help documents of the two engines are too long, and then open the file using a text editor. The following command is used as an example:
touch v8-help.txt
v8 --help >> v8-help.txt
touch v8-debug-help.txt
v8-debug --help >> v8-debug-help.txt
Copy the code
This generates two help documents that can be used together with the help documents.
(4) Run V8, V8-debug
To execute JavaScript code directly on the command line, add the -e option and enclose the code in double quotes.
v8 -e "console.log(Date.now())"
1622362516291
Copy the code
2) Execute JavaScript files directly
Create a new index.js file in the current directory and write #console.log(Date.now())
v8 index.js
1622362597761The suffix must be complete, otherwise v8 index is reportedError reading 'index'
Copy the code
(5) Environmental variables
As a general rule, the C:\ Users %USERPROFILE%.jsvu directory can be configured into the environment variable and executed in any directory. In fact, the configuration does not take effect. The address is not found when you enter the PATH command on the command line. This point needs to be examined.
Track code execution
- Look at the AST
In conjunction with the above JavaScript execution flow, once the V8 environment is ready and the code is received, the first step is to “explain.” That is, the interpreter generates the AST and scope.
# jsvu install v8-debug, switch to the execution directory v8-debug-e --print-ast"var test=100"
[generating bytecode for function:] -AST ---
FUNC at0.KIND0.LITERAL ID0.SUSPEND COUNT0.NAME"".INFERRED NAME"".DECLS.VARIABLE (00000287FDC61870) (mode = VAR, assigned = true)"test".BLOCK NOCOMPLETIONS at- 1.EXPRESSION STATEMENT at9...INIT at9....VAR PROXY unallocated (00000287FDC61870) (mode = VAR, assigned = true)"test"....LITERAL100 -print-astParameters can only be usedv8-debug.v8There is noCopy the code
2. View scope
# jsvu install v8-debug and switch to the execution directory v8-debug-e --print-scopes"var test=100"
Global scope:
global { // (00000276E4C67CF8) (0, 12)
// will be compiled
// 1 stack slots
// temporary vars:
TEMPORARY .result; // (00000276E4C68030) local[0]
// local vars:
VAR test; // (00000276E4C67F40)
}
Copy the code
The above line generates a global scope, and you can see the test variable added to the global scope.
- View the generated bytecode
Once you have the AST and scope, you need to generate bytecode from both. The interpreter both generates and executes bytecodes.
# jsvu install v8-debug, switch to the execution directory v8-debug-e --print-bytecode"var test=100"
[generated bytecode for function: (0x028b08292abd <SharedFunctionInfo>)]
Bytecode length18:Parameter count 1
Register count 3
Frame size 24
OSR nesting level: 0
Bytecode Age: 0
0000028B08292B4A@ 0:13 00LdaConstant [0]
0000028B08292B4C@ # 2:c2 Star1
0000028B08292B4D@ 3:19fe f8 Mov <closure>, r2
0000028B08292B50@ 6:64 4f 01 f9 02 CallRuntime [DeclareGlobals].r1-r2
0000028B08292B55@ 11:0d 64 LdaSmi [100]
0000028B08292B57@ 13:23 01 00StaGlobal [1], [0]
0000028B08292B5A@ 16:0e LdaUndefined
0000028B08292B5B@ 17:a8 Return
Constant pool (size = 2)
0000028B08292B19: [FixedArray] in OldSpace
- map: 0x028b08002205 <Map>
- length: 2 0:0x028b08292b05 <FixedArray[1]>
1: 0x028b0820bfb9 <String[4] : #test>
Handler Table (size = 0)
Source Position Table (size = 0)
Copy the code
- View optimization and de-optimization
After the bytecode is generated, the interpreter interprets and executes the bytecode. If a code is repeatedly executed, the monitor marks it as hot code and submits it to the compiler for optimization. If you want to see which code is optimized, you can use the following command:
v8-debug -e --trace-opt "var test=100"
Copy the code
To see which code is de-optimized, use the following command line
v8-debug -e --trace-deopt "var test=100"
Copy the code