node/test/node-api/test_instance_data/test.js
Gabriel Schulhof 884e287199 n-api: free instance data as reference
Instance data associated with a `napi_env` is no longer stored on the
env itself but is instead rendered as a reference. Since
`v8impl::Reference` is tied to a JS object, this modification factors
out the `v8impl::Reference` refcounting and the deletion process into
a base class for `v8impl::Reference`, called `v8impl::RefBase`. The
instance data is then stored as a `v8impl::RefBase`, along with other
references, preventing a segfault that arises from the fact that, up
until now, upon `napi_env` destruction, the instance data was freed
after all references had already been forcefully freed. If the addon
freed a reference during the `napi_set_instance_data` finalizer
callback, such a reference had already been freed during environment
teardown, causing a double free.

Re: https://github.com/nodejs/node-addon-api/pull/663
PR-URL: https://github.com/nodejs/node/pull/31638
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: David Carlier <devnexen@gmail.com>
2020-02-06 12:43:24 -08:00

56 lines
2.1 KiB
JavaScript

'use strict';
// Test API calls for instance data.
const common = require('../../common');
if (module.parent) {
// When required as a module, run the tests.
const test_instance_data =
require(`./build/${common.buildType}/test_instance_data`);
// Test that instance data can be used in an async work callback.
new Promise((resolve) => test_instance_data.asyncWorkCallback(resolve))
// Test that the buffer finalizer can access the instance data.
.then(() => new Promise((resolve) => {
test_instance_data.testBufferFinalizer(resolve);
global.gc();
}))
// Test that the thread-safe function can access the instance data.
.then(() => new Promise((resolve) =>
test_instance_data.testThreadsafeFunction(common.mustCall(),
common.mustCall(resolve))));
} else {
// When launched as a script, run tests in either a child process or in a
// worker thread.
const assert = require('assert');
const requireAs = require('../../common/require-as');
const runOptions = { stdio: ['inherit', 'pipe', 'inherit'] };
const { spawnSync } = require('child_process');
// Run tests in a child process.
requireAs(__filename, ['--expose-gc'], runOptions, 'child');
// Run tests in a worker thread in a child process.
requireAs(__filename, ['--expose-gc'], runOptions, 'worker');
function testProcessExit(addonName) {
// Make sure that process exit is clean when the instance data has
// references to JS objects.
const path = require
.resolve(`./build/${common.buildType}/${addonName}`)
// Replace any backslashes with double backslashes because they'll be re-
// interpreted back to single backslashes in the command line argument
// to the child process. Windows needs this.
.replace(/\\/g, '\\\\');
const child = spawnSync(process.execPath, ['-e', `require('${path}');`]);
assert.strictEqual(child.signal, null);
assert.strictEqual(child.status, 0);
assert.strictEqual(child.stderr.toString(), 'addon_free');
}
testProcessExit('test_ref_then_set');
testProcessExit('test_set_then_ref');
}