You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

fast-typescript-memoize

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fast-typescript-memoize - npm Package Compare versions

Comparing version

to
1.2.1

120

dist/Memoize.js

@@ -10,8 +10,8 @@ "use strict";

const [hasher, options] = typeof a1 === "function" ? [a1, a2] : [undefined, a1];
return (_target, _propertyKey, descriptor) => {
return (_target, propName, descriptor) => {
if (typeof descriptor.value === "function") {
descriptor.value = buildNewMethod(descriptor.value, hasher, options);
descriptor.value = buildNewMethod(descriptor.value, propName, hasher, options);
}
else if (descriptor.get) {
descriptor.get = buildNewMethod(descriptor.get, hasher, options);
descriptor.get = buildNewMethod(descriptor.get, propName, hasher, options);
}

@@ -29,38 +29,80 @@ else {

*/
function buildNewMethod(origMethod, hasher, { clearOnReject, clearOnResolve } = {
function buildNewMethod(origMethod, propName, hasher, { clearOnReject, clearOnResolve } = {
clearOnReject: true,
clearOnResolve: false,
}) {
const identifier = ++counter;
// Depending on the arguments of the method we're memoizing, we use one of 3
// storages (with some code boilerplate for performance):
// - If the arguments hash (defaults to the 1st argument) is a JS OBJECT, we
// store the memoized value in a WeakMap keyed by that object. It allows JS
// GC to garbage collect that object since it's not retained in the internal
// memoized WeakMap. Motivation: if we lose the object, we obviously can't
// pass it to any method with `Memoize()`, and thus, we anyways won't be
// able to access the memoized value, so WeakMap is a perfect hit here.
// - If the arguments hash is of a PRIMITIVE TYPE, we store the memoized value
// in a regular Map. Primitive types (like strings, numbers etc.) can't be
// used as WeakMap keys for obvious reasons.
// - And lastly, if it's a NO-ARGUMENTS METHOD, we store the value in a hidden
// object property directly. This is the most frequent use case.
const propWeakName = `__memoized_weak_${propName.toString()}_${counter}`;
const propMapName = `__memoized_map_${propName.toString()}_${counter}`;
const propValName = `__memoized_val_${propName.toString()}_${counter}`;
counter++;
return function (...args) {
let value;
if (hasher || args.length > 0) {
const propMapName = `__memoized_map_${identifier}`;
// Get or create map
if (!this.hasOwnProperty(propMapName)) {
Object.defineProperty(this, propMapName, {
configurable: false,
enumerable: false,
writable: false,
value: new Map(),
});
}
const map = this[propMapName];
const hashKey = hasher ? hasher.apply(this, args) : args[0];
if (map.has(hashKey)) {
value = map.get(hashKey);
if (hashKey !== null && typeof hashKey === "object") {
// Arg (or hash) is an object: WeakMap.
if (!this.hasOwnProperty(propWeakName)) {
Object.defineProperty(this, propWeakName, {
configurable: false,
enumerable: false,
writable: false,
value: new WeakMap(),
});
}
const weak = this[propWeakName];
if (weak.has(hashKey)) {
value = weak.get(hashKey);
}
else {
value = origMethod.apply(this, args);
if (clearOnReject && value instanceof Promise) {
value = value.catch(deleteWeakKeyAndRethrow.bind(undefined, weak, hashKey));
}
if (clearOnResolve && value instanceof Promise) {
value = value.then(deleteWeakKeyAndReturn.bind(undefined, weak, hashKey));
}
weak.set(hashKey, value);
}
}
else {
value = origMethod.apply(this, args);
if (clearOnReject && value instanceof Promise) {
value = value.catch(deleteMapKeyAndRethrow.bind(undefined, map, hashKey));
// Arg (or hash) is a primitive type: Map.
if (!this.hasOwnProperty(propMapName)) {
Object.defineProperty(this, propMapName, {
configurable: false,
enumerable: false,
writable: false,
value: new Map(),
});
}
if (clearOnResolve && value instanceof Promise) {
value = value.then(deleteMapKeyAndReturn.bind(undefined, map, hashKey));
const map = this[propMapName];
if (map.has(hashKey)) {
value = map.get(hashKey);
}
map.set(hashKey, value);
else {
value = origMethod.apply(this, args);
if (clearOnReject && value instanceof Promise) {
value = value.catch(deleteMapKeyAndRethrow.bind(undefined, map, hashKey));
}
if (clearOnResolve && value instanceof Promise) {
value = value.then(deleteMapKeyAndReturn.bind(undefined, map, hashKey));
}
map.set(hashKey, value);
}
}
}
else {
const propValName = `__memoized_value_${identifier}`;
// No arg: plain object property.
if (this.hasOwnProperty(propValName)) {

@@ -88,6 +130,10 @@ value = this[propValName];

}
/**
* A helper function to just not use "=>" closures and thus control, which
* variables will be retained from garbage collection.
*/
//
// Below are helper functions to just not use "=>" closures and thus control,
// which variables will be retained from garbage collection.
//
function deleteWeakKeyAndRethrow(weak, key, e) {
weak.delete(key);
throw e;
}
function deleteMapKeyAndRethrow(map, key, e) {

@@ -97,6 +143,2 @@ map.delete(key);

}
/**
* A helper function to just not use "=>" closures and thus control, which
* variables will be retained from garbage collection.
*/
function deleteObjPropAndRethrow(obj, key, e) {

@@ -106,6 +148,6 @@ delete obj[key];

}
/**
* A helper function to just not use "=>" closures and thus control, which
* variables will be retained from garbage collection.
*/
function deleteWeakKeyAndReturn(weak, key, value) {
weak.delete(key);
return value;
}
function deleteMapKeyAndReturn(map, key, value) {

@@ -115,6 +157,2 @@ map.delete(key);

}
/**
* A helper function to just not use "=>" closures and thus control, which
* variables will be retained from garbage collection.
*/
function deleteObjPropAndReturn(obj, key, value) {

@@ -121,0 +159,0 @@ delete obj[key];

{
"name": "fast-typescript-memoize",
"version": "1.1.1",
"version": "1.2.1",
"description": "Fast memoization decorator and other helpers with 1st class support for Promises.",

@@ -5,0 +5,0 @@ "homepage": "https://github.com/dimikot/fast-typescript-memoize#readme",

@@ -30,2 +30,5 @@ # fast-typescript-memoize: fast memoization decorator and other helpers with 1st class support for Promises

4. Does not support any notion of expiration.
5. When the 1st argument of the method is an object (or when hasher handler
returns an object), it is not retained from GC, so you can memoize on object
args safely, without thinking about memory leaks.

@@ -49,2 +52,7 @@ ```ts

@Memoize()
method1obj(arg: object) {
return count++;
}
@Memoize((arg1, arg2) => `${arg1}#${arg2}`)

@@ -56,3 +64,3 @@ method2(arg1: string, arg2: number) {

@Memoize(function (arg1, arg2) { return `${this.some}:${arg1}#${arg2}`; })
method3(arg1: string, arg2: number) {
method2this(arg1: string, arg2: number) {
return count++;

@@ -77,2 +85,3 @@ }

const obj = new Class();
const arg = { my: 42 };

@@ -86,7 +95,10 @@ obj.method0(); // count is incremented

obj.method1obj(arg); // count is incremented, arg is not retained
obj.method1obj(arg); // count is NOT incremented
obj.method2("abc", 42); // count is incremented
obj.method2("abc", 42); // count is NOT incremented
obj.method3("abc", 42); // count is incremented (strongly typed `this`)
obj.method3("abc", 42); // count is NOT incremented
obj.method2this("abc", 42); // count is incremented (strongly typed `this`)
obj.method2this("abc", 42); // count is NOT incremented

@@ -93,0 +105,0 @@ await asyncMethod("ok"); // count is incremented

Sorry, the diff of this file is not supported yet