UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

377 lines (373 loc) 12.3 kB
'use strict'; var global = require('../../types/global.js'); var types = require('./types.js'); /** * Advanced Reference Tracker * * Tracks object references with leak detection and comprehensive monitoring */ // Initialize polyfills for WeakRef and FinalizationRegistry global.initializePolyfills(); /** * Advanced reference tracker with comprehensive leak detection */ class AdvancedReferenceTracker { constructor(eventManager, config) { this.references = new Map(); this.isEnabled = true; this.eventManager = eventManager; this.config = config; // Initialize finalization registry for automatic cleanup this.finalizationRegistry = new FinalizationRegistry((id) => { this.handleObjectFinalization(id); }); // Start periodic cleanup if enabled if (config.enableLeakDetection) { this.startPeriodicCleanup(); } } /** * Add reference to an object with comprehensive metadata */ addReference(obj, id) { if (!this.isEnabled) { return; } try { const now = Date.now(); const existingRef = this.references.get(id); if (existingRef) { // Update existing reference existingRef.refCount++; existingRef.lastAccessed = now; } else { // Create new reference const metadata = { id, weakRef: new WeakRef(obj), refCount: 1, createdAt: now, lastAccessed: now, size: this.estimateObjectSize(obj), type: this.getObjectType(obj), stackTrace: this.config.enablePerformanceMonitoring ? this.captureStackTrace() : undefined }; this.references.set(id, metadata); // Register with finalization registry this.finalizationRegistry.register(obj, id); this.eventManager.emit(types.MemoryEventType.OBJECT_TRACKED, { id, size: metadata.size, type: metadata.type, timestamp: now }); } } catch (error) { this.eventManager.emit(types.MemoryEventType.ERROR_OCCURRED, { error: `Failed to add reference for ${id}: ${error}`, operation: 'addReference', objectId: id }); } } /** * Remove reference from an object */ removeReference(id) { if (!this.isEnabled) { return; } try { const metadata = this.references.get(id); if (!metadata) { return; } metadata.refCount--; metadata.lastAccessed = Date.now(); if (metadata.refCount <= 0) { this.references.delete(id); this.eventManager.emit(types.MemoryEventType.OBJECT_RELEASED, { id, lifetime: Date.now() - metadata.createdAt, size: metadata.size }); } } catch (error) { this.eventManager.emit(types.MemoryEventType.ERROR_OCCURRED, { error: `Failed to remove reference for ${id}: ${error}`, operation: 'removeReference', objectId: id }); } } /** * Get reference count for an object */ getRefCount(id) { const metadata = this.references.get(id); return metadata ? metadata.refCount : 0; } /** * Get object age in milliseconds */ getObjectAge(id) { const metadata = this.references.get(id); return metadata ? Date.now() - metadata.createdAt : 0; } /** * Get last access time */ getLastAccess(id) { const metadata = this.references.get(id); return metadata ? metadata.lastAccessed : 0; } /** * Clean up dead references */ cleanup() { if (!this.isEnabled) { return; } let cleanedCount = 0; const now = Date.now(); for (const [id, metadata] of this.references.entries()) { const obj = metadata.weakRef.deref(); if (!obj) { this.references.delete(id); cleanedCount++; } } if (cleanedCount > 0) { this.eventManager.emit(types.MemoryEventType.GC_COMPLETED, { objectsCollected: cleanedCount, operation: 'cleanup', timestamp: now }); } } /** * Get all tracked object IDs */ getTrackedObjects() { return Array.from(this.references.keys()); } /** * Detect potential memory leaks with advanced heuristics */ detectLeaks() { if (!this.isEnabled || !this.config.enableLeakDetection) { return []; } const now = Date.now(); const leaks = []; const suspiciousObjects = []; let totalLeakedMemory = 0; for (const [id, metadata] of this.references.entries()) { const obj = metadata.weakRef.deref(); if (!obj) { // Object was garbage collected, clean up this.references.delete(id); continue; } const age = now - metadata.createdAt; const timeSinceAccess = now - metadata.lastAccessed; // Leak detection heuristics const isOld = age > this.config.leakDetectionThreshold; const isStale = timeSinceAccess > this.config.leakDetectionThreshold / 2; const hasHighRefCount = metadata.refCount > 10; // Configurable threshold const isLargeObject = metadata.size > 1024 * 1024; // > 1MB if (isOld && isStale) { if (hasHighRefCount || isLargeObject) { leaks.push(id); totalLeakedMemory += metadata.size; } else { suspiciousObjects.push(id); } } } if (leaks.length > 0) { const result = { leaks, suspiciousObjects, totalLeakedMemory, detectionTime: Date.now(), confidence: this.calculateLeakConfidence(leaks.length, suspiciousObjects.length) }; this.eventManager.emit(types.MemoryEventType.LEAK_DETECTED, result); } return leaks; } /** * Calculate confidence level for leak detection */ calculateLeakConfidence(leakCount, suspiciousCount) { const totalObjects = this.references.size; if (totalObjects === 0) return 0; const leakRatio = leakCount / totalObjects; const suspiciousRatio = suspiciousCount / totalObjects; // Higher confidence for more leaks relative to total objects let confidence = Math.min(leakRatio * 2, 1.0); // Reduce confidence if there are many suspicious objects (might be false positives) if (suspiciousRatio > 0.5) { confidence *= 0.7; } return Math.max(0, Math.min(1, confidence)); } /** * Handle object finalization */ handleObjectFinalization(id) { const metadata = this.references.get(id); if (metadata) { this.references.delete(id); this.eventManager.emit(types.MemoryEventType.OBJECT_RELEASED, { id, lifetime: Date.now() - metadata.createdAt, size: metadata.size, automatic: true }); } } /** * Estimate object size in bytes */ estimateObjectSize(obj) { if (obj === null || obj === undefined) return 0; const type = typeof obj; switch (type) { case 'boolean': return 4; case 'number': return 8; case 'string': return obj.length * 2; // UTF-16 case 'object': if (obj instanceof ArrayBuffer) return obj.byteLength; if (obj instanceof Uint8Array) return obj.byteLength; if (Array.isArray(obj)) { return obj.reduce((sum, item) => sum + this.estimateObjectSize(item), 0); } // Estimate object size based on properties return Object.keys(obj).length * 64; // Rough estimate default: return 64; // Default estimate } } /** * Get object type description */ getObjectType(obj) { if (obj === null) return 'null'; if (obj === undefined) return 'undefined'; if (Array.isArray(obj)) return 'Array'; if (obj instanceof Date) return 'Date'; if (obj instanceof RegExp) return 'RegExp'; if (obj instanceof Error) return 'Error'; if (obj instanceof ArrayBuffer) return 'ArrayBuffer'; if (obj instanceof Uint8Array) return 'Uint8Array'; return obj.constructor?.name || typeof obj; } /** * Capture stack trace for debugging */ captureStackTrace() { try { const stack = new Error().stack; return stack?.split('\n').slice(3, 8).join('\n'); // Skip first 3 lines, take next 5 } catch { return undefined; } } /** * Start periodic cleanup */ startPeriodicCleanup() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); } this.cleanupInterval = setInterval(() => { this.cleanup(); if (this.config.enableLeakDetection) { this.detectLeaks(); } }, this.config.autoCleanupInterval); } /** * Get tracker statistics */ getStats() { const now = Date.now(); let totalSize = 0; let oldestObject = now; let newestObject = 0; const typeDistribution = {}; for (const metadata of this.references.values()) { totalSize += metadata.size; oldestObject = Math.min(oldestObject, metadata.createdAt); newestObject = Math.max(newestObject, metadata.createdAt); typeDistribution[metadata.type] = (typeDistribution[metadata.type] || 0) + 1; } return { trackedObjects: this.references.size, totalEstimatedSize: totalSize, averageObjectSize: this.references.size > 0 ? totalSize / this.references.size : 0, oldestObjectAge: oldestObject < now ? now - oldestObject : 0, newestObjectAge: newestObject > 0 ? now - newestObject : 0, typeDistribution, isEnabled: this.isEnabled }; } /** * Enable/disable tracker */ setEnabled(enabled) { this.isEnabled = enabled; if (enabled && this.config.enableLeakDetection && !this.cleanupInterval) { this.startPeriodicCleanup(); } else if (!enabled && this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = undefined; } } /** * Update configuration */ updateConfig(config) { this.config = config; if (config.enableLeakDetection && this.isEnabled) { this.startPeriodicCleanup(); } else if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = undefined; } } /** * Destroy the tracker */ destroy() { this.isEnabled = false; if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = undefined; } this.references.clear(); } } exports.AdvancedReferenceTracker = AdvancedReferenceTracker; //# sourceMappingURL=reference-tracker.js.map