1'use strict'; 2// Flags: --expose-gc 3 4const { gcUntil, buildType } = require('../../common'); 5const assert = require('assert'); 6 7const test_reference = require(`./build/${buildType}/test_reference`); 8 9// This test script uses external values with finalizer callbacks 10// in order to track when values get garbage-collected. Each invocation 11// of a finalizer callback increments the finalizeCount property. 12assert.strictEqual(test_reference.finalizeCount, 0); 13 14// Run each test function in sequence, 15// with an async delay and GC call between each. 16async function runTests() { 17 (() => { 18 const symbol = test_reference.createSymbol('testSym'); 19 test_reference.createReference(symbol, 0); 20 assert.strictEqual(test_reference.referenceValue, symbol); 21 })(); 22 test_reference.deleteReference(); 23 24 (() => { 25 const symbol = test_reference.createSymbolFor('testSymFor'); 26 test_reference.createReference(symbol, 0); 27 assert.strictEqual(test_reference.referenceValue, symbol); 28 })(); 29 test_reference.deleteReference(); 30 31 (() => { 32 const symbol = test_reference.createSymbolFor('testSymFor'); 33 test_reference.createReference(symbol, 1); 34 assert.strictEqual(test_reference.referenceValue, symbol); 35 assert.strictEqual(test_reference.referenceValue, Symbol.for('testSymFor')); 36 })(); 37 test_reference.deleteReference(); 38 39 (() => { 40 const symbol = test_reference.createSymbolForEmptyString(); 41 test_reference.createReference(symbol, 0); 42 assert.strictEqual(test_reference.referenceValue, Symbol.for('')); 43 })(); 44 test_reference.deleteReference(); 45 46 (() => { 47 const symbol = test_reference.createSymbolForEmptyString(); 48 test_reference.createReference(symbol, 1); 49 assert.strictEqual(test_reference.referenceValue, symbol); 50 assert.strictEqual(test_reference.referenceValue, Symbol.for('')); 51 })(); 52 test_reference.deleteReference(); 53 54 assert.throws(() => test_reference.createSymbolForIncorrectLength(), 55 /Invalid argument/); 56 57 (() => { 58 const value = test_reference.createExternal(); 59 assert.strictEqual(test_reference.finalizeCount, 0); 60 assert.strictEqual(typeof value, 'object'); 61 test_reference.checkExternal(value); 62 })(); 63 await gcUntil('External value without a finalizer', 64 () => (test_reference.finalizeCount === 0)); 65 66 (() => { 67 const value = test_reference.createExternalWithFinalize(); 68 assert.strictEqual(test_reference.finalizeCount, 0); 69 assert.strictEqual(typeof value, 'object'); 70 test_reference.checkExternal(value); 71 })(); 72 await gcUntil('External value with a finalizer', 73 () => (test_reference.finalizeCount === 1)); 74 75 (() => { 76 const value = test_reference.createExternalWithFinalize(); 77 assert.strictEqual(test_reference.finalizeCount, 0); 78 test_reference.createReference(value, 0); 79 assert.strictEqual(test_reference.referenceValue, value); 80 })(); 81 // Value should be GC'd because there is only a weak ref 82 await gcUntil('Weak reference', 83 () => (test_reference.referenceValue === undefined && 84 test_reference.finalizeCount === 1)); 85 test_reference.deleteReference(); 86 87 (() => { 88 const value = test_reference.createExternalWithFinalize(); 89 assert.strictEqual(test_reference.finalizeCount, 0); 90 test_reference.createReference(value, 1); 91 assert.strictEqual(test_reference.referenceValue, value); 92 })(); 93 // Value should NOT be GC'd because there is a strong ref 94 await gcUntil('Strong reference', 95 () => (test_reference.finalizeCount === 0)); 96 test_reference.deleteReference(); 97 await gcUntil('Strong reference (cont.d)', 98 () => (test_reference.finalizeCount === 1)); 99 100 (() => { 101 const value = test_reference.createExternalWithFinalize(); 102 assert.strictEqual(test_reference.finalizeCount, 0); 103 test_reference.createReference(value, 1); 104 })(); 105 // Value should NOT be GC'd because there is a strong ref 106 await gcUntil('Strong reference, increment then decrement to weak reference', 107 () => (test_reference.finalizeCount === 0)); 108 assert.strictEqual(test_reference.incrementRefcount(), 2); 109 // Value should NOT be GC'd because there is a strong ref 110 await gcUntil( 111 'Strong reference, increment then decrement to weak reference (cont.d-1)', 112 () => (test_reference.finalizeCount === 0)); 113 assert.strictEqual(test_reference.decrementRefcount(), 1); 114 // Value should NOT be GC'd because there is a strong ref 115 await gcUntil( 116 'Strong reference, increment then decrement to weak reference (cont.d-2)', 117 () => (test_reference.finalizeCount === 0)); 118 assert.strictEqual(test_reference.decrementRefcount(), 0); 119 // Value should be GC'd because the ref is now weak! 120 await gcUntil( 121 'Strong reference, increment then decrement to weak reference (cont.d-3)', 122 () => (test_reference.finalizeCount === 1)); 123 test_reference.deleteReference(); 124 // Value was already GC'd 125 await gcUntil( 126 'Strong reference, increment then decrement to weak reference (cont.d-4)', 127 () => (test_reference.finalizeCount === 1)); 128} 129runTests(); 130 131// This test creates a napi_ref on an object that has 132// been wrapped by napi_wrap and for which the finalizer 133// for the wrap calls napi_delete_ref on that napi_ref. 134// 135// Since both the wrap and the reference use the same 136// object the finalizer for the wrap and reference 137// may run in the same gc and in any order. 138// 139// It does that to validate that napi_delete_ref can be 140// called before the finalizer has been run for the 141// reference (there is a finalizer behind the scenes even 142// though it cannot be passed to napi_create_reference). 143// 144// Since the order is not guarranteed, run the 145// test a number of times maximize the chance that we 146// get a run with the desired order for the test. 147// 148// 1000 reliably recreated the problem without the fix 149// required to ensure delete could be called before 150// the finalizer in manual testing. 151for (let i = 0; i < 1000; i++) { 152 const wrapObject = new Object(); 153 test_reference.validateDeleteBeforeFinalize(wrapObject); 154 global.gc(); 155} 156