js-runtime/runtime-checker.js
2023-09-09 13:46:41 +02:00

62 lines
1.8 KiB
JavaScript

Function.prototype.addRuntimeChecker = function() {
var handler = {
apply: function(target, thisArg, argumentValues) {
// https://stackoverflow.com/a/31194949
var argumentNames = target.toString()
.replace(/[/][/].*$/mg,'') // strip single-line comments
.replace(/\s+/g, '') // strip white space
.replace(/[/][*][^/\*]*[*][/]/g, '') // strip multi-line comments
.split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters
.replace(/=[^,]+/g, '') // strip any ES6 defaults
.split(',').filter(Boolean); // split & filter [""]
var argumentNamesWithoutTypes = [];
for (var i = 0; i < argumentValues.length; i++) {
// Get type from parameter name
var match = argumentNames[i].match(/(.+)_(\w+)/);
if (!match || match.length < 3) {
argumentNamesWithoutTypes.push(argumentNames[i]);
continue;
}
argumentNamesWithoutTypes.push(match[1]);
var type = match[2];
// Check types
if (typeof argumentValues[i] !== type)
throw new Error("Invalid argument #" + i + " - Expected " + type + ", got " + typeof argumentValues[i]);
}
// Assign actual names to window object before calling functions
var oldWindow = {};
for (var i = 0; i < argumentNamesWithoutTypes.length; i++) {
var name = argumentNamesWithoutTypes[i];
oldWindow[name] = window[name];
window[name] = argumentValues[i];
}
// Call function
var runtimeCheckerResult = target.apply(thisArg, argumentValues);
// Re-assign old window values
for (var name in oldWindow) {
if (!oldWindow.hasOwnProperty(name)) continue;
window[name] = oldWindow[name];
}
return runtimeCheckerResult;
}
};
return new Proxy(this, handler);
}
window.typedFunction = function(func) {
return func.addRuntimeChecker();
}