Skip to content

Commit

Permalink
util: support classes in util.deprecate()
Browse files Browse the repository at this point in the history
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: #7690
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Michaël Zasso <mic.besace@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
vdeturckheim authored and cjihrig committed Aug 4, 2016
1 parent e1643cc commit 320f433
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 4 deletions.
2 changes: 1 addition & 1 deletion doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
12 changes: 12 additions & 0 deletions test/fixtures/deprecated-userland-class.js
Original file line number Diff line number Diff line change
@@ -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);
19 changes: 19 additions & 0 deletions test/fixtures/deprecated-userland-subclass.js
Original file line number Diff line number Diff line change
@@ -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);
24 changes: 21 additions & 3 deletions test/sequential/test-deprecation-flags.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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));
});

0 comments on commit 320f433

Please sign in to comment.