# Node.js C++ codebase
Hi! đź‘‹ You've found the C++ code backing Node.js. This README aims to help you
get started working on it and document some idioms you may encounter while
doing so.
## Coding style
Node.js has a document detailing its [C++ coding style][]
that can be helpful as a reference for stylistic issues.
## V8 API documentation
A lot of the Node.js codebase is around what the underlying JavaScript engine,
V8, provides through its API for embedders. Knowledge of this API can also be
useful when working with native addons for Node.js written in C++, although for
new projects [N-API][] is typically the better alternative.
V8 does not provide much public API documentation beyond what is
available in its C++ header files, most importantly `v8.h`, which can be
accessed online in the following locations:
* On GitHub: [`v8.h` in Node.js master][]
* On GitHub: [`v8.h` in V8 master][]
* On the Chromium project's Code Search application: [`v8.h` in Code Search][]
V8 also provides an [introduction for V8 embedders][],
which can be useful for understanding some of the concepts it uses in its
embedder API.
Important concepts when using V8 are the ones of [`Isolate`][]s and
[JavaScript value handles][].
## libuv API documentation
The other major dependency of Node.js is [libuv][], providing
the [event loop][] and other operation system abstractions to Node.js.
There is a [reference documentation for the libuv API][].
## File structure
The Node.js C++ files follow this structure:
The `.h` header files contain declarations, and sometimes definitions that don't
require including other headers (e.g. getters, setters, etc.). They should only
include other `.h` header files and nothing else.
The `-inl.h` header files contain definitions of inline functions from the
corresponding `.h` header file (e.g. functions marked `inline` in the
declaration or `template` functions). They always include the corresponding
`.h` header file, and can include other `.h` and `-inl.h` header files as
needed. It is not mandatory to split out the definitions from the `.h` file
into an `-inl.h` file, but it becomes necessary when there are multiple
definitions and contents of other `-inl.h` files start being used. Therefore, it
is recommended to split a `-inl.h` file when inline functions become longer than
a few lines to keep the corresponding `.h` file readable and clean. All visible
definitions from the `-inl.h` file should be declared in the corresponding `.h`
header file.
The `.cc` files contain definitions of non-inline functions from the
corresponding `.h` header file. They always include the corresponding `.h`
header file, and can include other `.h` and `-inl.h` header files as needed.
## Helpful concepts
A number of concepts are involved in putting together Node.js on top of V8 and
libuv. This section aims to explain some of them and how they work together.
### `Isolate`
The `v8::Isolate` class represents a single JavaScript engine instance, in
particular a set of JavaScript objects that can refer to each other
(the “heap”).
The `v8::Isolate` is often passed to other V8 API functions, and provides some
APIs for managing the behaviour of the JavaScript engine or querying about its
current state or statistics such as memory usage.
V8 APIs are not thread-safe unless explicitly specified. In a typical Node.js
application, the main thread and any `Worker` threads each have one `Isolate`,
and JavaScript objects from one `Isolate` cannot refer to objects from
another `Isolate`.
Garbage collection, as well as other operations that affect the entire heap,
happen on a per-`Isolate` basis.
Typical ways of accessing the current `Isolate` in the Node.js code are:
* Given a `FunctionCallbackInfo` for a [binding function][],
using `args.GetIsolate()`.
* Given a [`Context`][], using `context->GetIsolate()`.
* Given a [`Environment`][], using `env->isolate()`.
### V8 JavaScript values
V8 provides classes that mostly correspond to JavaScript types; for example,
`v8::Value` is a class representing any kind of JavaScript type, with
subclasses such as `v8::Number` (which in turn has subclasses like `v8::Int32`),
`v8::Boolean` or `v8::Object`. Most types are represented by subclasses
of `v8::Object`, e.g. `v8::Uint8Array` or `v8::Date`.
### Internal fields
V8 provides the ability to store data in so-called “internal fields” inside
`v8::Object`s that were created as instances of C++-backed classes. The number
of fields needs to be defined when creating that class.
Both JavaScript values and `void*` pointers may be stored in such fields.
In most native Node.js objects, the first internal field is used to store a
pointer to a [`BaseObject`][] subclass, which then contains all relevant
information associated with the JavaScript object.
Typical ways of working with internal fields are:
* `obj->InternalFieldCount()` to look up the number of internal fields for an
object (`0` for regular JavaScript objects).
* `obj->GetInternalField(i)` to get a JavaScript value from an internal field.
* `obj->SetInternalField(i, v)` to store a JavaScript value in an
internal field.
* `obj->GetAlignedPointerFromInternalField(i)` to get a `void*` pointer from an
internal field.
* `obj->SetAlignedPointerInInternalField(i, p)` to store a `void*` pointer in an
internal field.
[`Context`][]s provide the same feature under the name “embedder data”.
### JavaScript value handles
All JavaScript values are accessed through the V8 API through so-called handles,
of which there are two types: [`Local`][]s and [`Global`][]s.
#### `Local` handles
A `v8::Local` handle is a temporary pointer to a JavaScript object, where
“temporary” usually means that is no longer needed after the current function
is done executing. `Local` handles can only be allocated on the C++ stack.
Most of the V8 API uses `Local` handles to work with JavaScript values or return
them from functions.
Whenever a `Local` handle is created, a `v8::HandleScope` or
`v8::EscapableHandleScope` object must exist on the stack. The `Local` is then
added to that scope and deleted along with it.
When inside a [binding function][], a `HandleScope` already exists outside of
it, so there is no need to explicitly create one.
`EscapableHandleScope`s can be used to allow a single `Local` handle to be
passed to the outer scope. This is useful when a function returns a `Local`.
The following JavaScript and C++ functions are mostly equivalent:
```js
function getFoo(obj) {
return obj.foo;
}
```
```cpp
v8::Local GetFoo(v8::Local context,
v8::Local obj) {
v8::Isolate* isolate = context->GetIsolate();
v8::EscapableHandleScope handle_scope(isolate);
// The 'foo_string' handle cannot be returned from this function because
// it is not “escaped” with `.Escape()`.
v8::Local foo_string =
v8::String::NewFromUtf8(isolate, "foo").ToLocalChecked();
v8::Local return_value;
if (obj->Get(context, foo_string).ToLocal(&return_value)) {
return handle_scope.Escape(return_value);
} else {
// There was a JS exception! Handle it somehow.
return v8::Local();
}
}
```
See [exception handling][] for more information about the usage of `.To()`,
`.ToLocalChecked()`, `v8::Maybe` and `v8::MaybeLocal` usage.
##### Casting local handles
If it is known that a `Local` refers to a more specific type, it can
be cast to that type using `.As<...>()`:
```cpp
v8::Local some_value;
// CHECK() is a Node.js utilitity that works similar to assert().
CHECK(some_value->IsUint8Array());
v8::Local as_uint8 = some_value.As();
```
Generally, using `val.As()` is only valid if `val->IsX()` is true, and
failing to follow that rule may lead to crashes.
##### Detecting handle leaks
If it is expected that no `Local` handles should be created within a given
scope unless explicitly within a `HandleScope`, a `SealHandleScope` can be used.
For example, there is a `SealHandleScope` around the event loop, forcing
any functions that are called from the event loop and want to run or access
JavaScript code to create `HandleScope`s.
#### `Global` handles
A `v8::Global` handle (sometimes also referred to by the name of its parent
class `Persistent`, although use of that is discouraged in Node.js) is a
reference to a JavaScript object that can remain active as long as the engine
instance is active.
Global handles can be either strong or weak. Strong global handles are so-called
“GC roots”, meaning that they will keep the JavaScript object they refer to
alive even if no other objects refer to them. Weak global handles do not do
that, and instead optionally call a callback when the object they refer to
is garbage-collected.
```cpp
v8::Global reference;
void StoreReference(v8::Isolate* isolate, v8::Local obj) {
// Create a strong reference to `obj`.
reference.Reset(isolate, obj);
}
// Must be called with a HandleScope around it.
v8::Local LoadReference(v8::Isolate* isolate) {
return reference.Get(isolate);
}
```
##### `Eternal` handles
`v8::Eternal` handles are a special kind of handles similar to `v8::Global`
handles, with the exception that the values they point to are never
garbage-collected while the JavaScript Engine instance is alive, even if
the `v8::Eternal` itself is destroyed at some point. This type of handle
is rarely used.
### `Context`
JavaScript allows multiple global objects and sets of built-in JavaScript
objects (like the `Object` or `Array` functions) to coexist inside the same
heap. Node.js exposes this ability through the [`vm` module][].
V8 refers to each of these global objects and their associated builtins as a
`Context`.
Currently, in Node.js there is one main `Context` associated with an
[`Environment`][] instance, and most Node.js features will only work inside
that context. (The only exception at the time of writing are
[`MessagePort`][] objects.) This restriction is not inherent to the design of
Node.js, and a sufficiently committed person could restructure Node.js to
provide built-in modules inside of `vm.Context`s.
Often, the `Context` is passed around for [exception handling][].
Typical ways of accessing the current `Context` in the Node.js code are:
* Given an [`Isolate`][], using `isolate->GetCurrentContext()`.
* Given an [`Environment`][], using `env->context()` to get the `Environment`'s
main context.
### Event loop
The main abstraction for an event loop inside Node.js is the `uv_loop_t` struct.
Typically, there is one event loop per thread. This includes not only the main
thread and Workers, but also helper threads that may occasionally be spawned
in the course of running a Node.js program.
The current event loop can be accessed using `env->event_loop()` given an
[`Environment`][] instance. The restriction of using a single event loop
is not inherent to the design of Node.js, and a sufficiently committed person
could restructure Node.js to provide e.g. the ability to run parts of Node.js
inside an event loop separate from the active thread's event loop.
### `Environment`
Node.js instances are represented by the `Environment` class.
Currently, every `Environment` class is associated with:
* One [event loop][]
* One [`Isolate`][]
* One main [`Context`][]
The `Environment` class contains a large number of different fields for
different Node.js modules, for example a libuv timer for `setTimeout()` or
the memory for a `Float64Array` that the `fs` module uses for storing data
returned from a `fs.stat()` call.
It also provides [cleanup hooks][] and maintains a list of [`BaseObject`][]
instances.
Typical ways of accessing the current `Environment` in the Node.js code are:
* Given a `FunctionCallbackInfo` for a [binding function][],
using `Environment::GetCurrent(args)`.
* Given a [`BaseObject`][], using `env()` or `self->env()`.
* Given a [`Context`][], using `Environment::GetCurrent(context)`.
This requires that `context` has been associated with the `Environment`
instance, e.g. is the main `Context` for the `Environment` or one of its
`vm.Context`s.
* Given an [`Isolate`][], using `Environment::GetCurrent(isolate)`. This looks
up the current [`Context`][] and then uses that.
### `IsolateData`
Every Node.js instance ([`Environment`][]) is associated with one `IsolateData`
instance that contains information about or associated with a given
[`Isolate`][].
#### String table
`IsolateData` contains a list of strings that can be quickly accessed
inside Node.js code, e.g. given an `Environment` instance `env` the JavaScript
string “name” can be accessed through `env->name_string()` without actually
creating a new JavaScript string.
### Platform
Every process that uses V8 has a `v8::Platform` instance that provides some
functionalities to V8, most importantly the ability to schedule work on
background threads.
Node.js provides a `NodePlatform` class that implements the `v8::Platform`
interface and uses libuv for providing background threading abilities.
The platform can be accessed through `isolate_data->platform()` given an
[`IsolateData`][] instance, although that only works when:
* The current Node.js instance was not started by an embedder; or
* The current Node.js instance was started by an embedder whose `v8::Platform`
implementation also implement's the `node::MultiIsolatePlatform` interface
and who passed this to Node.js.
### Binding functions
C++ functions exposed to JS follow a specific signature. The following example
is from `node_util.cc`:
```cpp
void ArrayBufferViewHasBuffer(const FunctionCallbackInfo& args) {
CHECK(args[0]->IsArrayBufferView());
args.GetReturnValue().Set(args[0].As()->HasBuffer());
}
```
(Namespaces are usually omitted through the use of `using` statements in the
Node.js source code.)
`args[n]` is a `Local` that represents the n-th argument passed to the
function. `args.This()` is the `this` value inside this function call.
`args.Holder()` is equivalent to `args.This()` in all use cases inside of
Node.js.
`args.GetReturnValue()` is a placeholder for the return value of the function,
and provides a `.Set()` method that can be called with a boolean, integer,
floating-point number or a `Local` to set the return value.
Node.js provides various helpers for building JS classes in C++ and/or attaching
C++ functions to the exports of a built-in module:
```cpp
void Initialize(Local