Skip to content

Commit 90c81b8

Browse files
committed
doc: add v8 fast api contribution guidelines
1 parent f5dc92c commit 90c81b8

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Adding V8 Fast API
2+
3+
Node.js uses [V8](https://github.com/v8/v8) as the JavaScript engine.
4+
In order to provide fast paths for functions that are called quite often,
5+
V8 proposes the usage of `fast api calls` that does not use any of the V8
6+
internals, but uses internal C functions.
7+
8+
## Limitations
9+
10+
* Fast API functions can not use JavaScript heap allocation.
11+
* Fast API functions can not trigger JavaScript execution.
12+
* Reporting/returning errors is not available on fast API, but can be done
13+
through fallbacks to slow API.
14+
* Not all parameter and return types are supported in fast API calls.
15+
For a full list, please look into
16+
[`v8-fast-api-calls.h`](../../deps/v8/include/v8-fast-api-calls.h) file.
17+
18+
## Requirements
19+
20+
* Each unique fast API function signature should be defined inside the
21+
[`external references`](../../src/node_external_reference.h) file.
22+
* To test fast APIs, make sure to run the tests in a loop with a decent
23+
iterations count to trigger V8 for optimization and to prefer the fast API
24+
over slow one.
25+
26+
## Fallback to slow path
27+
28+
Fast API supports fallback to slow path (implementation that uses V8 internals)
29+
in case logically it is wise to do so, for example when providing a more
30+
detailed error. Fallback mechanism can be enabled, and changed from both caller
31+
JavaScript function or from the fast API function declaration.
32+
33+
Every fast API function accessible from JavaScript side can pass an object
34+
consisting of fallback key with a boolean value as the last parameter
35+
(or the first parameter, if no parameters of the function exist).
36+
37+
In V8 the options fallback is defined as `FastApiCallbackOptions` inside
38+
[`v8-fast-api-calls.h`](../../deps/v8/include/v8-fast-api-calls.h) file.
39+
40+
* JavaScript land
41+
42+
Example of a JavaScript call into a fast API:
43+
44+
```js
45+
// Let divide be a function that provides fast api calls.
46+
const { divide } = internalBinding('custom_namespace');
47+
48+
function divide(a, b) {
49+
return divide(a, b, { fallback: a === 0 })
50+
}
51+
```
52+
53+
* C++ land
54+
55+
Example of a conditional fast path on C++
56+
57+
```cpp
58+
// Anywhere in the execution flow, you can set fallback and stop the execution.
59+
static double divide(const int32_t a,
60+
const int32_t b,
61+
v8::FastApiCallbackOptions& options) {
62+
if (b == 0) {
63+
options.fallback = true;
64+
} else {
65+
return a / b;
66+
}
67+
}
68+
```
69+
70+
## Example
71+
72+
A typical function that communicates between JavaScript and C++ is as follows.
73+
74+
* On the JavaScript side:
75+
76+
```js
77+
const { divide } = internalBinding('custom_namespace');
78+
```
79+
80+
* On the C++ side:
81+
82+
```cpp
83+
#include "v8-fast-api-calls.h"
84+
85+
namespace node {
86+
namespace custom_namespace {
87+
88+
static void divide(const FunctionCallbackInfo<Value>& args) {
89+
Environment* env = Environment::GetCurrent(args);
90+
CHECK(args[0]->IsInt32());
91+
CHECK(args[1]->IsInt32());
92+
auto a = args[0].As<v8::Int32>();
93+
auto b = args[1].As<v8::Int32>();
94+
95+
if (b->Value() == 0) {
96+
return node::THROW_ERR_INVALID_STATE(env, "Error");
97+
}
98+
99+
double result = a->Value() / b->Value();
100+
args.GetReturnValue().Set(result);
101+
}
102+
103+
static double FastDivide(const int32_t a,
104+
const int32_t b,
105+
v8::FastApiCallbackOptions& options) {
106+
if (b == 0) {
107+
options.fallback = true;
108+
} else {
109+
return a / b;
110+
}
111+
}
112+
113+
CFunction fast_divide_(CFunction::Make(FastDivide));
114+
115+
static void Initialize(Local<Object> target,
116+
Local<Value> unused,
117+
Local<Context> context,
118+
void* priv) {
119+
SetFastMethod(context, target, "divide", Divide, &fast_divide_);
120+
}
121+
122+
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
123+
registry->Register(Divide);
124+
registry->Register(FastDivide);
125+
registry->Register(fast_divide_.GetTypeInfo());
126+
}
127+
128+
} // namespace custom_namespace
129+
} // namespace node
130+
131+
NODE_BINDING_CONTEXT_AWARE_INTERNAL(custom_namespace,
132+
node::custom_namespace::Initialize);
133+
NODE_BINDING_EXTERNAL_REFERENCE(
134+
custom_namespace,
135+
node::custom_namespace::RegisterExternalReferences);
136+
```
137+
138+
* Update external references ([`node_external_reference.h`](../../src/node_external_reference.h))
139+
140+
Since our implementation used
141+
`double(const int32_t a, const int32_t b, v8::FastApiCallbackOptions& options)`
142+
signature, we need to add it to external references.
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+
```

0 commit comments

Comments
 (0)