@@ -29,6 +29,12 @@ for example, they may not trigger garbage collection.
2929* To test fast APIs, make sure to run the tests in a loop with a decent
3030 iterations count to trigger relevant optimizations that prefer the fast API
3131 over the slow one.
32+ * In debug mode (` --debug ` or ` --debug-node ` flags), the fast API calls can be
33+ tracked using the ` TRACK_V8_FAST_API_CALL("key") ` macro. This can be used to
34+ count how many times fast paths are taken during tests. The key is a global
35+ identifier and should be unique across the codebase.
36+ Use ` "binding_name.function_name" ` or ` "binding_name.function_name.suffix" ` to
37+ ensure uniqueness.
3238* The fast callback must be idempotent up to the point where error and fallback
3339 conditions are checked, because otherwise executing the slow callback might
3440 produce visible side effects twice.
@@ -77,6 +83,7 @@ A typical function that communicates between JavaScript and C++ is as follows.
7783* On the C++ side:
7884
7985 ``` cpp
86+ #include " node_debug.h"
8087 #include " v8-fast-api-calls.h"
8188
8289 namespace node {
@@ -102,9 +109,11 @@ A typical function that communicates between JavaScript and C++ is as follows.
102109 const int32_t b,
103110 v8::FastApiCallbackOptions& options) {
104111 if (b == 0) {
112+ TRACK_V8_FAST_API_CALL("custom_namespace.divide.error");
105113 options.fallback = true;
106114 return 0;
107115 } else {
116+ TRACK_V8_FAST_API_CALL("custom_namespace.divide.ok");
108117 return a / b;
109118 }
110119 }
@@ -148,3 +157,42 @@ A typical function that communicates between JavaScript and C++ is as follows.
148157 const int32_t b,
149158 v8::FastApiCallbackOptions& options);
150159 ```
160+
161+ * In the unit tests:
162+
163+ Since the fast API function uses ` TRACK_V8_FAST_API_CALL ` , we can ensure that
164+ the fast paths are taken and test them by writing tests that force
165+ V8 optimizations and check the counters.
166+
167+ ``` js
168+ // Flags: --expose-internals --no-warnings --allow-natives-syntax
169+ ' use strict' ;
170+ const common = require (' ../common' );
171+
172+ const { internalBinding } = require (' internal/test/binding' );
173+ // We could also require a function that uses the internal binding internally.
174+ const { divide } = internalBinding (' custom_namespace' );
175+
176+ if (common .isDebug ) {
177+ const { getV8FastApiCallCount } = internalBinding (' debug' );
178+
179+ // The function that will be optimized. It has to be a function written in
180+ // JavaScript. Since `divide` comes from the C++ side, we need to wrap it.
181+ function testFastPath (a , b ) {
182+ return divide (a, b);
183+ }
184+
185+ eval (' %PrepareFunctionForOptimization(testFastPath)' );
186+ // This call will let V8 know about the argument types that the function expects.
187+ assert .strictEqual (testFastPath (6 , 3 ), 2 );
188+
189+ eval (' %OptimizeFunctionOnNextCall(testFastPath)' );
190+ assert .strictEqual (testFastPath (8 , 2 ), 4 );
191+ assert .throws (() => testFastPath (1 , 0 ), {
192+ code: ' ERR_INVALID_STATE' ,
193+ });
194+
195+ assert .strictEqual (getV8FastApiCallCount (' custom_namespace.divide.ok' ), 1 );
196+ assert .strictEqual (getV8FastApiCallCount (' custom_namespace.divide.error' ), 1 );
197+ }
198+ ```
0 commit comments