1# Adding V8 Fast API 2 3Node.js uses [V8](https://v8.dev/) as its JavaScript engine. 4Embedding functions implemented in C++ incur a high overhead, so V8 5provides an API to implement native functions which may be invoked directly 6from JIT-ed code. These functions also come with additional constraints, 7for 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 38Fast API supports fallback to slow path for when it is desirable to do so, 39for example, when throwing a custom error or executing JavaScript code is 40needed. The fallback mechanism can be enabled and changed from the C++ 41implementation of the fast API function declaration. 42 43Passing `true` to the `fallback` option will force V8 to run the slow path 44with the same arguments. 45 46In 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 69A 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 ``` 151