2023-09-09 12:22:26 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-09-09 12:46:41 +01:00
|
|
|
window.typedFunction = function(func) {
|
|
|
|
return func.addRuntimeChecker();
|
|
|
|
}
|
|
|
|
|
2023-09-09 13:20:03 +01:00
|
|
|
window.typedVar = function(name, value) {
|
|
|
|
function getTypeName(type) {
|
|
|
|
return typeof type === "function" ? type.name : typeof type;
|
|
|
|
}
|
|
|
|
|
|
|
|
var scope = typedVar.caller || window;
|
|
|
|
|
|
|
|
Object.defineProperty(scope, name, {
|
|
|
|
set: function(newValue) {
|
|
|
|
if (
|
|
|
|
(typeof value === "function" && !(newValue instanceof value)) ||
|
|
|
|
(typeof value !== "function" && typeof newValue !== typeof value)
|
|
|
|
) {
|
|
|
|
throw new Error("Cannot assign value of type " + getTypeName(newValue) + " to variable " + name + " of type " + getTypeName(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|