11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci// Flags: --expose-gc
31cb0ef41Sopenharmony_ci
41cb0ef41Sopenharmony_ciconst { gcUntil, buildType } = require('../../common');
51cb0ef41Sopenharmony_ciconst assert = require('assert');
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst test_reference = require(`./build/${buildType}/test_reference`);
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ci// This test script uses external values with finalizer callbacks
101cb0ef41Sopenharmony_ci// in order to track when values get garbage-collected. Each invocation
111cb0ef41Sopenharmony_ci// of a finalizer callback increments the finalizeCount property.
121cb0ef41Sopenharmony_ciassert.strictEqual(test_reference.finalizeCount, 0);
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ci// Run each test function in sequence,
151cb0ef41Sopenharmony_ci// with an async delay and GC call between each.
161cb0ef41Sopenharmony_ciasync function runTests() {
171cb0ef41Sopenharmony_ci  (() => {
181cb0ef41Sopenharmony_ci    const symbol = test_reference.createSymbol('testSym');
191cb0ef41Sopenharmony_ci    test_reference.createReference(symbol, 0);
201cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, symbol);
211cb0ef41Sopenharmony_ci  })();
221cb0ef41Sopenharmony_ci  test_reference.deleteReference();
231cb0ef41Sopenharmony_ci
241cb0ef41Sopenharmony_ci  (() => {
251cb0ef41Sopenharmony_ci    const symbol = test_reference.createSymbolFor('testSymFor');
261cb0ef41Sopenharmony_ci    test_reference.createReference(symbol, 0);
271cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, symbol);
281cb0ef41Sopenharmony_ci  })();
291cb0ef41Sopenharmony_ci  test_reference.deleteReference();
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ci  (() => {
321cb0ef41Sopenharmony_ci    const symbol = test_reference.createSymbolFor('testSymFor');
331cb0ef41Sopenharmony_ci    test_reference.createReference(symbol, 1);
341cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, symbol);
351cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, Symbol.for('testSymFor'));
361cb0ef41Sopenharmony_ci  })();
371cb0ef41Sopenharmony_ci  test_reference.deleteReference();
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci  (() => {
401cb0ef41Sopenharmony_ci    const symbol = test_reference.createSymbolForEmptyString();
411cb0ef41Sopenharmony_ci    test_reference.createReference(symbol, 0);
421cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, Symbol.for(''));
431cb0ef41Sopenharmony_ci  })();
441cb0ef41Sopenharmony_ci  test_reference.deleteReference();
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ci  (() => {
471cb0ef41Sopenharmony_ci    const symbol = test_reference.createSymbolForEmptyString();
481cb0ef41Sopenharmony_ci    test_reference.createReference(symbol, 1);
491cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, symbol);
501cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, Symbol.for(''));
511cb0ef41Sopenharmony_ci  })();
521cb0ef41Sopenharmony_ci  test_reference.deleteReference();
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci  assert.throws(() => test_reference.createSymbolForIncorrectLength(),
551cb0ef41Sopenharmony_ci                /Invalid argument/);
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci  (() => {
581cb0ef41Sopenharmony_ci    const value = test_reference.createExternal();
591cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.finalizeCount, 0);
601cb0ef41Sopenharmony_ci    assert.strictEqual(typeof value, 'object');
611cb0ef41Sopenharmony_ci    test_reference.checkExternal(value);
621cb0ef41Sopenharmony_ci  })();
631cb0ef41Sopenharmony_ci  await gcUntil('External value without a finalizer',
641cb0ef41Sopenharmony_ci                () => (test_reference.finalizeCount === 0));
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci  (() => {
671cb0ef41Sopenharmony_ci    const value = test_reference.createExternalWithFinalize();
681cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.finalizeCount, 0);
691cb0ef41Sopenharmony_ci    assert.strictEqual(typeof value, 'object');
701cb0ef41Sopenharmony_ci    test_reference.checkExternal(value);
711cb0ef41Sopenharmony_ci  })();
721cb0ef41Sopenharmony_ci  await gcUntil('External value with a finalizer',
731cb0ef41Sopenharmony_ci                () => (test_reference.finalizeCount === 1));
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci  (() => {
761cb0ef41Sopenharmony_ci    const value = test_reference.createExternalWithFinalize();
771cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.finalizeCount, 0);
781cb0ef41Sopenharmony_ci    test_reference.createReference(value, 0);
791cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, value);
801cb0ef41Sopenharmony_ci  })();
811cb0ef41Sopenharmony_ci  // Value should be GC'd because there is only a weak ref
821cb0ef41Sopenharmony_ci  await gcUntil('Weak reference',
831cb0ef41Sopenharmony_ci                () => (test_reference.referenceValue === undefined &&
841cb0ef41Sopenharmony_ci                test_reference.finalizeCount === 1));
851cb0ef41Sopenharmony_ci  test_reference.deleteReference();
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci  (() => {
881cb0ef41Sopenharmony_ci    const value = test_reference.createExternalWithFinalize();
891cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.finalizeCount, 0);
901cb0ef41Sopenharmony_ci    test_reference.createReference(value, 1);
911cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.referenceValue, value);
921cb0ef41Sopenharmony_ci  })();
931cb0ef41Sopenharmony_ci  // Value should NOT be GC'd because there is a strong ref
941cb0ef41Sopenharmony_ci  await gcUntil('Strong reference',
951cb0ef41Sopenharmony_ci                () => (test_reference.finalizeCount === 0));
961cb0ef41Sopenharmony_ci  test_reference.deleteReference();
971cb0ef41Sopenharmony_ci  await gcUntil('Strong reference (cont.d)',
981cb0ef41Sopenharmony_ci                () => (test_reference.finalizeCount === 1));
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ci  (() => {
1011cb0ef41Sopenharmony_ci    const value = test_reference.createExternalWithFinalize();
1021cb0ef41Sopenharmony_ci    assert.strictEqual(test_reference.finalizeCount, 0);
1031cb0ef41Sopenharmony_ci    test_reference.createReference(value, 1);
1041cb0ef41Sopenharmony_ci  })();
1051cb0ef41Sopenharmony_ci  // Value should NOT be GC'd because there is a strong ref
1061cb0ef41Sopenharmony_ci  await gcUntil('Strong reference, increment then decrement to weak reference',
1071cb0ef41Sopenharmony_ci                () => (test_reference.finalizeCount === 0));
1081cb0ef41Sopenharmony_ci  assert.strictEqual(test_reference.incrementRefcount(), 2);
1091cb0ef41Sopenharmony_ci  // Value should NOT be GC'd because there is a strong ref
1101cb0ef41Sopenharmony_ci  await gcUntil(
1111cb0ef41Sopenharmony_ci    'Strong reference, increment then decrement to weak reference (cont.d-1)',
1121cb0ef41Sopenharmony_ci    () => (test_reference.finalizeCount === 0));
1131cb0ef41Sopenharmony_ci  assert.strictEqual(test_reference.decrementRefcount(), 1);
1141cb0ef41Sopenharmony_ci  // Value should NOT be GC'd because there is a strong ref
1151cb0ef41Sopenharmony_ci  await gcUntil(
1161cb0ef41Sopenharmony_ci    'Strong reference, increment then decrement to weak reference (cont.d-2)',
1171cb0ef41Sopenharmony_ci    () => (test_reference.finalizeCount === 0));
1181cb0ef41Sopenharmony_ci  assert.strictEqual(test_reference.decrementRefcount(), 0);
1191cb0ef41Sopenharmony_ci  // Value should be GC'd because the ref is now weak!
1201cb0ef41Sopenharmony_ci  await gcUntil(
1211cb0ef41Sopenharmony_ci    'Strong reference, increment then decrement to weak reference (cont.d-3)',
1221cb0ef41Sopenharmony_ci    () => (test_reference.finalizeCount === 1));
1231cb0ef41Sopenharmony_ci  test_reference.deleteReference();
1241cb0ef41Sopenharmony_ci  // Value was already GC'd
1251cb0ef41Sopenharmony_ci  await gcUntil(
1261cb0ef41Sopenharmony_ci    'Strong reference, increment then decrement to weak reference (cont.d-4)',
1271cb0ef41Sopenharmony_ci    () => (test_reference.finalizeCount === 1));
1281cb0ef41Sopenharmony_ci}
1291cb0ef41Sopenharmony_cirunTests();
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_ci// This test creates a napi_ref on an object that has
1321cb0ef41Sopenharmony_ci// been wrapped by napi_wrap and for which the finalizer
1331cb0ef41Sopenharmony_ci// for the wrap calls napi_delete_ref on that napi_ref.
1341cb0ef41Sopenharmony_ci//
1351cb0ef41Sopenharmony_ci// Since both the wrap and the reference use the same
1361cb0ef41Sopenharmony_ci// object the finalizer for the wrap and reference
1371cb0ef41Sopenharmony_ci// may run in the same gc and in any order.
1381cb0ef41Sopenharmony_ci//
1391cb0ef41Sopenharmony_ci// It does that to validate that napi_delete_ref can be
1401cb0ef41Sopenharmony_ci// called before the finalizer has been run for the
1411cb0ef41Sopenharmony_ci// reference (there is a finalizer behind the scenes even
1421cb0ef41Sopenharmony_ci// though it cannot be passed to napi_create_reference).
1431cb0ef41Sopenharmony_ci//
1441cb0ef41Sopenharmony_ci// Since the order is not guarranteed, run the
1451cb0ef41Sopenharmony_ci// test a number of times maximize the chance that we
1461cb0ef41Sopenharmony_ci// get a run with the desired order for the test.
1471cb0ef41Sopenharmony_ci//
1481cb0ef41Sopenharmony_ci// 1000 reliably recreated the problem without the fix
1491cb0ef41Sopenharmony_ci// required to ensure delete could be called before
1501cb0ef41Sopenharmony_ci// the finalizer in manual testing.
1511cb0ef41Sopenharmony_cifor (let i = 0; i < 1000; i++) {
1521cb0ef41Sopenharmony_ci  const wrapObject = new Object();
1531cb0ef41Sopenharmony_ci  test_reference.validateDeleteBeforeFinalize(wrapObject);
1541cb0ef41Sopenharmony_ci  global.gc();
1551cb0ef41Sopenharmony_ci}
156