• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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