• 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 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