|
(function () { |
|
'use strict'; |
|
/** |
|
* Stores original versions of native functions before patching. |
|
* @type {Map<string, Function>} |
|
* @description Map structure: |
|
* - Key: Original method's name property (e.g., 'log', 'error') |
|
* - Value: Original native function implementation |
|
*/ |
|
const savedOriginalMethods = new Map(); |
|
/** |
|
* Stores the string conversion of the modified local function together with the original local function. |
|
* @type {Map<string, Function>} |
|
* @description Map structure: |
|
* - Key: String conversion of the modified native function |
|
* - Value: Original native function implementation |
|
*/ |
|
const methodRemapDictionary = new Map(); |
|
|
|
/** |
|
* Converts a function to shorthand method syntax while preserving closure variables |
|
* @param {Function} functionToConvert - Function to convert to shorthand method syntax |
|
* @param {string} functionName - Name to use for the method property |
|
* @returns {Function} Generated function with access to closure variables |
|
* @description |
|
* 1. Extracts function body by removing 'function' declaration |
|
* 2. Creates new method-style function using dynamic evaluation |
|
* 3. Injects closure references (savedOriginalMethods, methodRemapDictionary) |
|
* through immediately-invoked arguments |
|
*/ |
|
function convertFunctionToShorthand(standardFunction, functionName) { |
|
const functionBody = standardFunction.toString().replace(new RegExp(`^function([^\\(]*)`), '') |
|
|
|
const shorthandFunction = new Function( |
|
`const [savedOriginalMethods, methodRemapDictionary, name]=arguments;return { |
|
${functionName}${functionBody} |
|
}.${functionName};` |
|
)(savedOriginalMethods, methodRemapDictionary, functionName); |
|
|
|
return shorthandFunction; |
|
} |
|
|
|
/** |
|
* Replaces a native function with a modified proxy function |
|
* @param {string} name - The name of the native function to be modified |
|
* @param {Function} nativeFunction - The original native function implementation |
|
* @param {Function} modifyFunction - The function to modify the native function |
|
* @returns {Function} The shorthand function that replaces the native function |
|
* @description This function stores the original native function in a map, |
|
* converts the modifying function to a shorthand method, |
|
* and returns a shorthand function that maintains a reference |
|
* to the original implementation. |
|
*/ |
|
function modifyNativeFunction(name, nativeFunction, modifyFunction) { |
|
methodRemapDictionary.set(name, nativeFunction); |
|
const shorthandModifyFunction = convertFunctionToShorthand(modifyFunction, name); |
|
savedOriginalMethods.set(methodRemapDictionary.get('toString').call(shorthandModifyFunction), nativeFunction); |
|
return shorthandModifyFunction; |
|
} |
|
|
|
/** |
|
* A function that handles toString operations with method remapping support. |
|
* It attempts to apply toString on the original method if available in the savedOriginalMethods map. |
|
* If an error occurs, it modifies the error stack trace by removing the third line before rethrowing. |
|
* |
|
* @function toString |
|
* @param {...*} args - Arguments to be passed to the toString method |
|
* @returns {string} The string representation of the object |
|
* @throws {Error} Rethrows any error that occurs during execution with modified stack trace |
|
*/ |
|
const toString = function (...args) { |
|
try { |
|
const ret = Reflect.apply(methodRemapDictionary.get("toString"), this, args); |
|
if (savedOriginalMethods.has(ret)) { |
|
return Reflect.apply(methodRemapDictionary.get("toString"), savedOriginalMethods.get(ret), args); |
|
} |
|
return ret; |
|
} catch (error) { |
|
const stackArray = error.stack.split('\n'); |
|
stackArray.splice(2, 1); |
|
const modifiedStack = stackArray.join('\n'); |
|
error.stack = modifiedStack; |
|
throw error; |
|
} |
|
}; |
|
Function.prototype.toString = modifyNativeFunction('toString', Function.prototype.toString, toString); |
|
|
|
/** |
|
* Patches console methods to intercept and handle potential errors during argument access. |
|
* |
|
* Iterates through a list of console methods (log, error, info, warn, trace, dir, debug, dirxml, table ...) |
|
* and replaces each with a modified version. The modified version attempts to access specific properties |
|
* of each argument passed to the console method (toString, name, message). If any error occurs during |
|
* this access, the error's stack trace is modified by removing the third line before re-throwing. |
|
* The original console methods are stored and used for the actual logging. |
|
*/ |
|
const consoleMethods = ['log','error','info','warn','trace','dir','debug','dirxml','table']; |
|
for (const name of consoleMethods) { |
|
const fn = function (...args) { |
|
try { |
|
for (const arg of args) { |
|
const descriptors=Object.getOwnPropertyDescriptors(arg); |
|
if(arg?.hasOwnProperty('toString')){ |
|
arg.toString(); |
|
} |
|
if(descriptors?.name?.get){ |
|
descriptors?.name?.get(); |
|
} |
|
if(descriptors?.message?.get){ |
|
descriptors?.message?.get(); |
|
} |
|
} |
|
//Reflect.apply(orginalMethodsMap.get(name), this, args); |
|
} catch (error) { |
|
const stackArray = error.stack.split('\n'); |
|
stackArray.splice(2, 1); |
|
const modifiedStack = stackArray.join('\n'); |
|
error.stack = modifiedStack; |
|
throw error; |
|
} |
|
}; |
|
console[name] = modifyNativeFunction(name, console[name], fn); |
|
} |
|
})(); |