-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
util: allow deprecate on classes #7690
Conversation
class deprecatedClass { | ||
} | ||
|
||
var depreciated = util.deprecate(deprecatedClass, 'deprecatedClass is deprecated.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be deprecated
? Also, const
please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack. Sorry, French-English mixin in my brain
@cjihrig Thanks a lot for the review ! |
@vdeturckheim no need to change the existing test. Thanks though. |
@@ -60,6 +60,9 @@ exports._deprecate = function(fn, msg) { | |||
var warned = false; | |||
function deprecated() { | |||
warned = exports.printDeprecationMessage(msg, warned, deprecated); | |||
if (new.target) { | |||
return new (Function.prototype.bind.apply(fn, arguments)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's important to make the deprecated class
correctly subclassable. Can you change this line to return Reflect.construct(fn, arguments, new.target)
and add a test that verifies that ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
Seems fair to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a test should be added with a subclass as well to make confirm the functionality
Seems reasonable to me, I bet core will need it at some point in the future. |
const deprecated = util.deprecate(deprecatedClass, 'deprecatedClass is deprecated.'); | ||
|
||
class subclass extends deprecated { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this blank line.
Can you remove all of the additional commits from this PR? |
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the backticks around "function" here, refer to the named parameter. If that's the case, I'm not sure it makes sense to put "class" in backticks too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
One small comment, but LGTM. |
Looking at it again, I am not sure it's doing what we expect it to. Example: class A {
hello() {
console.log('world');
}
}
const DA = util.deprecate(A, 'A is deprecated');
var da = new DA(); // logs deprecation message
da.hello(); // throws To fix this, we need to pass |
Maybe we should change the prototype chain of the returned function to include Something like: Object.setPrototypeOf(deprecated, A);
Object.setPrototypeOf(deprecated.prototype, A.prototype); |
@targos your solution seems to work: const deprecate = function(fn, msg) {
var warned = false;
function deprecated() {
warned = console.log(msg);
if (new.target) {
return Reflect.construct(fn, arguments, new.target);
}
return fn.apply(this, arguments);
}
Object.setPrototypeOf(deprecated, fn);
Object.setPrototypeOf(deprecated.prototype, fn.prototype);
return deprecated;
};
class A{};
const DA = deprecate(A, 'A is deprecated');
class B extends DA{
constructor() {
super();
}
}
const b = new B();
console.log(b instanceof B); // true
console.log(b instanceof A); // true
console.log(b instanceof DA); // true |
|
||
const deprecated = util.deprecate(deprecatedClass, 'deprecatedClass is deprecated.'); | ||
|
||
new deprecated(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a few instanceof
checks ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
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.
I added a check on the presence of |
Oh yes, I forgot about arrow functions. new CI: https://ci.nodejs.org/job/node-test-pull-request/3296/ |
@targos Is there anything else I need to do before merge ? :) |
@vdeturckheim No, everything's fine. Waiting for other collaborators to review :) |
@@ -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)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, an easier way to test for the deprecation warning would be to listen for warning
events...
process.on('warning', (warning) => {
if (warning.name === 'DeprecationWarning') {
// ...
}
});
But that doesn't need to be fixed here at all.. just pointing it out
LGTM. Good stuff. |
It's been a while since the last CI run: https://ci.nodejs.org/job/node-test-pull-request/3489/ |
@jasnell I don't really understand why the build fails now :/ |
@vdeturckheim That’s an unrelated build problem, nothing to worry about. New CI attempt: https://ci.nodejs.org/job/node-test-commit/4369/ |
Looks like more unrelated failures. |
:( Is there anything I can/should do here ? |
Probably no… the last two CI runs together are green, though, and one of the flaky tests from the last run is just being fixed. |
Ok, thanks :) |
Landed in 320f433. Thanks, and sorry for the delay. |
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>
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>
this is not landing cleanly on v4.x, would someone be willing to backport? |
this will need to be re-implemented without please feel free to submit a backport if you are able to |
Checklist
Affected core subsystem(s)
Description of change
Classes objects cannot be called without new. util.deprecate executes 'fn.apply'. 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.