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 value = test_reference.createExternal(); 26 assert.strictEqual(test_reference.finalizeCount, 0); 27 assert.strictEqual(typeof value, 'object'); 28 test_reference.checkExternal(value); 29 })(); 30 await gcUntil('External value without a finalizer', 31 () => (test_reference.finalizeCount === 0)); 32 33 (() => { 34 const value = test_reference.createExternalWithFinalize(); 35 assert.strictEqual(test_reference.finalizeCount, 0); 36 assert.strictEqual(typeof value, 'object'); 37 test_reference.checkExternal(value); 38 })(); 39 await gcUntil('External value with a finalizer', 40 () => (test_reference.finalizeCount === 1)); 41 42 (() => { 43 const value = test_reference.createExternalWithFinalize(); 44 assert.strictEqual(test_reference.finalizeCount, 0); 45 test_reference.createReference(value, 0); 46 assert.strictEqual(test_reference.referenceValue, value); 47 })(); 48 // Value should be GC'd because there is only a weak ref 49 await gcUntil('Weak reference', 50 () => (test_reference.referenceValue === undefined && 51 test_reference.finalizeCount === 1)); 52 test_reference.deleteReference(); 53 54 (() => { 55 const value = test_reference.createExternalWithFinalize(); 56 assert.strictEqual(test_reference.finalizeCount, 0); 57 test_reference.createReference(value, 1); 58 assert.strictEqual(test_reference.referenceValue, value); 59 })(); 60 // Value should NOT be GC'd because there is a strong ref 61 await gcUntil('Strong reference', 62 () => (test_reference.finalizeCount === 0)); 63 test_reference.deleteReference(); 64 await gcUntil('Strong reference (cont.d)', 65 () => (test_reference.finalizeCount === 1)); 66 67 (() => { 68 const value = test_reference.createExternalWithFinalize(); 69 assert.strictEqual(test_reference.finalizeCount, 0); 70 test_reference.createReference(value, 1); 71 })(); 72 // Value should NOT be GC'd because there is a strong ref 73 await gcUntil('Strong reference, increment then decrement to weak reference', 74 () => (test_reference.finalizeCount === 0)); 75 assert.strictEqual(test_reference.incrementRefcount(), 2); 76 // Value should NOT be GC'd because there is a strong ref 77 await gcUntil( 78 'Strong reference, increment then decrement to weak reference (cont.d-1)', 79 () => (test_reference.finalizeCount === 0)); 80 assert.strictEqual(test_reference.decrementRefcount(), 1); 81 // Value should NOT be GC'd because there is a strong ref 82 await gcUntil( 83 'Strong reference, increment then decrement to weak reference (cont.d-2)', 84 () => (test_reference.finalizeCount === 0)); 85 assert.strictEqual(test_reference.decrementRefcount(), 0); 86 // Value should be GC'd because the ref is now weak! 87 await gcUntil( 88 'Strong reference, increment then decrement to weak reference (cont.d-3)', 89 () => (test_reference.finalizeCount === 1)); 90 test_reference.deleteReference(); 91 // Value was already GC'd 92 await gcUntil( 93 'Strong reference, increment then decrement to weak reference (cont.d-4)', 94 () => (test_reference.finalizeCount === 1)); 95} 96runTests(); 97 98// This test creates a napi_ref on an object that has 99// been wrapped by napi_wrap and for which the finalizer 100// for the wrap calls napi_delete_ref on that napi_ref. 101// 102// Since both the wrap and the reference use the same 103// object the finalizer for the wrap and reference 104// may run in the same gc and in any order. 105// 106// It does that to validate that napi_delete_ref can be 107// called before the finalizer has been run for the 108// reference (there is a finalizer behind the scenes even 109// though it cannot be passed to napi_create_reference). 110// 111// Since the order is not guarranteed, run the 112// test a number of times maximize the chance that we 113// get a run with the desired order for the test. 114// 115// 1000 reliably recreated the problem without the fix 116// required to ensure delete could be called before 117// the finalizer in manual testing. 118for (let i = 0; i < 1000; i++) { 119 const wrapObject = new Object(); 120 test_reference.validateDeleteBeforeFinalize(wrapObject); 121 global.gc(); 122} 123