-
-
Notifications
You must be signed in to change notification settings - Fork 33.9k
util: allow deprecate on classes #7690
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
Conversation
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. |
lib/internal/util.js
Outdated
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. |
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? |
doc/api/util.md
Outdated
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(); // throwsTo 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 |
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 :) |
| 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.