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
stream: ensure state existed when getting property #27190
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This needs some tests. |
fdaa3d4
to
7228614
Compare
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.
This does not really seem necessary to me.
@nodejs/streams PTAL
assert.strictEqual(this.readableLength, 0); | ||
assert.strictEqual(this.writableHighWaterMark, undefined); | ||
assert.strictEqual(this.writableLength, 0); | ||
Duplex.call(this); |
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.
The call to super should always be done before accessing this
.
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.
Honestly speaking, I learned this from destroyed
method tests:
Lines 182 to 204 in 53e0f63
Object.defineProperty(Readable.prototype, 'destroyed', { | |
// Making it explicit this property is not enumerable | |
// because otherwise some prototype manipulation in | |
// userland will fail | |
enumerable: false, | |
get() { | |
if (this._readableState === undefined) { | |
return false; | |
} | |
return this._readableState.destroyed; | |
}, | |
set(value) { | |
// We ignore the value if the stream | |
// has not been initialized yet | |
if (!this._readableState) { | |
return; | |
} | |
// Backward compatibility, the user is explicitly | |
// managing destroyed | |
this._readableState.destroyed = value; | |
} | |
}); |
node/test/parallel/test-stream-readable-destroy.js
Lines 155 to 166 in 53e0f63
{ | |
function MyReadable() { | |
assert.strictEqual(this.destroyed, false); | |
this.destroyed = false; | |
Readable.call(this); | |
} | |
Object.setPrototypeOf(MyReadable.prototype, Readable.prototype); | |
Object.setPrototypeOf(MyReadable, Readable); | |
new MyReadable(); | |
} |
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.
@BridgeAR So should I update the test ? Any advice would be appreciated.
What is the case where those properties are not initialized? The state is allocated with the stream itself. |
The simplest case I'm considering is to get the property from
|
Ensure state has initialized when getting property from prototype. On the other hand, it wouldn't throw TypeError when accessing readableLength, readableHighWaterMark from stream.Readable.prototype. Same for `Duplex` and `Writable`.
7228614
to
bdbd323
Compare
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.
LGTM
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
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 am -0 on this, since I don't think we have to guard against something like this. There should not be a point to access these properties before the class is instantiated. The error itself might not be that helpful but AFAIC it's just something unsupported. If at all, I would probably throw an error that the class has to be instantiated first.
I think someone maybe have a choice to get some information(some properties' default value) from |
@BridgeAR FYI, it seems that all of our public api's prototype(e.g. So I think maybe this is a point for consistence. Are you ok if I land this ? |
@ZYSzys IMO that's more like a implementation detail. Did you actually run into this anywhere in real code? |
@BridgeAR Maybe things like: const { Readable } = require('stream');
// Get the default value of our `readableHighWaterMark`,
// so that we can instantiate our readable stream due to this value.
// For some references ?
const defaultHighWaterMark = Readable.prototype.readableHighWaterMark;
// Twice than the default value
const rs = new Readable({ highWaterMark: defaultHighWaterMark * 2 }) |
@ZYSzys I don't think a maybe warrants this change. Your example also seems unreliable: most defaults are not set on the prototype but often in the constructor itself. |
Yep 🤔👍... But at least it wouldn't throw the confused Wouldn't the default value better than the confused error here at present ? |
@ZYSzys did you run into this anywhere in real code? I personally would not bother and I doubt that this is something people really run into. We could theoretically throw an error that accessing the property is undefined outside of the instance but to me it just adds checks for the default case that are won't help people in real life. At least that's my point of view. |
@lpinca @mafintosh @addaleax PTAL. It would be good to have some people who work more frequently on streams giving their opinion in this case. |
I kind of agree with Ruben. I think this actually masks user errors for example when they forget to call the parent constructor. A thrown error is better is this case. |
Ensure state has initialized when getting property from prototype.
On the other hand, it wouldn't throw
TypeError
when accessingreadableLength
,readableHighWaterMark
and e.g. fromstream.Readable.prototype
.Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes