day-3.mjs

December 26, 2021 ยท View on GitHub

/////////////////////////////////////////////////////////////////////////////// // Day 3 - Disappearing Act // // #region problem // /////////////////////////////////////////////////////////////////////////////// // Write a hold(toHold) function that returns a frozen Object with a // // non-getter property .value set to toHold. // // // // Write a second function free(result) that when given the result of // // hold(obj) allows the .value of result to be freed without GC of // // result. // ///////////////////////////////////////////////////////////////////////////////

/**

  • @template {unknown} T
  • @param {T} toHold
  • @returns {Readonly<{value: T}>} */ function hold(toHold) { // ... }

/**

  • @template {{value: unknown}} T
  • @param {T} result
  • @returns {void} */ function free(result) { // ... }

/////////////////////////////////////////////////////////////////////////////// // #endregion problem // ///////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////// // Test - Leave code after this alone // // #region test // ///////////////////////////////////////////////////////////////////////////////

import assert from 'node:assert/strict';

// keep the process alive indefinitely so we can wait for the GC check setInterval(() => { // this makes the FinalizationRegistry fire faster, VM magics if (!finalizer) { console.log(finalizer) } [].concat(0); [].concat(0); [].concat(0); [].concat(0); }, 0); const finalizer = new FinalizationRegistry((err) => { if (err) { console.error(err); } process.exit(err ? 1 : 0); });

let value = new class EasyToFindInHeapSnapshot {}; finalizer.register(value, '');

// exports don't GC since they live on as long as the module does export const holder = hold(value); assert.ok(Object.isFrozen(holder), 'result should be frozen'); assert.ok(Object.hasOwn(holder, 'value'));

// holder shouldn't be GC'd, we need to show that the solution doesn't require // the holder to be GC'd finalizer.register(holder, 'result should not be GC'd, how did you get here?');

let descriptor = Object.getOwnPropertyDescriptor(holder, 'value'); assert.ok(Object.hasOwn(descriptor, 'value')) assert.strictEqual( descriptor?.value, value, 'result .value should equal the value given to hold(toHold)' ); assert.strictEqual( descriptor?.writable, false, 'result .value should be non-writable to be frozen' ); assert.strictEqual( descriptor.configurable, false, 'result .value should be non-configurable to be frozen' ); descriptor = null; value = null;

free(holder);

/////////////////////////////////////////////////////////////////////////////// // #endregion test // ///////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////// // Hints - Encoded in ROT13 encoded // // #region hints // /////////////////////////////////////////////////////////////////////////////// // // // Fvzcyr: Cebkl unf inevbhf jnlf gb cerirag npprff gb cebcregvrf rira jura // // vgf gnetrg vf sebmra. // // // /////////////////////////////////////////////////////////////////////////////// // #endregion hints // ///////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////// // Use Cases // // #region uses // /////////////////////////////////////////////////////////////////////////////// // // // This kind of workflow can help be used for debugging to ensure that a // // value is not accessed after some point in time. The fact that a value can // // be GC'd shows it isn't accessible anymore. // // // /////////////////////////////////////////////////////////////////////////////// // #endregion uses // ///////////////////////////////////////////////////////////////////////////////