JavaScript engine is the program responsible for executing the code coming from script
blocks. One of the most popular engines is V8 developed by Google. One the very high level here is how it works:
- Content of
script
is fetched by Html Parser as a sequence of bytes which then is transformed into the tokens by stream decoder.
- Decoded token are transformed into the AST. AST is a representation of the code’s structure.
- AST is transferred to interpreter called Ignition. It acts as a bytecode generator and as a high-performance bytecode interpreter. Ignition uses JIT compilation when walks through the tree.
- During the initial processing of JavaScript code, Ignition takes the parsed code and transforms it into a concise bytecode representation. The process is called Just-In-time (JIT) compilation. It’s not specific to JavaScript, for example it’s also used in .NET. In general it is the process of translating code written in a programming language to machine code at runtime.
- Bytecode is an intermediate code and suitable for cases when it needs to be run once. It is an abstraction of machine code. It’s a bit slow but JavaScript engine can run it by itself. Compared to the original JavaScript code, bytecode is more compact and easier for the interpreter to process.
- When the process is done AST can be deleted.
- Bytecode then passed to profiler called TurboFan with some metrics collected by Ignition interpreter. Profiler monitors and watches code to optimize it. It creates highly optimized machine code which runs directly on the CPU.
- At runtime, certain dynamic information is available, such as type identification. A JIT compiler monitors to detect functions or loops of code that are run multiple times. These pieces of code are then compiled. If they’re quite commonly executed, JIT will optimize them and also store the optimized compiled code for execution.
- TurboFan compiles the hottest parts of your code into machine code. By “hot” we mean the code that is executed again and again.
- It takes bytecode from Ignition and compiles architecture-independent instructions into machine code for the underlying hardware. It performs low-level instruction selection and machine register allocation in the process. It employs various optimization techniques to squeeze out the most performance from your JavaScript code. These include:
- Removing unused code sections
- Replacing function calls with the actual function body for better performance. This is called inlining
- Assigning variables to processor registers for faster access
- Making assumptions about data types to generate more efficient code
Bytecode is an intermediate representation representation for virtual machines or compilers. It is processed by the software, not hardware.
Machine code is a low-level code that can be run and understood directly by machine. It means it can be run directly on the CPU.
The main difference between compiler and interpreter is that the interpreter goes through the code line-by-line and executes it. The compiler prepares code for execution at first.
References