sajad torkamani

What is a JavaScript engine?

A JavaScript engine is the software component responsible for parsing, compiling, and executing JavaScript code. This can be client-side code that’s executed inside a web browser (e.g., Chrome or Firefox) or server-side code executed in a runtime like Node.js or Deno.

Here are some popular JavaScript engines:

NameUsed in
V8Chromium based browsers (Chrome, Edge, Brave, Electron), Node.js, Deno
SpiderMonkeyFirefox
JavaScriptCoreSafari
ChakraInternet Explorer

A closer look at V8

Let’s take a closer look at what the V8 engine does when you execute a Node.js program like this:

function printMessage() {
  const message = 'Hello world'
  console.log(message)
}

printMessage()

Here’s an approximate overview of what V8 does. We’ll take a closer look at each step.

JavaScript V8 engine

1. Scanner converts source code into known tokens

Your source code is usually encoded (UTF-8 by default in Chrome and Node) as a stream of bytes. A program called the scanner parses this stream and converts it into a list of known tokens .

For example, our sample program would be encoded in UTF-8 as follows:

\x66\x75\x6E\x63\x74\x69\x6F\x6E\x20\x70\x72\x69\x6E\x74\x4D\x65\x73\x73\x61\x67\x65\x28\x29\x20\x7B\x0A\x20\x20\x63\x6F\x6E\x73\x74\x20\x6D\x65\x73\x73\x61\x67\x65\x20\x3D\x20\x27\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x27\x3B\x0A\x20\x20\x63\x6F\x6E\x73\x6F\x6C\x65\x2E\x6C\x6F\x67\x28\x6D\x65\x73\x73\x61\x67\x65\x29\x3B\x0A\x7D\x0A\x0A\x70\x72\x69\x6E\x74\x4D\x65\x73\x73\x61\x67\x65\x28\x29\x3B\x0A

Check out this link to see how source code translates to UTF-8.

Every time the scanner comes across a known construct like functionconst, or var, it will convert it into its corresponding token. Refer to keywords.txt for the full list of mappings between source code constructs and tokens.

2. Parser converts tokens into AST

parser parses the list of tokens to generate an Abstract Syntax Tree (AST). An AST is a tree representation of source code in which each node represents a code construct (e.g., function literal, block, variable declaration, assignment) .

Here’s an example AST:

Example JavaScript AST

Check out this link to get an idea of what an AST looks like for the sample Node.js program shown earlier.

3. Ignition interpreter compiles AST into bytecode

V8’s Ignition interpreter compiles the generated AST into bytecode that’s not tied to any processor or machine implementation.

You can see bytecode output for a Node program by running something like:

node --print-bytecode --print-bytecode-filter=<func-name> <file>

Assuming you print the bytecode for the printMessage function from above:

node --print-bytecode --print-bytecode-filter=printMessage program.js

You’ll get something like this:

[generated bytecode for function: printMessage (0x1c616aa37f89 <SharedFunctionInfo printMessage>)]
Bytecode length: 19
Parameter count 1
Register count 3
Frame size 24
OSR nesting level: 0
Bytecode Age: 0
   44 S> 0x1c616aa38cee @    0 : 13 00             LdaConstant [0]
         0x1c616aa38cf0 @    2 : c3                Star0 
   61 S> 0x1c616aa38cf1 @    3 : 21 01 00          LdaGlobal [1], [0]
         0x1c616aa38cf4 @    6 : c1                Star2 
   69 E> 0x1c616aa38cf5 @    7 : 2d f8 02 02       LdaNamedProperty r2, [2], [2]
         0x1c616aa38cf9 @   11 : c2                Star1 
   69 E> 0x1c616aa38cfa @   12 : 5d f9 f8 fa 04    CallProperty1 r1, r2, r0, [4]
         0x1c616aa38cff @   17 : 0e                LdaUndefined 
   83 S> 0x1c616aa38d00 @   18 : a8                Return 
Constant pool (size = 3)
0x1c616aa38c91: [FixedArray] in OldSpace
 - map: 0x2834596412c1 <Map>
 - length: 3
           0: 0x1c616aa38c29 <String[11]: #Hello world>
           1: 0x28c6e5db90e9 <String[7]: #console>
           2: 0x28c6e5d8c0e1 <String[3]: #log>
Handler Table (size = 0)
Source Position Table (size = 12)
0x1c616aa38d09 <ByteArray[12]>
Hello world

4. Turbofan compiler compiles bytecode into machine code

V8’s Turbofan compiler compiles the bytecode into optimized machine code (binary) for the current processor (Intel, ARM, Apple Silicon, etc). Turbofan will analyze the bytecode and do a bunch of clever optimizations (maybe need to learn more about this).

5. Machine code is executed

The machine code is executed by the CPU.

Sources

Tagged: JavaScript