135 lines
3.8 KiB
JavaScript
135 lines
3.8 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
var customError = require("es5-ext/error/custom")
|
||
|
, defineLength = require("es5-ext/function/_define-length")
|
||
|
, partial = require("es5-ext/function/#/partial")
|
||
|
, copy = require("es5-ext/object/copy")
|
||
|
, normalizeOpts = require("es5-ext/object/normalize-options")
|
||
|
, callable = require("es5-ext/object/valid-callable")
|
||
|
, d = require("d")
|
||
|
, WeakMap = require("es6-weak-map")
|
||
|
, resolveLength = require("./resolve-length")
|
||
|
, extensions = require("./registered-extensions")
|
||
|
, resolveResolve = require("./resolve-resolve")
|
||
|
, resolveNormalize = require("./resolve-normalize");
|
||
|
|
||
|
var slice = Array.prototype.slice, defineProperties = Object.defineProperties;
|
||
|
|
||
|
module.exports = function (memoize) {
|
||
|
return function (fn/*, options*/) {
|
||
|
var map, length, options = normalizeOpts(arguments[1]), memoized, resolve, normalizer;
|
||
|
|
||
|
callable(fn);
|
||
|
|
||
|
// Do not memoize already memoized function
|
||
|
if (hasOwnProperty.call(fn, "__memoized__") && !options.force) return fn;
|
||
|
|
||
|
length = resolveLength(options.length, fn.length, options.async && extensions.async);
|
||
|
options.length = length ? length - 1 : 0;
|
||
|
map = new WeakMap();
|
||
|
|
||
|
if (options.resolvers) resolve = resolveResolve(options.resolvers);
|
||
|
if (options.normalizer) normalizer = resolveNormalize(options.normalizer);
|
||
|
|
||
|
if (
|
||
|
length === 1 &&
|
||
|
!normalizer &&
|
||
|
!options.async &&
|
||
|
!options.dispose &&
|
||
|
!options.maxAge &&
|
||
|
!options.max &&
|
||
|
!options.refCounter
|
||
|
) {
|
||
|
return defineProperties(
|
||
|
function (obj) {
|
||
|
var result, args = arguments;
|
||
|
if (resolve) args = resolve(args);
|
||
|
obj = args[0];
|
||
|
if (map.has(obj)) return map.get(obj);
|
||
|
result = fn.apply(this, args);
|
||
|
if (map.has(obj)) {
|
||
|
throw customError("Circular invocation", "CIRCULAR_INVOCATION");
|
||
|
}
|
||
|
map.set(obj, result);
|
||
|
return result;
|
||
|
},
|
||
|
{
|
||
|
__memoized__: d(true),
|
||
|
delete: d(function (obj) {
|
||
|
if (resolve) obj = resolve(arguments)[0];
|
||
|
return map.delete(obj);
|
||
|
})
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
memoized = defineProperties(
|
||
|
defineLength(function (obj) {
|
||
|
var memoizer, args = arguments;
|
||
|
if (resolve) {
|
||
|
args = resolve(args);
|
||
|
obj = args[0];
|
||
|
}
|
||
|
memoizer = map.get(obj);
|
||
|
if (!memoizer) {
|
||
|
if (normalizer) {
|
||
|
options = copy(options);
|
||
|
options.normalizer = copy(normalizer);
|
||
|
options.normalizer.get = partial.call(options.normalizer.get, obj);
|
||
|
options.normalizer.set = partial.call(options.normalizer.set, obj);
|
||
|
if (options.normalizer.delete) {
|
||
|
options.normalizer.delete = partial.call(
|
||
|
options.normalizer.delete, obj
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
map.set(obj, memoizer = memoize(partial.call(fn, obj), options));
|
||
|
}
|
||
|
return memoizer.apply(this, slice.call(args, 1));
|
||
|
}, length),
|
||
|
{
|
||
|
__memoized__: d(true),
|
||
|
delete: d(
|
||
|
defineLength(function (obj) {
|
||
|
var memoizer, args = arguments;
|
||
|
if (resolve) {
|
||
|
args = resolve(args);
|
||
|
obj = args[0];
|
||
|
}
|
||
|
memoizer = map.get(obj);
|
||
|
if (!memoizer) return;
|
||
|
memoizer.delete.apply(this, slice.call(args, 1));
|
||
|
}, length)
|
||
|
)
|
||
|
}
|
||
|
);
|
||
|
if (!options.refCounter) return memoized;
|
||
|
defineProperties(memoized, {
|
||
|
deleteRef: d(
|
||
|
defineLength(function (obj) {
|
||
|
var memoizer, args = arguments;
|
||
|
if (resolve) {
|
||
|
args = resolve(args);
|
||
|
obj = args[0];
|
||
|
}
|
||
|
memoizer = map.get(obj);
|
||
|
if (!memoizer) return null;
|
||
|
return memoizer.deleteRef.apply(this, slice.call(args, 1));
|
||
|
}, length)
|
||
|
),
|
||
|
getRefCount: d(
|
||
|
defineLength(function (obj) {
|
||
|
var memoizer, args = arguments;
|
||
|
if (resolve) {
|
||
|
args = resolve(args);
|
||
|
obj = args[0];
|
||
|
}
|
||
|
memoizer = map.get(obj);
|
||
|
if (!memoizer) return 0;
|
||
|
return memoizer.getRefCount.apply(this, slice.call(args, 1));
|
||
|
}, length)
|
||
|
)
|
||
|
});
|
||
|
return memoized;
|
||
|
};
|
||
|
};
|