From 7723fa7b85104ea7d22b9d82efb9b92635164229 Mon Sep 17 00:00:00 2001 From: Agop Shirinian Date: Fri, 26 Jan 2018 14:31:54 -0500 Subject: [PATCH 1/8] Replace new Function() usage with a regular function and preserve arity manually. --- index.js | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/index.js b/index.js index f244765..de2306f 100644 --- a/index.js +++ b/index.js @@ -65,20 +65,6 @@ function convertDataDescriptorToAccessor (obj, prop, message) { return descriptor } -/** - * Create arguments string to keep arity. - */ - -function createArgumentsString (arity) { - var str = '' - - for (var i = 0; i < arity; i++) { - str += ', arg' + i - } - - return str.substr(2) -} - /** * Create stack string from stack. */ @@ -398,19 +384,24 @@ function wrapfunction (fn, message) { throw new TypeError('argument fn must be a function') } - var args = createArgumentsString(fn.length) + var deprecate = this var stack = getStack() var site = callSiteLocation(stack[1]) site.name = fn.name - // eslint-disable-next-line no-new-func - var deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', - '"use strict"\n' + - 'return function (' + args + ') {' + - 'log.call(deprecate, message, site)\n' + - 'return fn.apply(this, arguments)\n' + - '}')(fn, log, this, message, site) + var deprecatedfn = function () { + log.call(deprecate, message, site) + return fn.apply(this, arguments) + } + + // Preserve fn's arity. + Object.defineProperty(deprecatedfn, 'length', { + configurable: true, + enumerable: false, + value: fn.length, + writable: false + }) return deprecatedfn } From 71100de496dc7dfe51bf961c3aacde5a63946dae Mon Sep 17 00:00:00 2001 From: Agop Shirinian Date: Fri, 26 Jan 2018 15:28:02 -0500 Subject: [PATCH 2/8] Add fallback for older versions of Node.js that can't redefine a function's length. --- index.js | 51 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index de2306f..6272adf 100644 --- a/index.js +++ b/index.js @@ -65,6 +65,20 @@ function convertDataDescriptorToAccessor (obj, prop, message) { return descriptor } +/** + * Create arguments string to keep arity. + */ + +function createArgumentsString (arity) { + var str = '' + + for (var i = 0; i < arity; i++) { + str += ', arg' + i + } + + return str.substr(2) +} + /** * Create stack string from stack. */ @@ -390,18 +404,33 @@ function wrapfunction (fn, message) { site.name = fn.name - var deprecatedfn = function () { - log.call(deprecate, message, site) - return fn.apply(this, arguments) - } + var deprecatedfn + + try { + // Node.js 3.3+ allows us to redefine a function's length property. + // We use this ability to preserve fn's arity without eval. + deprecatedfn = function () { + log.call(deprecate, message, site) + return fn.apply(this, arguments) + } - // Preserve fn's arity. - Object.defineProperty(deprecatedfn, 'length', { - configurable: true, - enumerable: false, - value: fn.length, - writable: false - }) + Object.defineProperty(deprecatedfn, 'length', { + configurable: true, + enumerable: false, + value: fn.length, + writable: false + }) + } catch (e) { + // If that fails, we use eval to manually construct a function with + // the correct arity. + var args = createArgumentsString(fn.length) + deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', + '"use strict"\n' + + 'return function (' + args + ') {' + + 'log.call(deprecate, message, site)\n' + + 'return fn.apply(this, arguments)\n' + + '}')(fn, log, deprecate, message, site) + } return deprecatedfn } From b22e5acfa89a138af72e39495de7f005d4980d60 Mon Sep 17 00:00:00 2001 From: Agop Shirinian Date: Fri, 26 Jan 2018 15:31:12 -0500 Subject: [PATCH 3/8] Add eslint comment for new Function() usage. --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 6272adf..cd03ec7 100644 --- a/index.js +++ b/index.js @@ -424,6 +424,7 @@ function wrapfunction (fn, message) { // If that fails, we use eval to manually construct a function with // the correct arity. var args = createArgumentsString(fn.length) + // eslint-disable-next-line no-new-func deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', '"use strict"\n' + 'return function (' + args + ') {' + From b7721797db50449df48f53664ef4c939c20e6754 Mon Sep 17 00:00:00 2001 From: Agop Shirinian Date: Fri, 26 Jan 2018 15:39:40 -0500 Subject: [PATCH 4/8] Remove trailing spaces. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index cd03ec7..84e8492 100644 --- a/index.js +++ b/index.js @@ -405,7 +405,7 @@ function wrapfunction (fn, message) { site.name = fn.name var deprecatedfn - + try { // Node.js 3.3+ allows us to redefine a function's length property. // We use this ability to preserve fn's arity without eval. From 67200de654d9139208d579d88f02c02fc42a17ac Mon Sep 17 00:00:00 2001 From: Agop Shirinian Date: Mon, 29 Jan 2018 15:46:33 -0500 Subject: [PATCH 5/8] Move feature detection logic to lib/compat. --- index.js | 46 +-------------------- lib/compat/index.js | 4 ++ lib/compat/wrap-function-inner.js | 69 +++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 44 deletions(-) create mode 100644 lib/compat/wrap-function-inner.js diff --git a/index.js b/index.js index 84e8492..a3240b3 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ */ var eventListenerCount = require('./lib/compat').eventListenerCount +var wrapFunctionInner = require('./lib/compat').wrapFunctionInner var relative = require('path').relative /** @@ -65,20 +66,6 @@ function convertDataDescriptorToAccessor (obj, prop, message) { return descriptor } -/** - * Create arguments string to keep arity. - */ - -function createArgumentsString (arity) { - var str = '' - - for (var i = 0; i < arity; i++) { - str += ', arg' + i - } - - return str.substr(2) -} - /** * Create stack string from stack. */ @@ -404,36 +391,7 @@ function wrapfunction (fn, message) { site.name = fn.name - var deprecatedfn - - try { - // Node.js 3.3+ allows us to redefine a function's length property. - // We use this ability to preserve fn's arity without eval. - deprecatedfn = function () { - log.call(deprecate, message, site) - return fn.apply(this, arguments) - } - - Object.defineProperty(deprecatedfn, 'length', { - configurable: true, - enumerable: false, - value: fn.length, - writable: false - }) - } catch (e) { - // If that fails, we use eval to manually construct a function with - // the correct arity. - var args = createArgumentsString(fn.length) - // eslint-disable-next-line no-new-func - deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', - '"use strict"\n' + - 'return function (' + args + ') {' + - 'log.call(deprecate, message, site)\n' + - 'return fn.apply(this, arguments)\n' + - '}')(fn, log, deprecate, message, site) - } - - return deprecatedfn + return wrapFunctionInner(fn, log, deprecate, message, site) } /** diff --git a/lib/compat/index.js b/lib/compat/index.js index 181550c..0936b57 100644 --- a/lib/compat/index.js +++ b/lib/compat/index.js @@ -22,6 +22,10 @@ lazyProperty(module.exports, 'eventListenerCount', function eventListenerCount ( return EventEmitter.listenerCount || require('./event-listener-count') }) +lazyProperty(module.exports, 'wrapFunctionInner', function wrapFunctionInner () { + return require('./wrap-function-inner') +}) + /** * Define a lazy property. */ diff --git a/lib/compat/wrap-function-inner.js b/lib/compat/wrap-function-inner.js new file mode 100644 index 0000000..48eef2f --- /dev/null +++ b/lib/compat/wrap-function-inner.js @@ -0,0 +1,69 @@ +function detectCanUseDefinePropertyOnFunctionLength() { + function detectfn () {} + + try { + // Node.js 3.3+. + Object.defineProperty(detectfn, 'length', { + configurable: true, + enumerable: false, + value: 1, + writable: false + }) + } catch (err) { + // Likely Node.js 2.5 or older. + } + + return detectfn.length === 1 +} + +// Perform the detection only once. +var canUseDefinePropertyOnFunctionLength = + detectCanUseDefinePropertyOnFunctionLength() + +/** + * Create arguments string to keep arity. + */ + +function createArgumentsString (arity) { + var str = '' + + for (var i = 0; i < arity; i++) { + str += ', arg' + i + } + + return str.substr(2) +} + +function wrapFunctionInner (fn, log, deprecate, message, site) { + var deprecatedfn + + if (canUseDefinePropertyOnFunctionLength) { + // Preserve fn's arity without eval(). + deprecatedfn = function () { + log.call(deprecate, message, site) + return fn.apply(this, arguments) + } + + Object.defineProperty(deprecatedfn, 'length', { + configurable: true, + enumerable: false, + value: fn.length, + writable: false + }) + } else { + // If unsupported, use eval to manually construct a function with the + // correct arity. + var args = createArgumentsString(fn.length) + // eslint-disable-next-line no-new-func + deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', + '"use strict"\n' + + 'return function (' + args + ') {' + + 'log.call(deprecate, message, site)\n' + + 'return fn.apply(this, arguments)\n' + + '}')(fn, log, deprecate, message, site) + } + + return deprecatedfn +} + +module.exports = wrapFunctionInner From 82a04f9cff734d357c73e574452e8b457bcfbeda Mon Sep 17 00:00:00 2001 From: Agop Shirinian Date: Mon, 29 Jan 2018 15:59:51 -0500 Subject: [PATCH 6/8] Fix lint warnings. --- lib/compat/wrap-function-inner.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compat/wrap-function-inner.js b/lib/compat/wrap-function-inner.js index 48eef2f..667bda8 100644 --- a/lib/compat/wrap-function-inner.js +++ b/lib/compat/wrap-function-inner.js @@ -1,12 +1,12 @@ -function detectCanUseDefinePropertyOnFunctionLength() { - function detectfn () {} +function detectCanUseDefinePropertyOnFunctionLength () { + function detectfn () {} try { // Node.js 3.3+. Object.defineProperty(detectfn, 'length', { configurable: true, enumerable: false, - value: 1, + value: 1, writable: false }) } catch (err) { From 8e2a6dce80c96f5d47d85319d7c56747dfb22419 Mon Sep 17 00:00:00 2001 From: Agop Shirinian Date: Mon, 5 Feb 2018 12:03:04 -0500 Subject: [PATCH 7/8] Clean wrapFunction compatibility layer. --- lib/compat/wrap-function-inner.js | 61 ++++++++++++++----------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/lib/compat/wrap-function-inner.js b/lib/compat/wrap-function-inner.js index 667bda8..583102a 100644 --- a/lib/compat/wrap-function-inner.js +++ b/lib/compat/wrap-function-inner.js @@ -1,4 +1,4 @@ -function detectCanUseDefinePropertyOnFunctionLength () { +function canUseDefinePropertyOnFunctionLength () { function detectfn () {} try { @@ -16,10 +16,6 @@ function detectCanUseDefinePropertyOnFunctionLength () { return detectfn.length === 1 } -// Perform the detection only once. -var canUseDefinePropertyOnFunctionLength = - detectCanUseDefinePropertyOnFunctionLength() - /** * Create arguments string to keep arity. */ @@ -34,36 +30,35 @@ function createArgumentsString (arity) { return str.substr(2) } -function wrapFunctionInner (fn, log, deprecate, message, site) { - var deprecatedfn - - if (canUseDefinePropertyOnFunctionLength) { - // Preserve fn's arity without eval(). - deprecatedfn = function () { - log.call(deprecate, message, site) - return fn.apply(this, arguments) - } - - Object.defineProperty(deprecatedfn, 'length', { - configurable: true, - enumerable: false, - value: fn.length, - writable: false - }) - } else { - // If unsupported, use eval to manually construct a function with the - // correct arity. - var args = createArgumentsString(fn.length) - // eslint-disable-next-line no-new-func - deprecatedfn = new Function('fn', 'log', 'deprecate', 'message', 'site', - '"use strict"\n' + - 'return function (' + args + ') {' + - 'log.call(deprecate, message, site)\n' + - 'return fn.apply(this, arguments)\n' + - '}')(fn, log, deprecate, message, site) +function wrapFunctionWithDefineProperty (fn, log, deprecate, message, site) { + var deprecatedfn = function () { + log.call(deprecate, message, site) + return fn.apply(this, arguments) } + // Preserve fn's arity. + Object.defineProperty(deprecatedfn, 'length', { + configurable: true, + enumerable: false, + value: fn.length, + writable: false + }) + return deprecatedfn } -module.exports = wrapFunctionInner +function wrapFunctionWithEval (fn, log, deprecate, message, site) { + // Preserve fn's arity by manually constructing an arguments string and + // eval'ing it into a new function. + var args = createArgumentsString(fn.length) + // eslint-disable-next-line no-new-func + return new Function('fn', 'log', 'deprecate', 'message', 'site', + '"use strict"\n' + + 'return function (' + args + ') {' + + 'log.call(deprecate, message, site)\n' + + 'return fn.apply(this, arguments)\n' + + '}')(fn, log, deprecate, message, site) +} + +module.exports = canUseDefinePropertyOnFunctionLength() + ? wrapFunctionWithDefineProperty : wrapFunctionWithEval From 564ca96f03bc733921346efca37f9f1678ef2762 Mon Sep 17 00:00:00 2001 From: Agop Shirinian Date: Wed, 7 Feb 2018 14:34:58 -0500 Subject: [PATCH 8/8] Add copyright comment block and use strict. --- lib/compat/wrap-function-inner.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/compat/wrap-function-inner.js b/lib/compat/wrap-function-inner.js index 583102a..16ed457 100644 --- a/lib/compat/wrap-function-inner.js +++ b/lib/compat/wrap-function-inner.js @@ -1,3 +1,11 @@ +/*! + * depd + * Copyright(c) 2014-2018 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + function canUseDefinePropertyOnFunctionLength () { function detectfn () {}