Skip to content

Commit d153a93

Browse files
anonrigjuanarbol
authored andcommitted
doc: add v8 fast api contribution guidelines
PR-URL: #46199 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent b06298c commit d153a93

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed
+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Adding V8 Fast API
2+
3+
Node.js uses [V8](https://v8.dev/) as its JavaScript engine.
4+
Embedding functions implemented in C++ incur a high overhead, so V8
5+
provides an API to implement native functions which may be invoked directly
6+
from JITted code. These functions also come with additional constraints,
7+
for example, they may not trigger garbage collection.
8+
9+
## Limitations
10+
11+
* Fast API functions may not trigger garbage collection. This means by proxy
12+
that JavaScript execution and heap allocation are also forbidden, including
13+
`v8::Array::Get()` or `v8::Number::New()`.
14+
* Throwing errors is not available from within a fast API call, but can be done
15+
through the fallback to the slow API.
16+
* Not all parameter and return types are supported in fast API calls.
17+
For a full list, please look into
18+
[`v8-fast-api-calls.h`](../../deps/v8/include/v8-fast-api-calls.h).
19+
20+
## Requirements
21+
22+
* Any function passed to `CFunction::Make`, including fast API function
23+
declarations, should have their signature registered in
24+
[`node_external_reference.h`](../../src/node_external_reference.h) file.
25+
Although, it would not start failing or crashing until the function ends up
26+
in a snapshot (either the built-in or a user-land one). Please refer to the
27+
[binding functions documentation](../../src#binding-functions) for more
28+
information.
29+
* To test fast APIs, make sure to run the tests in a loop with a decent
30+
iterations count to trigger relevant optimizations that prefer the fast API
31+
over the slow one.
32+
* The fast callback must be idempotent up to the point where error and fallback
33+
conditions are checked, because otherwise executing the slow callback might
34+
produce visible side effects twice.
35+
36+
## Fallback to slow path
37+
38+
Fast API supports fallback to slow path for when it is desirable to do so,
39+
for example, when throwing a custom error or executing JavaScript code is
40+
needed. The fallback mechanism can be enabled and changed from the C++
41+
implementation of the fast API function declaration.
42+
43+
Passing `true` to the `fallback` option will force V8 to run the slow path
44+
with the same arguments.
45+
46+
In V8, the options fallback is defined as `FastApiCallbackOptions` inside
47+
[`v8-fast-api-calls.h`](../../deps/v8/include/v8-fast-api-calls.h).
48+
49+
* C++ land
50+
51+
Example of a conditional fast path on C++
52+
53+
```cpp
54+
// Anywhere in the execution flow, you can set fallback and stop the execution.
55+
static double divide(const int32_t a,
56+
const int32_t b,
57+
v8::FastApiCallbackOptions& options) {
58+
if (b == 0) {
59+
options.fallback = true;
60+
return 0;
61+
} else {
62+
return a / b;
63+
}
64+
}
65+
```
66+
67+
## Example
68+
69+
A typical function that communicates between JavaScript and C++ is as follows.
70+
71+
* On the JavaScript side:
72+
73+
```js
74+
const { divide } = internalBinding('custom_namespace');
75+
```
76+
77+
* On the C++ side:
78+
79+
```cpp
80+
#include "v8-fast-api-calls.h"
81+
82+
namespace node {
83+
namespace custom_namespace {
84+
85+
static void SlowDivide(const FunctionCallbackInfo<Value>& args) {
86+
Environment* env = Environment::GetCurrent(args);
87+
CHECK_GE(args.Length(), 2);
88+
CHECK(args[0]->IsInt32());
89+
CHECK(args[1]->IsInt32());
90+
auto a = args[0].As<v8::Int32>();
91+
auto b = args[1].As<v8::Int32>();
92+
93+
if (b->Value() == 0) {
94+
return node::THROW_ERR_INVALID_STATE(env, "Error");
95+
}
96+
97+
double result = a->Value() / b->Value();
98+
args.GetReturnValue().Set(v8::Number::New(env->isolate(), result));
99+
}
100+
101+
static double FastDivide(const int32_t a,
102+
const int32_t b,
103+
v8::FastApiCallbackOptions& options) {
104+
if (b == 0) {
105+
options.fallback = true;
106+
return 0;
107+
} else {
108+
return a / b;
109+
}
110+
}
111+
112+
CFunction fast_divide_(CFunction::Make(FastDivide));
113+
114+
static void Initialize(Local<Object> target,
115+
Local<Value> unused,
116+
Local<Context> context,
117+
void* priv) {
118+
SetFastMethod(context, target, "divide", SlowDivide, &fast_divide_);
119+
}
120+
121+
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
122+
registry->Register(SlowDivide);
123+
registry->Register(FastDivide);
124+
registry->Register(fast_divide_.GetTypeInfo());
125+
}
126+
127+
} // namespace custom_namespace
128+
} // namespace node
129+
130+
NODE_BINDING_CONTEXT_AWARE_INTERNAL(custom_namespace,
131+
node::custom_namespace::Initialize);
132+
NODE_BINDING_EXTERNAL_REFERENCE(
133+
custom_namespace,
134+
node::custom_namespace::RegisterExternalReferences);
135+
```
136+
137+
* Update external references ([`node_external_reference.h`](../../src/node_external_reference.h))
138+
139+
Since our implementation used
140+
`double(const int32_t a, const int32_t b, v8::FastApiCallbackOptions& options)`
141+
signature, we need to add it to external references and in
142+
`ALLOWED_EXTERNAL_REFERENCE_TYPES`.
143+
144+
Example declaration:
145+
146+
```cpp
147+
using CFunctionCallbackReturningDouble = double (*)(const int32_t a,
148+
const int32_t b,
149+
v8::FastApiCallbackOptions& options);
150+
```

src/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ embedder API.
3131
Important concepts when using V8 are the ones of [`Isolate`][]s and
3232
[JavaScript value handles][].
3333

34+
V8 supports [fast API calls][], which can be useful for improving the
35+
performance in certain cases.
36+
3437
## libuv API documentation
3538

3639
The other major dependency of Node.js is [libuv][], providing
@@ -1055,6 +1058,7 @@ static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
10551058
[cleanup hooks]: #cleanup-hooks
10561059
[event loop]: #event-loop
10571060
[exception handling]: #exception-handling
1061+
[fast API calls]: ../doc/contributing/adding-v8-fast-api.md
10581062
[internal field]: #internal-fields
10591063
[introduction for V8 embedders]: https://v8.dev/docs/embed
10601064
[libuv]: https://libuv.org/

0 commit comments

Comments
 (0)