protoblast
Advanced tools
Comparing version 0.8.8 to 0.8.9
@@ -0,1 +1,5 @@ | ||
## 0.8.9 (2023-05-09) | ||
* Add `Function#instrumentMethod()` to add instrumentation to existing methods | ||
## 0.8.8 (2023-04-20) | ||
@@ -2,0 +6,0 @@ |
@@ -69,3 +69,3 @@ var performanceNow, | ||
// Default test sizes | ||
Blast.benchmark_sync_sizes = [1, 10, 120, 120, 240, 240, 240, 480]; | ||
Blast.benchmark_sync_sizes = [5, 10, 120, 120, 240, 240, 240, 480]; | ||
Blast.benchmark_async_sizes = [120, 120, 120, 240, 240, 480]; | ||
@@ -187,4 +187,4 @@ | ||
// Only the first 5 tests are mandatory | ||
if (r > 4) { | ||
// Only the first 6 tests are mandatory | ||
if (r > 5) { | ||
// If the last sample is not the best, stop further tests | ||
@@ -191,0 +191,0 @@ if (samples.indexOf(Bound.Array.max(samples)) !== r) { |
@@ -1224,2 +1224,18 @@ const finished_constitutors = new WeakMap(), | ||
/** | ||
* Instrument a static method | ||
* | ||
* @author Jelle De Loecker <jelle@elevenways.be> | ||
* @since 0.8.9 | ||
* @version 0.8.9 | ||
* | ||
* @param {Function} constructor Constructor to modify prototype of | ||
* @param {String} method_name The name of the method to instrument | ||
* @param {Function} before Function to call before executing the main method | ||
* @param {Function} after Function to call after executing the main method | ||
*/ | ||
defClassMethod(function instrumentStaticMethod(constructor, method_name, before, after) { | ||
return Blast.addMethodInstrumentation(constructor, method_name, before, after); | ||
}); | ||
/** | ||
* Set a static method on the given constructor. | ||
@@ -1373,2 +1389,18 @@ * Can also be used to set a static property. This won't fire an error | ||
/** | ||
* Instrument a method | ||
* | ||
* @author Jelle De Loecker <jelle@elevenways.be> | ||
* @since 0.8.9 | ||
* @version 0.8.9 | ||
* | ||
* @param {Function} constructor Constructor to modify prototype of | ||
* @param {String} method_name The name of the method to instrument | ||
* @param {Function} before Function to call before executing the main method | ||
* @param {Function} after Function to call after executing the main method | ||
*/ | ||
defClassMethod(function instrumentMethod(constructor, method_name, before, after) { | ||
return Blast.addMethodInstrumentation(constructor.prototype, method_name, before, after); | ||
}); | ||
/** | ||
* Set a prototype method on the given constructor | ||
@@ -1378,3 +1410,3 @@ * | ||
* @since 0.1.3 | ||
* @version 0.6.1 | ||
* @version 0.8.9 | ||
* | ||
@@ -1389,7 +1421,5 @@ * @param {Function} constructor Constructor to modify prototype of | ||
var existing, | ||
method, | ||
let method, | ||
proto, | ||
keys, | ||
i; | ||
keys; | ||
@@ -1414,2 +1444,5 @@ if (typeof constructor === 'function') { | ||
let has_typed_wrapper, | ||
i; | ||
for (i = 0; i < keys.length; i++) { | ||
@@ -1420,7 +1453,9 @@ | ||
has_typed_wrapper = Blast.hasSignatureWrapperMethod(proto, keys[i]); | ||
// Get a possible existing value | ||
existing = Blast.Bound.Object.getPropertyDescriptor(proto, keys[0]); | ||
let existing = Blast.Bound.Object.getPropertyDescriptor(proto, keys[0]); | ||
// If there already was something here, set it as this method's parent | ||
if (existing && typeof existing.value !== 'undefined') { | ||
if (!has_typed_wrapper && existing && typeof existing.value !== 'undefined') { | ||
if (typeof method == 'object') { | ||
@@ -1441,4 +1476,5 @@ Blast.defineValue(method.value, 'super', existing.value); | ||
// Now set the method on the prototype | ||
if (typeof method == 'function') { | ||
if (Blast.hasSignatureWrapperMethod(proto, keys[i])) { | ||
Blast.addCatchallMethod(proto, keys[i], method); | ||
} else if (typeof method == 'function') { | ||
Blast.defineValue(proto, keys[i], method); | ||
@@ -1445,0 +1481,0 @@ } else { |
@@ -533,2 +533,3 @@ const TYPE_WRAPPER = Symbol('type_wrapper'), | ||
* | ||
* @param {TypesCollection} types_collection | ||
* @param {SignatureType[]} argument_types | ||
@@ -538,3 +539,4 @@ * @param {SignatureType} return_type | ||
*/ | ||
constructor(argument_types, return_type, fnc) { | ||
constructor(types_collection, argument_types, return_type, fnc) { | ||
this.types_collection = types_collection; | ||
this.argument_types = argument_types; | ||
@@ -551,2 +553,10 @@ this.return_type = return_type; | ||
} | ||
if (!fnc.super) { | ||
const that = this; | ||
Blast.defineValue(fnc, 'super', function _super(...args) { | ||
return that.types_collection.doSuper(this, args); | ||
}); | ||
} | ||
} | ||
@@ -650,2 +660,244 @@ | ||
/** | ||
* Does the given context have a signature wrapper? | ||
* | ||
* @author Jelle De Loecker <jelle@elevenways.be> | ||
* @since 0.8.9 | ||
* @version 0.8.9 | ||
* | ||
* @param {Object} context | ||
* | ||
* @return {Boolean} | ||
*/ | ||
Blast.hasSignatureWrapperMethod = function hasSignatureWrapperMethod(context, name) { | ||
if (!context) { | ||
throw new Error('Invalid context passed to getSignatureWrapperMethod'); | ||
} | ||
let value = context[name]; | ||
if (!value) { | ||
return false; | ||
} | ||
return value[TYPE_WRAPPER] || false; | ||
}; | ||
/** | ||
* The class to keep track of the signature wrapper methods | ||
* | ||
* @author Jelle De Loecker <jelle@elevenways.be> | ||
* @since 0.8.9 | ||
* @version 0.8.9 | ||
*/ | ||
class TypesCollection { | ||
catchall = null; | ||
lengths = []; | ||
before = []; | ||
after = []; | ||
has_before_instrumentation = false; | ||
has_after_instrumentation = false; | ||
is_static = false; | ||
wrapper_method = null; | ||
constructor(context, name) { | ||
this.context = context; | ||
this.name = name; | ||
if (typeof context == 'function') { | ||
this.parent_context = context.super; | ||
this.is_static = true; | ||
} else if (context.constructor) { | ||
this.parent_context = context.constructor.super?.prototype; | ||
} else { | ||
this.parent_context = null; | ||
} | ||
} | ||
setCatchAll(method) { | ||
this.catchall = method; | ||
if (this.wrapper_method.super) { | ||
const that = this; | ||
Blast.defineValue(method, 'super', function _super(...args) { | ||
let super_method = that.wrapper_method.super; | ||
if (super_method && super_method[TYPE_WRAPPER]) { | ||
return super_method[TYPES].executeWithArguments(this, args, false); | ||
} else { | ||
return super_method.apply(this, args); | ||
} | ||
}); | ||
} | ||
} | ||
addBeforeInstrumentation(method) { | ||
this.has_before_instrumentation = true; | ||
this.before.push(method); | ||
} | ||
addAfterInstrumentation(method) { | ||
this.has_after_instrumentation = true; | ||
this.after.push(method); | ||
} | ||
doBeforeInstrumentation(instance, args) { | ||
if (this.has_before_instrumentation) { | ||
let i; | ||
for (i = 0; i < this.before.length; i++) { | ||
this.before[i](instance, args); | ||
} | ||
} | ||
if (this.parent_context) { | ||
let parent = this.parent_context[this.name]; | ||
if (parent) { | ||
let types = parent[TYPES]; | ||
if (types && types != this) { | ||
types.doBeforeInstrumentation(instance, args); | ||
} | ||
} | ||
} | ||
} | ||
doAfterInstrumentation(instance, args, result) { | ||
if (this.has_after_instrumentation) { | ||
let i; | ||
for (i = 0; i < this.after.length; i++) { | ||
this.after[i](instance, args, result); | ||
} | ||
} | ||
if (this.parent_context) { | ||
let parent = this.parent_context[this.name]; | ||
if (parent) { | ||
let types = parent[TYPES]; | ||
if (types && types != this) { | ||
types.doAfterInstrumentation(instance, args, result); | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Find the correct method for the given arguments | ||
* | ||
* @author Jelle De Loecker <jelle@elevenways.be> | ||
* @since 0.7.25 | ||
* @version 0.8.9 | ||
* | ||
* @param {Array} args | ||
* | ||
* @return {Object} | ||
*/ | ||
findCorrectSignature(args) { | ||
let length = args.length, | ||
signatures = this[length], | ||
result = false; | ||
if (signatures) { | ||
result = testSignatures(signatures, args, length); | ||
} | ||
if (!result) { | ||
let test_length, | ||
i; | ||
for (i = 0; i < this.lengths.length; i++) { | ||
test_length = this.lengths[i]; | ||
if (test_length > length) { | ||
signatures = this[test_length]; | ||
if (signatures) { | ||
result = testSignatures(signatures, args, length); | ||
if (result) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
executeWithArguments(instance, args, do_instrumentation) { | ||
let signature = this.findCorrectSignature(args), | ||
result; | ||
if (do_instrumentation) { | ||
this.doBeforeInstrumentation(instance, args); | ||
} | ||
if (!signature) { | ||
let super_method = this.wrapper_method.super; | ||
if (super_method) { | ||
if (super_method[TYPE_WRAPPER]) { | ||
signature = super_method[TYPES].findCorrectSignature(args); | ||
} | ||
} | ||
if (signature) { | ||
result = signature.execute(instance, args); | ||
} else { | ||
if (this.catchall) { | ||
result = this.catchall.apply(instance, args); | ||
} else if (super_method) { | ||
if (super_method[TYPE_WRAPPER]) { | ||
result = super_method[TYPES].executeWithArguments(instance, args, false); | ||
} else { | ||
result = super_method.apply(instance, args); | ||
} | ||
} else { | ||
throw new TypeError('Failed to find "' + this.name + '" method matching signature `' + argsToSignatureString(args) + '`'); | ||
} | ||
} | ||
} else { | ||
result = signature.execute(instance, args); | ||
} | ||
if (do_instrumentation) { | ||
this.doAfterInstrumentation(instance, args, result); | ||
} | ||
return result; | ||
} | ||
doSuper(instance, args) { | ||
let super_method = this.parent_context?.[this.name]; | ||
if (!super_method) { | ||
throw new Error('No super method is available'); | ||
} | ||
if (super_method[TYPE_WRAPPER]) { | ||
return super_method[TYPES].executeWithArguments(instance, args, false); | ||
} else { | ||
return super_method.apply(instance, args); | ||
} | ||
} | ||
} | ||
/** | ||
* Get/create the typed wrapper method | ||
@@ -656,3 +908,3 @@ * which will delegate calls to the correct signature | ||
* @since 0.7.25 | ||
* @version 0.8.1 | ||
* @version 0.8.9 | ||
* | ||
@@ -669,2 +921,3 @@ * @param {Object} context | ||
let descriptor = Object.getOwnPropertyDescriptor(context, name), | ||
super_types = false, | ||
current_method = descriptor?.value, | ||
@@ -677,2 +930,3 @@ wrapper_method, | ||
super_method = context[name]; | ||
super_types = super_method[TYPES]; | ||
} | ||
@@ -686,26 +940,16 @@ } else { | ||
if (!wrapper_method) { | ||
let types_by_length = { | ||
lengths: [], | ||
}; | ||
let types_collection = new TypesCollection(context, name); | ||
wrapper_method = Fn.create(name, function _doCorrectTypeMethod(...args) { | ||
return types_collection.executeWithArguments(this, args, true); | ||
}); | ||
let signature = findCorrectSignature(types_by_length, args); | ||
types_collection.wrapper_method = wrapper_method; | ||
if (!signature) { | ||
if (wrapper_method.super) { | ||
return wrapper_method.super.apply(this, args); | ||
} | ||
if (current_method) { | ||
types_collection.setCatchAll(current_method); | ||
} | ||
if (current_method) { | ||
return current_method.apply(this, args); | ||
} | ||
throw new TypeError('Failed to find "' + name + '" method matching signature `' + argsToSignatureString(args) + '`'); | ||
} | ||
return signature.execute(this, args); | ||
}); | ||
wrapper_method[TYPES] = types_by_length; | ||
wrapper_method[TYPES] = types_collection; | ||
wrapper_method[TYPE_WRAPPER] = true; | ||
@@ -723,3 +967,55 @@ | ||
function callFunctions() { | ||
} | ||
/** | ||
* Instrument an existing method | ||
* | ||
* @author Jelle De Loecker <jelle@elevenways.be> | ||
* @since 0.8.9 | ||
* @version 0.8.9 | ||
* | ||
* @param {Object} context Holder of the method | ||
* @param {String} method_name The name of the method to instrument | ||
* @param {Function} before Function to call before executing the main method | ||
* @param {Function} after Function to call after executing the main method | ||
*/ | ||
Blast.addMethodInstrumentation = function addMethodInstrumentation(context, method_name, before, after) { | ||
let wrapper = Blast.getSignatureWrapperMethod(context, method_name), | ||
types_collection = wrapper[TYPES]; | ||
if (before) { | ||
types_collection.addBeforeInstrumentation(before); | ||
} | ||
if (after) { | ||
types_collection.addAfterInstrumentation(after); | ||
} | ||
}; | ||
/** | ||
* Add a catchall method | ||
* | ||
* @author Jelle De Loecker <jelle@elevenways.be> | ||
* @since 0.8.9 | ||
* @version 0.8.9 | ||
* | ||
* @param {Object} context | ||
* @param {String} name | ||
* @param {Function} fnc | ||
*/ | ||
Blast.addCatchallMethod = function addCatchallMethod(context, name, fnc) { | ||
let wrapper = Blast.getSignatureWrapperMethod(context, name); | ||
let config = wrapper[TYPES]; | ||
config.setCatchAll(fnc); | ||
return fnc; | ||
}; | ||
/** | ||
* Add a typed method | ||
@@ -758,9 +1054,9 @@ * | ||
wrapper = Blast.getSignatureWrapperMethod(context, name), | ||
types_by_length = wrapper[TYPES], | ||
signatures = types_by_length[length]; | ||
types_collection = wrapper[TYPES], | ||
signatures = types_collection[length]; | ||
if (!signatures) { | ||
signatures = []; | ||
types_by_length[length] = signatures; | ||
types_by_length.lengths.push(length); | ||
types_collection[length] = signatures; | ||
types_collection.lengths.push(length); | ||
} else if (findDuplicateSignature(signatures, argument_types)) { | ||
@@ -770,3 +1066,3 @@ throw new Error('Tried to create a duplicate signature "' + name + '"'); | ||
let signature = new MethodSignature(argument_types, return_types, fnc); | ||
let signature = new MethodSignature(types_collection, argument_types, return_types, fnc); | ||
@@ -828,49 +1124,2 @@ signatures.push(signature); | ||
/** | ||
* Find the correct method for the given arguments | ||
* | ||
* @author Jelle De Loecker <jelle@elevenways.be> | ||
* @since 0.7.25 | ||
* @version 0.7.25 | ||
* | ||
* @param {Object} types_by_length | ||
* @param {Array} args | ||
* | ||
* @return {Object} | ||
*/ | ||
function findCorrectSignature(types_by_length, args) { | ||
let length = args.length, | ||
signatures = types_by_length[length], | ||
result; | ||
if (signatures) { | ||
result = testSignatures(signatures, args, length); | ||
} | ||
if (!result) { | ||
let test_length, | ||
i; | ||
for (i = 0; i < types_by_length.lengths.length; i++) { | ||
test_length = types_by_length.lengths[i]; | ||
if (test_length > length) { | ||
signatures = types_by_length[test_length]; | ||
if (signatures) { | ||
result = testSignatures(signatures, args, length); | ||
if (result) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
/** | ||
* Find the correct signature for the given arguments | ||
@@ -877,0 +1126,0 @@ * |
{ | ||
"name": "protoblast", | ||
"description": "Native object expansion library", | ||
"version": "0.8.8", | ||
"version": "0.8.9", | ||
"author": "Jelle De Loecker <jelle@elevenways.be>", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
801354
33105