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
JavaScript
'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