UNPKG

@fast-check/poisoning

Version:

Set of utilities to ease detection and revert of poisoning

85 lines (84 loc) 3.65 kB
import { PoisoningFreeArray, MapSymbol, SortSymbol, ShiftSymbol, PushSymbol } from './PoisoningFreeArray.js'; import { GetSymbol, HasSymbol, SetSymbol, PoisoningFreeMap } from './PoisoningFreeMap.js'; import { AddSymbol, HasSymbol as SetHasSymbol, PoisoningFreeSet } from './PoisoningFreeSet.js'; const SString = String; const safeObjectGetOwnPropertyDescriptors = Object.getOwnPropertyDescriptors; const safeObjectGetOwnPropertyNames = Object.getOwnPropertyNames; const safeObjectGetOwnPropertySymbols = Object.getOwnPropertySymbols; function compareKeys(keyA, keyB) { const sA = SString(keyA[0]); const sB = SString(keyB[0]); return sA < sB ? -1 : sA > sB ? 1 : 0; } function extractAllDescriptorsDetails(instance) { const descriptors = safeObjectGetOwnPropertyDescriptors(instance); const allDescriptors = PoisoningFreeArray.from([ ...safeObjectGetOwnPropertyNames(descriptors), ...safeObjectGetOwnPropertySymbols(descriptors), ]); const allDescriptorsDetails = PoisoningFreeArray.from(allDescriptors[MapSymbol]((name) => [ name, descriptors[name], ])); return allDescriptorsDetails[SortSymbol](compareKeys); } function flagRootRecursively(knownGlobals, instance, currentRoot) { const storedGlobal = knownGlobals[GetSymbol](instance); if (storedGlobal === undefined) { return; } if (storedGlobal.rootAncestors[SetHasSymbol](currentRoot)) { return; } storedGlobal.rootAncestors[AddSymbol](currentRoot); if (storedGlobal.depth <= 1) { return; } for (const [, descriptor] of storedGlobal.properties) { flagRootRecursively(knownGlobals, descriptor.value, currentRoot); } } export function captureAllGlobals() { const knownGlobals = PoisoningFreeMap.from(); const nextCaptures = PoisoningFreeArray.from([ { instance: globalThis, name: 'globalThis', currentDepth: 0, lastRootInPath: 'globalThis' }, ]); while (nextCaptures.length !== 0) { const { instance, name, currentDepth, lastRootInPath } = nextCaptures[ShiftSymbol](); if (typeof instance !== 'function' && typeof instance !== 'object') { continue; } if (instance === null || instance === undefined) { continue; } if (knownGlobals[HasSymbol](instance)) { flagRootRecursively(knownGlobals, instance, lastRootInPath); continue; } const allDescriptorsDetails = extractAllDescriptorsDetails(instance); const localGlobal = { name, depth: currentDepth, properties: PoisoningFreeMap.from(), rootAncestors: PoisoningFreeSet.from([lastRootInPath]), }; knownGlobals[SetSymbol](instance, localGlobal); for (let index = 0; index !== allDescriptorsDetails.length; ++index) { const descriptorName = allDescriptorsDetails[index][0]; const descriptor = allDescriptorsDetails[index][1]; localGlobal.properties[SetSymbol](descriptorName, descriptor); if (typeof descriptorName === 'symbol') { continue; } const subGlobalName = currentDepth !== 0 ? name + '.' + SString(descriptorName) : SString(descriptorName); const newLastRootInPath = currentDepth <= 1 ? name : lastRootInPath; nextCaptures[PushSymbol]({ instance: descriptor.value, name: subGlobalName, currentDepth: currentDepth + 1, lastRootInPath: newLastRootInPath, }); } } return knownGlobals; }