Recap18 / 19

How the engine works: a recap

How V8 prepares code, executes functions, and manages memory.

We will recap the JavaScript Engine section: how the engine prepares and executes code, stores objects, and releases memory.

V8 receives JavaScript source text and prepares instructions to execute it. Consider a function that creates an enemy object:

function spawnEnemy() {
  return { hp: 100 };
}

From source text to execution

An interactive diagram for the example above. Each stage passes its result to the next. V8's parser receives tokens from the scanner as it parses; these actions are separated here. The tree and bytecode are simplified.

  1. The scanner recognizes tokens

    Splits the text into parts: the word return, the property name hp, the number 100, braces, and other symbols.

    Text → tokens
    Tokens → parser
    functionspawnEnemy(){return{hp:100};}
  2. The parser builds the AST

    Builds a tree from the tokens to describe the program's structure. Here, the tree shows that the function returns an object with the property hp: 100.

    Tokens → program tree
    AST → bytecode generator
    • Function spawnEnemy
      • Function body
        • Return return
          • Object
            • Property hp
              • Number 100
  3. The bytecode generator creates instructions

    Walks the AST and writes operations for the interpreter. In this example, their meaning is: create an object, then return it.

    AST → bytecode
    Bytecode → Ignition
    CREATE_OBJECT { hp: 100 }Create an objectRETURNReturn it

    Teaching pseudobytecode: the instruction names are illustrative. The object will be created when the function is called.

  4. Ignition executes the bytecode

    Reads the instructions and performs the corresponding operations. The CPU executes the machine code of the interpreter itself.

    Bytecode → operations
    Result of spawnEnemy() { hp: 100 }

    Ignition executed the instructions: the object was created and returned from the function.

V8 can also use a JIT compiler to generate machine code for the function. The CPU then executes that version directly. Both execution paths must produce the same program result.

What happens to the call and the object

When the program executes const enemy = spawnEnemy(), the following happens:

  1. The call begins. The engine saves the call’s data on the stack: which function is running and where to return afterwards.
  2. The object is created. V8 allocates heap memory for it and stores hp: 100.
  3. The function returns its result. The call finishes, and the variable enemy receives a reference to the new object.
Call stackspawnEnemy() has finished

The program continues after the call.

Heap · object memoryenemy → { hp: 100 }

The object is accessible through the variable's reference.

If the object is also stored in an enemies array, it remains accessible through that array. Once no path from the roots leads to it, the garbage collector can release its memory.

Key takeaway

The scanner recognizes tokens, the parser builds an AST, the generator creates bytecode, and Ignition executes it. V8 can also generate machine code for a function. During execution, the engine maintains the call stack and manages object memory.

Check the primary source ↗

V8 · Scanner↗The scanner recognizes tokens, which the parser uses to build an AST.V8 · Generating bytecode from an AST↗The generator takes the parser's AST and produces a sequence of instructions for Ignition.V8 · Ignition↗The bytecode interpreter within V8.V8 · Sparkplug↗Generating machine code from bytecode and storing function-call data on the stack.V8 · Memory management↗Storing objects on the heap and finding accessible objects through references.