From b2cf0a2553df3494e7f31439c06201a02d6a9df1 Mon Sep 17 00:00:00 2001 From: jeff wilde Date: Thu, 26 Jan 2017 14:04:52 -0700 Subject: [PATCH 1/6] allows memoization of circular structures --- .gitignore | 1 + src/index.js | 40 ++++++++++++++++++++++++++++++++++++++-- src/index.test.js | 18 ++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index be1e0e9..0bec2f2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ npm-debug.log .DS_Store coverage/ +.vscode \ No newline at end of file diff --git a/src/index.js b/src/index.js index b4c493c..6a4c6c8 100644 --- a/src/index.js +++ b/src/index.js @@ -89,8 +89,44 @@ function strategyDefault (fn, options) { // Serializer // -function serializerDefault (...args) { - return JSON.stringify(args) +function customReplacer(args) { + var cache = []; + const replacer = function (key, value) { + if (typeof value === 'object' && value !== null) { + if (cache.indexOf(value) !== -1) { + // Circular reference found, discard key + return; + } + // Store value in our collection + cache.push(value); + } + return value; + } + cache = null; // Enable garbage collection + return replacer; +} + +// +// Serializer +// + +function serializerDefault(...args) { + try { + // try the fastest way first. + return JSON.stringify(args) + } catch (error) { + if (error instanceof TypeError && + error.message === 'Converting circular structure to JSON') { + // if node + if (util && util.inspect) { + return JSON.stringify(util.inspect(args)) + } else { + return JSON.stringify(args, customReplacer) + } + } else { + throw e; // let others bubble up + } + } } // diff --git a/src/index.test.js b/src/index.test.js index b8cf728..6e26725 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -144,3 +144,21 @@ test('inject custom serializer', function () { expect(serializerMethodExecutionCount).toBe(2) }) + +test('memoize circular JSON', function () { + var circular = { + a: 'foo' + } + circular.b = circular + + function circularFunction(a) { + return a.a + } + + var memoizedCircularFunction = memoize(circularFunction) + + // Assertions + + expect(memoizedCircularFunction(circular)).toBe("foo") + expect(memoizedCircularFunction(circular)).toBe("foo") +}) From 754d27079164b79fe4ed80d5e603195fc91ffc0c Mon Sep 17 00:00:00 2001 From: jeff wilde Date: Thu, 26 Jan 2017 14:27:11 -0700 Subject: [PATCH 2/6] oops forgot require('util') --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index 6a4c6c8..aacb77b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +const util = require('util'); // // Main // From f5b5af1263500f5e10cf33cdf8f34c42b187b381 Mon Sep 17 00:00:00 2001 From: jeff wilde Date: Fri, 27 Jan 2017 09:39:39 -0700 Subject: [PATCH 3/6] node 4 compatible --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index aacb77b..cac019a 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -const util = require('util'); +const util = require('util') // // Main // From 8d8b9f5bf8cd128abdcb5842b32fe2a92d80af99 Mon Sep 17 00:00:00 2001 From: jeff wilde Date: Fri, 27 Jan 2017 09:39:53 -0700 Subject: [PATCH 4/6] node 4 compatible --- benchmark/cache/index.js | 1 + benchmark/index.js | 1 + benchmark/recursive-solo.js | 1 + benchmark/strategy/index.js | 1 + benchmark/strategy/partial-application.js | 11 +++--- src/delme.js | 16 +++++++++ src/index.js | 42 ++++++++++++++--------- 7 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 src/delme.js diff --git a/benchmark/cache/index.js b/benchmark/cache/index.js index 62c1e27..b94f6c8 100644 --- a/benchmark/cache/index.js +++ b/benchmark/cache/index.js @@ -1,3 +1,4 @@ +'use stict' const ora = require('ora') const Table = require('cli-table2') const debug = require('logdown')() diff --git a/benchmark/index.js b/benchmark/index.js index 362f9be..dd50a86 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -1,3 +1,4 @@ +'use stict' const ora = require('ora') const Table = require('cli-table2') const debug = require('logdown')() diff --git a/benchmark/recursive-solo.js b/benchmark/recursive-solo.js index 70baf4e..4eb37b9 100644 --- a/benchmark/recursive-solo.js +++ b/benchmark/recursive-solo.js @@ -1,3 +1,4 @@ +'use stict' const ora = require('ora') const logger = require('logdown')() const Table = require('cli-table2') diff --git a/benchmark/strategy/index.js b/benchmark/strategy/index.js index d948b4f..125a562 100644 --- a/benchmark/strategy/index.js +++ b/benchmark/strategy/index.js @@ -1,3 +1,4 @@ +'use stict' const ora = require('ora') const Table = require('cli-table2') const debug = require('logdown')() diff --git a/benchmark/strategy/partial-application.js b/benchmark/strategy/partial-application.js index 15db328..62e7050 100644 --- a/benchmark/strategy/partial-application.js +++ b/benchmark/strategy/partial-application.js @@ -22,7 +22,10 @@ function strategy (fn, options) { return cache.get(cacheKey) } - function variadic (fn, cache, serializer, ...args) { + function variadic (fn, cache, serializer) { + for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { + args[_key - 3] = arguments[_key]; + } var cacheKey = serializer(args) if (!cache.has(cacheKey)) { @@ -34,9 +37,9 @@ function strategy (fn, options) { return cache.get(cacheKey) } - var memoized = fn.length === 1 - ? monadic - : variadic + var memoized = fn.length === 1 ? + monadic : + variadic memoized = memoized.bind(this, fn, options.cache.create(), options.serializer) memoized.label = 'strategy: Partial application, cache: ' + options.cache.label + ', serializer: ' + options.serializer.label diff --git a/src/delme.js b/src/delme.js new file mode 100644 index 0000000..a4f8a98 --- /dev/null +++ b/src/delme.js @@ -0,0 +1,16 @@ +var memoize = require('../src') + +var circular = { + a: 'foo' +} +circular.b = circular + +function circularFunction (a) { + return a.a +} + +var memoizedCircularFunction = memoize(circularFunction) + +// Assertions +var one = memoizedCircularFunction(circular) +var two = memoizedCircularFunction(circular) diff --git a/src/index.js b/src/index.js index cac019a..864c9ab 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +'use stict' const util = require('util') // // Main @@ -58,7 +59,11 @@ function strategyDefault (fn, options) { return cache.get(cacheKey) } - function variadic (fn, cache, serializer, ...args) { + function variadic (fn, cache, serializerrgs) { + for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) { + args[_key - 3] = arguments[_key]; + } + var cacheKey = serializer(args) if (!cache.has(cacheKey)) { @@ -70,9 +75,9 @@ function strategyDefault (fn, options) { return cache.get(cacheKey) } - var memoized = fn.length === 1 - ? monadic - : variadic + var memoized = fn.length === 1 ? + monadic : + variadic memoized = memoized.bind( this, @@ -96,22 +101,25 @@ function customReplacer(args) { if (typeof value === 'object' && value !== null) { if (cache.indexOf(value) !== -1) { // Circular reference found, discard key - return; + return } // Store value in our collection - cache.push(value); + cache.push(value) } - return value; } - cache = null; // Enable garbage collection - return replacer; + cache = null // Enable garbage collection + return replacer } // // Serializer // -function serializerDefault(...args) { +function serializerDefault () { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key] + } + try { // try the fastest way first. return JSON.stringify(args) @@ -125,7 +133,7 @@ function serializerDefault(...args) { return JSON.stringify(args, customReplacer) } } else { - throw e; // let others bubble up + throw error // let others bubble up } } } @@ -135,27 +143,27 @@ function serializerDefault(...args) { // class ObjectWithoutPrototypeCache { - constructor () { + constructor() { this.cache = Object.create(null) } - has (key) { + has(key) { return (key in this.cache) } - get (key) { + get(key) { return this.cache[key] } - set (key, value) { + set(key, value) { this.cache[key] = value } - delete (key) { + delete(key) { delete this.cache[key] } } const cacheDefault = { create: () => new ObjectWithoutPrototypeCache() -} +} \ No newline at end of file From 0844a54b3191c7e66e75fdcabc4e76f3939b06fa Mon Sep 17 00:00:00 2001 From: jeff wilde Date: Fri, 27 Jan 2017 09:50:42 -0700 Subject: [PATCH 5/6] fixes typo --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 864c9ab..77b6b05 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -'use stict' +'use strict' const util = require('util') // // Main From 441c93555a2de35ee8b048c5be474e6f125004cb Mon Sep 17 00:00:00 2001 From: jeff wilde Date: Fri, 27 Jan 2017 10:05:34 -0700 Subject: [PATCH 6/6] node 4 --- src/index.js | 54 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/src/index.js b/src/index.js index 77b6b05..f400bf4 100644 --- a/src/index.js +++ b/src/index.js @@ -142,28 +142,44 @@ function serializerDefault () { // Cache // -class ObjectWithoutPrototypeCache { - constructor() { - this.cache = Object.create(null) - } +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); - has(key) { - return (key in this.cache) - } +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - get(key) { - return this.cache[key] - } +var ObjectWithoutPrototypeCache = function () { + function ObjectWithoutPrototypeCache() { + _classCallCheck(this, ObjectWithoutPrototypeCache); - set(key, value) { - this.cache[key] = value + this.cache = Object.create(null); } - delete(key) { - delete this.cache[key] - } -} + _createClass(ObjectWithoutPrototypeCache, [{ + key: "has", + value: function has(key) { + return key in this.cache; + } + }, { + key: "get", + value: function get(key) { + return this.cache[key]; + } + }, { + key: "set", + value: function set(key, value) { + this.cache[key] = value; + } + }, { + key: "delete", + value: function _delete(key) { + delete this.cache[key]; + } + }]); -const cacheDefault = { - create: () => new ObjectWithoutPrototypeCache() -} \ No newline at end of file + return ObjectWithoutPrototypeCache; +}(); + +var cacheDefault = { + create: function create() { + return new ObjectWithoutPrototypeCache(); + } +}; \ No newline at end of file