docs/runtime/scopes-and-scope-infos.md
V8 manages variable scopes at both compile time and runtime using different data structures. This document explains how Scope and ScopeInfo work together.
ScopeDuring parsing and AST generation, V8 uses the Scope class (defined in src/ast/scopes.h) to represent lexical scopes.
Context on the heap (slow, needed for closures).V8 supports various scope types defined in src/common/globals.h:
SCRIPT_SCOPE: The top-level scope for a script or a top-level eval.REPL_MODE_SCOPE: The top-level scope for a repl-mode script.CLASS_SCOPE: The scope introduced by a class.EVAL_SCOPE: The top-level scope for an eval source.FUNCTION_SCOPE: The top-level scope for a function.MODULE_SCOPE: The scope introduced by a module literal.CATCH_SCOPE: The scope introduced by catch.BLOCK_SCOPE: The scope introduced by a new block.WITH_SCOPE: The scope introduced by with.SHADOW_REALM_SCOPE: Synthetic scope for ShadowRealm NativeContexts.ScopeInfoWhen V8 compiles a function, it serializes the compile-time Scope information into a ScopeInfo object (defined in src/objects/scope-info.h).
ScopeInfo is a compressed heap object that retains the necessary information about a scope for execution and debugging.
eval).ScopeInfo of the enclosing scope, forming a chain.ContextWhile ScopeInfo is read-only metadata, the Context is the actual runtime object (a variable-sized heap object, conceptually similar to a FixedArray) that holds the values of context-allocated variables.
ScopeInfo that requires a context will have a corresponding Context object created at runtime when the scope is entered.previous pointer), mirroring the ScopeInfo chain.Similar to scope types, V8 has different context types (see src/objects/contexts.h):
let, const).catch blocks to hold the exception object.with statements.eval executions.To understand how ScopeInfo and Context work in practice, consider a closure that captures a variable:
function makeCounter() {
let count = 0;
return function() {
return ++count;
};
}
const counter = makeCounter();
counter(); // Returns 1
counter(); // Returns 2
count, which is declared in the outer makeCounter function. The compiler decides that count cannot be stack-allocated and must be context-allocated.ScopeInfo for makeCounter will record that it needs a context with at least one slot for count.makeCounter() is called, V8 creates a new FunctionContext for it.count variable is stored in a slot in this context, initialized to 0.JSFunction object. This object contains a reference to the current Context (the makeCounter context).makeCounter returns, its stack frame is destroyed, but the FunctionContext lives on in the heap because the inner function holds a reference to it.counter() is called, V8 sets the current context to the one referenced by the closure. The ++count operation reads and writes directly to the slot in that heap context.Scope objects.Scope to allocate variables and generates ScopeInfo.ScopeInfo to create Context objects and access variables.