mirror of https://github.com/nodejs/node.git
125 lines
4.8 KiB
JavaScript
125 lines
4.8 KiB
JavaScript
'use strict';
|
|
// Flags: --expose-gc
|
|
//
|
|
// Testing API calls for Node-API references.
|
|
// We compare their behavior between Node-API version 8 and later.
|
|
// In version 8 references can be created only for object, function,
|
|
// and symbol types, while in newer versions they can be created for
|
|
// any value type.
|
|
//
|
|
const { gcUntil, buildType } = require('../../common');
|
|
const assert = require('assert');
|
|
const addon_v8 = require(`./build/${buildType}/test_reference_obj_only`);
|
|
const addon_new = require(`./build/${buildType}/test_reference_all_types`);
|
|
|
|
async function runTests(addon, isVersion8, isLocalSymbol) {
|
|
let allEntries = [];
|
|
|
|
(() => {
|
|
// Create values of all napi_valuetype types.
|
|
const undefinedValue = undefined;
|
|
const nullValue = null;
|
|
const booleanValue = false;
|
|
const numberValue = 42;
|
|
const stringValue = 'test_string';
|
|
const globalSymbolValue = Symbol.for('test_symbol_global');
|
|
const localSymbolValue = Symbol('test_symbol_local');
|
|
const symbolValue = isLocalSymbol ? localSymbolValue : globalSymbolValue;
|
|
const objectValue = { x: 1, y: 2 };
|
|
const functionValue = (x, y) => x + y;
|
|
const externalValue = addon.createExternal();
|
|
const bigintValue = 9007199254740991n;
|
|
|
|
// The position of entries in the allEntries array corresponds to the
|
|
// napi_valuetype enum value. See the CreateRef function for the
|
|
// implementation details.
|
|
allEntries = [
|
|
{ value: undefinedValue, canBeWeak: false, canBeRefV8: false },
|
|
{ value: nullValue, canBeWeak: false, canBeRefV8: false },
|
|
{ value: booleanValue, canBeWeak: false, canBeRefV8: false },
|
|
{ value: numberValue, canBeWeak: false, canBeRefV8: false },
|
|
{ value: stringValue, canBeWeak: false, canBeRefV8: false },
|
|
{ value: symbolValue, canBeWeak: isLocalSymbol, canBeRefV8: true,
|
|
isAlwaysStrong: !isLocalSymbol },
|
|
{ value: objectValue, canBeWeak: true, canBeRefV8: true },
|
|
{ value: functionValue, canBeWeak: true, canBeRefV8: true },
|
|
{ value: externalValue, canBeWeak: true, canBeRefV8: true },
|
|
{ value: bigintValue, canBeWeak: false, canBeRefV8: false },
|
|
];
|
|
|
|
// Go over all values of different types, create strong ref values for
|
|
// them, read the stored values, and check how the ref count works.
|
|
for (const entry of allEntries) {
|
|
if (!isVersion8 || entry.canBeRefV8) {
|
|
const index = addon.createRef(entry.value);
|
|
const refValue = addon.getRefValue(index);
|
|
assert.strictEqual(entry.value, refValue);
|
|
assert.strictEqual(addon.ref(index), 2);
|
|
assert.strictEqual(addon.unref(index), 1);
|
|
assert.strictEqual(addon.unref(index), 0);
|
|
} else {
|
|
assert.throws(() => { addon.createRef(entry.value); },
|
|
{
|
|
name: 'Error',
|
|
message: 'Invalid argument',
|
|
});
|
|
}
|
|
}
|
|
|
|
// When the reference count is zero, then object types become weak pointers
|
|
// and other types are released.
|
|
// Here we know that the GC is not run yet because the values are
|
|
// still in the allEntries array.
|
|
allEntries.forEach((entry, index) => {
|
|
if (!isVersion8 || entry.canBeRefV8) {
|
|
if (entry.canBeWeak || entry.isAlwaysStrong) {
|
|
assert.strictEqual(addon.getRefValue(index), entry.value);
|
|
} else {
|
|
assert.strictEqual(addon.getRefValue(index), undefined);
|
|
}
|
|
}
|
|
// Set to undefined to allow GC collect the value.
|
|
entry.value = undefined;
|
|
});
|
|
|
|
// To check that GC pass is done.
|
|
const objWithFinalizer = {};
|
|
addon.addFinalizer(objWithFinalizer);
|
|
})();
|
|
|
|
addon.initFinalizeCount();
|
|
assert.strictEqual(addon.getFinalizeCount(), 0);
|
|
await gcUntil('Wait until a finalizer is called',
|
|
() => (addon.getFinalizeCount() === 1));
|
|
|
|
// Create and call finalizer again to make sure that we had another GC pass.
|
|
(() => {
|
|
const objWithFinalizer = {};
|
|
addon.addFinalizer(objWithFinalizer);
|
|
})();
|
|
await gcUntil('Wait until a finalizer is called again',
|
|
() => (addon.getFinalizeCount() === 2));
|
|
|
|
// After GC and finalizers run, all values that support weak reference
|
|
// semantic must return undefined value.
|
|
allEntries.forEach((entry, index) => {
|
|
if (!isVersion8 || entry.canBeRefV8) {
|
|
if (!entry.isAlwaysStrong) {
|
|
assert.strictEqual(addon.getRefValue(index), undefined);
|
|
} else {
|
|
assert.notStrictEqual(addon.getRefValue(index), undefined);
|
|
}
|
|
addon.deleteRef(index);
|
|
}
|
|
});
|
|
}
|
|
|
|
async function runAllTests() {
|
|
await runTests(addon_v8, /* isVersion8 */ true, /* isLocalSymbol */ true);
|
|
await runTests(addon_v8, /* isVersion8 */ true, /* isLocalSymbol */ false);
|
|
await runTests(addon_new, /* isVersion8 */ false, /* isLocalSymbol */ true);
|
|
await runTests(addon_new, /* isVersion8 */ false, /* isLocalSymbol */ false);
|
|
}
|
|
|
|
runAllTests();
|