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