From bec56bcc14649b915ec673d7d551fd06998a8c9f Mon Sep 17 00:00:00 2001 From: vladimir Date: Tue, 12 Jul 2016 23:09:12 +0200 Subject: [PATCH] util: allow deprecate on classes Classes objects cannot be called without new. util.deprecate executes 'fn.apply'. Therefore, classes cannot be deprecated with util.deprecate. This uses 'new.target' to detect (when process is defined) calls to constructor to allow deprecate on class. --- doc/api/util.md | 2 +- lib/internal/util.js | 9 +++++++ test/fixtures/deprecated-userland-class.js | 12 ++++++++++ test/fixtures/deprecated-userland-subclass.js | 19 +++++++++++++++ test/sequential/test-deprecation-flags.js | 24 ++++++++++++++++--- 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/deprecated-userland-class.js create mode 100644 test/fixtures/deprecated-userland-subclass.js diff --git a/doc/api/util.md b/doc/api/util.md index a39d372c294d..e06cc8803cb1 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -46,7 +46,7 @@ environment variable. For example: `NODE_DEBUG=fs,net,tls`. ## util.deprecate(function, string) -The `util.deprecate()` method wraps the given `function` in such a way that +The `util.deprecate()` method wraps the given `function` or class in such a way that it is marked as deprecated. ```js diff --git a/lib/internal/util.js b/lib/internal/util.js index e3d9a66b3b2a..47027d16f4c9 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -60,9 +60,18 @@ exports._deprecate = function(fn, msg) { var warned = false; function deprecated() { warned = exports.printDeprecationMessage(msg, warned, deprecated); + if (new.target) { + return Reflect.construct(fn, arguments, new.target); + } return fn.apply(this, arguments); } + // the wrapper will keep the same prototype as fn to maintain prototype chain + Object.setPrototypeOf(deprecated, fn); + if (fn.prototype) { + Object.setPrototypeOf(deprecated.prototype, fn.prototype); + } + return deprecated; }; diff --git a/test/fixtures/deprecated-userland-class.js b/test/fixtures/deprecated-userland-class.js new file mode 100644 index 000000000000..867bbbf2aeea --- /dev/null +++ b/test/fixtures/deprecated-userland-class.js @@ -0,0 +1,12 @@ +const util = require('util'); +const assert = require('assert'); + +class deprecatedClass { +} + +const deprecated = util.deprecate(deprecatedClass, 'deprecatedClass is deprecated.'); + +const instance = new deprecated(); + +assert(instance instanceof deprecated); +assert(instance instanceof deprecatedClass); diff --git a/test/fixtures/deprecated-userland-subclass.js b/test/fixtures/deprecated-userland-subclass.js new file mode 100644 index 000000000000..2df374ad7d1b --- /dev/null +++ b/test/fixtures/deprecated-userland-subclass.js @@ -0,0 +1,19 @@ +const util = require('util'); +const assert = require('assert'); + +class deprecatedClass { +} + +const deprecated = util.deprecate(deprecatedClass, 'deprecatedClass is deprecated.'); + +class subclass extends deprecated { + constructor() { + super(); + } +} + +const instance = new subclass(); + +assert(instance instanceof subclass); +assert(instance instanceof deprecated); +assert(instance instanceof deprecatedClass); diff --git a/test/sequential/test-deprecation-flags.js b/test/sequential/test-deprecation-flags.js index bbb5ecb7002a..e9f5ee673d23 100644 --- a/test/sequential/test-deprecation-flags.js +++ b/test/sequential/test-deprecation-flags.js @@ -5,8 +5,14 @@ const execFile = require('child_process').execFile; const depmod = require.resolve(common.fixturesDir + '/deprecated.js'); const node = process.execPath; -const depUserland = - require.resolve(common.fixturesDir + '/deprecated-userland-function.js'); +const depUserlandFunction = + require.resolve(common.fixturesDir + '/deprecated-userland-function.js'); + +const depUserlandClass = + require.resolve(common.fixturesDir + '/deprecated-userland-class.js'); + +const depUserlandSubClass = + require.resolve(common.fixturesDir + '/deprecated-userland-subclass.js'); const normal = [depmod]; const noDep = ['--no-deprecation', depmod]; @@ -39,10 +45,22 @@ execFile(node, traceDep, function(er, stdout, stderr) { console.log('trace ok'); }); -execFile(node, [depUserland], function(er, stdout, stderr) { +execFile(node, [depUserlandFunction], function(er, stdout, stderr) { console.error('normal: testing deprecated userland function'); assert.equal(er, null); assert.equal(stdout, ''); assert(/deprecatedFunction is deprecated/.test(stderr)); console.error('normal: ok'); }); + +execFile(node, [depUserlandClass], function(er, stdout, stderr) { + assert.strictEqual(er, null); + assert.strictEqual(stdout, ''); + assert(/deprecatedClass is deprecated/.test(stderr)); +}); + +execFile(node, [depUserlandSubClass], function(er, stdout, stderr) { + assert.strictEqual(er, null); + assert.strictEqual(stdout, ''); + assert(/deprecatedClass is deprecated/.test(stderr)); +});