@@ -26,15 +26,8 @@ for example, they may not trigger garbage collection. |
26 | 26 | in a snapshot (either the built-in or a user-land one). Please refer to the |
27 | 27 | [binding functions documentation](../../src/README.md#binding-functions) for more |
28 | 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 | | -* 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. |
| 29 | +* Fast API functions must be tested following the example in |
| 30 | + [Test with Fast API path](#test-with-fast-api-path). |
38 | 31 | * The fast callback must be idempotent up to the point where error and fallback |
39 | 32 | conditions are checked, because otherwise executing the slow callback might |
40 | 33 | produce visible side effects twice. |
@@ -175,40 +168,47 @@ A typical function that communicates between JavaScript and C++ is as follows. |
175 | 168 | v8::FastApiCallbackOptions& options); |
176 | 169 | ``` |
177 | 170 |
|
178 | | -* In the unit tests: |
179 | | - |
180 | | - Since the fast API function uses `TRACK_V8_FAST_API_CALL`, we can ensure that |
181 | | - the fast paths are taken and test them by writing tests that force |
182 | | - V8 optimizations and check the counters. |
183 | | - |
184 | | - ```js |
185 | | - // Flags: --expose-internals --no-warnings --allow-natives-syntax |
186 | | - 'use strict'; |
187 | | - const common = require('../common'); |
188 | | - |
189 | | - const { internalBinding } = require('internal/test/binding'); |
190 | | - // We could also require a function that uses the internal binding internally. |
191 | | - const { divide } = internalBinding('custom_namespace'); |
192 | | - |
193 | | - // The function that will be optimized. It has to be a function written in |
194 | | - // JavaScript. Since `divide` comes from the C++ side, we need to wrap it. |
195 | | - function testFastPath(a, b) { |
196 | | - return divide(a, b); |
197 | | - } |
198 | | - |
199 | | - eval('%PrepareFunctionForOptimization(testFastPath)'); |
200 | | - // This call will let V8 know about the argument types that the function expects. |
201 | | - assert.strictEqual(testFastPath(6, 3), 2); |
202 | | - |
203 | | - eval('%OptimizeFunctionOnNextCall(testFastPath)'); |
204 | | - assert.strictEqual(testFastPath(8, 2), 4); |
205 | | - assert.throws(() => testFastPath(1, 0), { |
206 | | - code: 'ERR_INVALID_STATE', |
207 | | - }); |
208 | | - |
209 | | - if (common.isDebug) { |
210 | | - const { getV8FastApiCallCount } = internalBinding('debug'); |
211 | | - assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.ok'), 1); |
212 | | - assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.error'), 1); |
213 | | - } |
214 | | - ``` |
| 171 | +### Test with Fast API path |
| 172 | + |
| 173 | +In debug mode (`./configure --debug` or `./configure --debug-node` flags), the |
| 174 | +fast API calls can be tracked using the `TRACK_V8_FAST_API_CALL("key")` macro. |
| 175 | +This can be used to count how many times fast paths are taken during tests. The |
| 176 | +key is a global identifier and should be unique across the codebase. |
| 177 | +Use `"binding_name.function_name"` or `"binding_name.function_name.suffix"` to |
| 178 | +ensure uniqueness. |
| 179 | + |
| 180 | +In the unit tests, since the fast API function uses `TRACK_V8_FAST_API_CALL`, |
| 181 | +we can ensure that the fast paths are taken and test them by writing tests that |
| 182 | +force V8 optimizations and check the counters. |
| 183 | + |
| 184 | +```js |
| 185 | +// Flags: --expose-internals --no-warnings --allow-natives-syntax |
| 186 | +'use strict'; |
| 187 | +const common = require('../common'); |
| 188 | + |
| 189 | +const { internalBinding } = require('internal/test/binding'); |
| 190 | +// We could also require a function that uses the internal binding internally. |
| 191 | +const { divide } = internalBinding('custom_namespace'); |
| 192 | + |
| 193 | +// The function that will be optimized. It has to be a function written in |
| 194 | +// JavaScript. Since `divide` comes from the C++ side, we need to wrap it. |
| 195 | +function testFastPath(a, b) { |
| 196 | + return divide(a, b); |
| 197 | +} |
| 198 | + |
| 199 | +eval('%PrepareFunctionForOptimization(testFastPath)'); |
| 200 | +// This call will let V8 know about the argument types that the function expects. |
| 201 | +assert.strictEqual(testFastPath(6, 3), 2); |
| 202 | + |
| 203 | +eval('%OptimizeFunctionOnNextCall(testFastPath)'); |
| 204 | +assert.strictEqual(testFastPath(8, 2), 4); |
| 205 | +assert.throws(() => testFastPath(1, 0), { |
| 206 | + code: 'ERR_INVALID_STATE', |
| 207 | +}); |
| 208 | + |
| 209 | +if (common.isDebug) { |
| 210 | + const { getV8FastApiCallCount } = internalBinding('debug'); |
| 211 | + assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.ok'), 1); |
| 212 | + assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.error'), 1); |
| 213 | +} |
| 214 | +``` |