From 295d1ea016c4d66322a9c2fd6540fea3df19b8a6 Mon Sep 17 00:00:00 2001 From: vladimir Date: Tue, 12 Jul 2016 23:09:12 +0200 Subject: [PATCH] util: support classes in util.deprecate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Classes cannot be instantiated without new, but util.deprecate() uses Function.prototype.apply(). This commit uses new.target to detect constructor calls, allowing classes to be deprecated. PR-URL: https://github.com/nodejs/node/pull/7690 Reviewed-By: Colin Ihrig Reviewed-By: Michaƫl Zasso Reviewed-By: James M Snell --- 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 34c6839333..6f011929a2 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 6f2af0efb4..d231b41a2f 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -57,9 +57,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 0000000000..867bbbf2ae --- /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 0000000000..2df374ad7d --- /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 bbb5ecb700..e9f5ee673d 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)); +});