Skip to content
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

Closed
wants to merge 1 commit into from

Conversation

ZYSzys
Copy link
Member

@ZYSzys ZYSzys commented Apr 11, 2019

Ensure state has initialized when getting property from prototype.
On the other hand, it wouldn't throw TypeError when accessing
readableLength, readableHighWaterMark and e.g. from
stream.Readable.prototype.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • commit message follows commit guidelines

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot nodejs-github-bot added the stream Issues and PRs related to the stream subsystem. label Apr 11, 2019
@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot

This comment has been minimized.

@mscdex
Copy link
Contributor

mscdex commented Apr 11, 2019

This needs some tests.

@ZYSzys ZYSzys force-pushed the stream-prototype branch 2 times, most recently from fdaa3d4 to 7228614 Compare April 15, 2019 12:43
Copy link
Member

@BridgeAR BridgeAR left a 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);
Copy link
Member

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.

Copy link
Member Author

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:

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;
}
});

{
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();
}

Copy link
Member Author

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.

@mcollina
Copy link
Member

What is the case where those properties are not initialized? The state is allocated with the stream itself.

@ZYSzys
Copy link
Member Author

ZYSzys commented Apr 16, 2019

The simplest case I'm considering is to get the property from prototype directly instead of throw TypeError.

> stream.Readable.prototype.readableLength
Thrown:
TypeError: Cannot read property 'length' of undefined
    at Stream.get (_stream_readable.js:1082:32)
>

lib/_stream_duplex.js Outdated Show resolved Hide resolved
lib/_stream_readable.js Outdated Show resolved Hide resolved
lib/_stream_writable.js Outdated Show resolved Hide resolved
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`.
Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ZYSzys ZYSzys added the review wanted PRs that need reviews. label Apr 18, 2019
@nodejs-github-bot

This comment has been minimized.

@ZYSzys ZYSzys requested a review from BridgeAR April 21, 2019 04:50
@nodejs-github-bot

This comment has been minimized.

@nodejs-github-bot
Copy link
Collaborator

Copy link
Member

@BridgeAR BridgeAR left a 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.

@ZYSzys
Copy link
Member Author

ZYSzys commented Apr 26, 2019

There should not be a point to access these properties before the class is instantiated.

I think someone maybe have a choice to get some information(some properties' default value) from prototype before they instantiate the stream. E.g. getting the default value of highWaterMark from Readable.prototype.readableHighWaterMark before new Readable().

@ZYSzys
Copy link
Member Author

ZYSzys commented May 1, 2019

@BridgeAR FYI, it seems that all of our public api's prototype(e.g. events.prototype, net.prototype) properties can be accessed without throwing error, though the default value may be undefined.

So I think maybe this is a point for consistence. Are you ok if I land this ?

@BridgeAR
Copy link
Member

BridgeAR commented May 1, 2019

@ZYSzys IMO that's more like a implementation detail. Did you actually run into this anywhere in real code?

@ZYSzys
Copy link
Member Author

ZYSzys commented May 4, 2019

@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 })

@BridgeAR
Copy link
Member

BridgeAR commented May 4, 2019

@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.

@ZYSzys
Copy link
Member Author

ZYSzys commented May 6, 2019

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 TypeError now...
And maybe we can expose the default value to developers for references in this way.

Wouldn't the default value better than the confused error here at present ?

@BridgeAR
Copy link
Member

BridgeAR commented May 6, 2019

@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.

@BridgeAR
Copy link
Member

BridgeAR commented May 6, 2019

@lpinca @mafintosh @addaleax PTAL. It would be good to have some people who work more frequently on streams giving their opinion in this case.

@lpinca
Copy link
Member

lpinca commented May 14, 2019

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.
I'm -0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review wanted PRs that need reviews. stream Issues and PRs related to the stream subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants