Start an attempt at documenting V8's internals, from high-level concepts to low-level implementation details. These docs are intended to be readable to both humans and AI agents, with the hope that documentation like this can help both humans and AIs maintain a concept of the overall design and design constraints when making changes. It merges in the existing documentation we had along with a bunch of AI-assisted extra docs. Includes a web viewer that renders the markdown and mermaid diagrams. The viewer supports commenting, where comments are saved in docs/viewer/comments.json, and can be manually or agentically resolved. #ai-assisted Bug: 500280354 Change-Id: Ie9ffc2f2f85de1496b8266027ca8b5b455acdb63 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7761899 Auto-Submit: Leszek Swirski <leszeks@chromium.org> Commit-Queue: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/main@{#106585}
105 lines
3.7 KiB
Markdown
105 lines
3.7 KiB
Markdown
# JavaScript Overview for V8 Developers
|
|
|
|
This document provides a high-level overview of how JavaScript works, focusing on concepts that are relevant for understanding V8's implementation.
|
|
|
|
## Primitives vs. Objects
|
|
|
|
JavaScript has two main categories of values:
|
|
|
|
### 1. Primitives
|
|
Immutable values that are passed by value.
|
|
* `undefined`, `null`, `boolean` (`true`/`false`), `number`, `string`, `bigint`, `symbol`. (Spec names are capitalized, but JS `typeof` returns lowercase strings).
|
|
* In V8, small integers (Smis) are stored directly in the pointer word, while other primitives are allocated on the heap (e.g., `HeapNumber`, `String`).
|
|
|
|
### 2. Objects
|
|
Mutable collections of properties, passed by reference.
|
|
* Any value that is not a primitive is an object (including arrays and functions).
|
|
* In V8, objects are represented by `JSObject` and its subclasses.
|
|
* **Note**: JS also has primitive wrapper objects (e.g., `new String('foo')`), which are actual objects and not primitives. Be careful not to confuse them with the primitive values!
|
|
|
|
## How V8 implements prototypes
|
|
|
|
JavaScript uses a **prototypal inheritance** model. Every object has a hidden link to another object called its **prototype** (internal `[[Prototype]]`).
|
|
|
|
### Prototype Chain
|
|
* When you access a property on an object, JavaScript first looks for it on the object itself.
|
|
* If not found, it looks at the object's prototype, and then the prototype's prototype, forming a **prototype chain**.
|
|
* The chain ends when it reaches `null` (typically `Object.prototype.__proto__` is `null`).
|
|
|
|
### `Foo.prototype` vs `foo.__proto__`
|
|
* `Foo.prototype` is a property of the **constructor function** `Foo`. It is the object that will be used as the prototype for instances created with `new Foo()`.
|
|
* `foo.__proto__` is a property of the **instance** `foo`. It points to the prototype object that `foo` inherited from.
|
|
* If `foo = new Foo()`, then `foo.__proto__ === Foo.prototype`.
|
|
|
|
```dot
|
|
digraph G {
|
|
rankdir=TB;
|
|
node [shape=record];
|
|
edge [];
|
|
|
|
Foo [label="Constructor: Foo"];
|
|
Foo_Proto [label="Foo.prototype"];
|
|
foo [label="Instance: foo"];
|
|
Obj_Proto [label="Object.prototype"];
|
|
null_node [label="null"];
|
|
|
|
Foo -> Foo_Proto [label=".prototype"];
|
|
Foo -> foo [label="new Foo", style=dashed];
|
|
foo -> Foo_Proto [label="[[Prototype]]"];
|
|
Foo_Proto -> Obj_Proto [label="[[Prototype]]"];
|
|
Obj_Proto -> null_node [label="[[Prototype]]"];
|
|
}
|
|
```
|
|
|
|
### `__proto__` and V8 Implementation
|
|
* `__proto__` is an accessor property (getter/setter) on `Object.prototype` that exposes the internal prototype link.
|
|
* Modern JavaScript recommends using `Object.getPrototypeOf()` and `Object.setPrototypeOf()` instead of `__proto__`.
|
|
* In V8, the prototype is stored in the `Map` of the object. Changing the prototype of an object changes its map!
|
|
|
|
## ES6 Classes
|
|
|
|
ES6 classes are primarily **syntactic sugar** over JavaScript's existing prototype-based inheritance.
|
|
|
|
```javascript
|
|
class Animal {
|
|
constructor(name) {
|
|
this.name = name;
|
|
}
|
|
speak() {
|
|
console.log(`${this.name} makes a noise.`);
|
|
}
|
|
}
|
|
|
|
class Dog extends Animal {
|
|
speak() {
|
|
console.log(`${this.name} barks.`);
|
|
}
|
|
}
|
|
```
|
|
|
|
Under the hood, this is equivalent to:
|
|
|
|
```javascript
|
|
function Animal(name) {
|
|
this.name = name;
|
|
}
|
|
Animal.prototype.speak = function() {
|
|
console.log(this.name + ' makes a noise.');
|
|
};
|
|
|
|
function Dog(name) {
|
|
Animal.call(this, name);
|
|
}
|
|
Dog.prototype = Object.create(Animal.prototype);
|
|
Dog.prototype.constructor = Dog;
|
|
Dog.prototype.speak = function() {
|
|
console.log(this.name + ' barks.');
|
|
};
|
|
```
|
|
|
|
|
|
|
|
## File Structure
|
|
* `src/objects/js-objects.h`: Definitions for JavaScript objects.
|
|
* `src/objects/map.h`: Definitions for Maps (hidden classes) which store prototype links.
|