Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions lib/internal/webstreams/readablestream.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const {
extractHighWaterMark,
extractSizeAlgorithm,
lazyTransfer,
isViewedArrayBufferDetached,
isDetachedBuffer,
isBrandCheck,
resetQueue,
setPromiseHandled,
Expand Down Expand Up @@ -658,11 +658,13 @@ class ReadableStreamBYOBRequest {
const viewBuffer = ArrayBufferViewGetBuffer(view);
const viewBufferByteLength = ArrayBufferGetByteLength(viewBuffer);

if (viewByteLength === 0 || viewBufferByteLength === 0) {
throw new ERR_INVALID_STATE.TypeError(
'View ArrayBuffer is zero-length or detached');
if (isDetachedBuffer(viewBuffer)) {
throw new ERR_INVALID_STATE.TypeError('Viewed ArrayBuffer is detached');
}

assert(viewByteLength > 0);
assert(viewBufferByteLength > 0);

readableByteStreamControllerRespond(controller, bytesWritten);
}

Expand All @@ -681,7 +683,7 @@ class ReadableStreamBYOBRequest {
'This BYOB request has been invalidated');
}

if (isViewedArrayBufferDetached(view)) {
if (isDetachedBuffer(ArrayBufferViewGetBuffer(view))) {
throw new ERR_INVALID_STATE.TypeError('Viewed ArrayBuffer is detached');
}

Expand Down Expand Up @@ -894,14 +896,23 @@ class ReadableStreamBYOBReader {
],
view));
}

const viewByteLength = ArrayBufferViewGetByteLength(view);
const viewBuffer = ArrayBufferViewGetBuffer(view);
const viewBufferByteLength = ArrayBufferGetByteLength(viewBuffer);

if (viewByteLength === 0 || viewBufferByteLength === 0) {
return PromiseReject(
new ERR_INVALID_STATE.TypeError(
'View ArrayBuffer is zero-length or detached'));
'View or Viewed ArrayBuffer is zero-length',
),
);
}

if (isDetachedBuffer(viewBuffer)) {
return PromiseReject(
new ERR_INVALID_STATE.TypeError('Viewed ArrayBuffer is detached'),
);
}
// Supposed to assert here that the view's buffer is not
// detached, but there's no API available to use to check that.
Expand Down Expand Up @@ -2298,11 +2309,10 @@ function readableByteStreamControllerEnqueue(
if (pendingPullIntos.length) {
const firstPendingPullInto = pendingPullIntos[0];

const pendingBufferByteLength =
ArrayBufferGetByteLength(firstPendingPullInto.buffer);
if (pendingBufferByteLength === 0) {
if (isDetachedBuffer(firstPendingPullInto.buffer)) {
throw new ERR_INVALID_STATE.TypeError(
'Destination ArrayBuffer is zero-length or detached');
'Destination ArrayBuffer is detached',
);
}

firstPendingPullInto.buffer =
Expand Down
21 changes: 6 additions & 15 deletions lib/internal/webstreams/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,24 +129,15 @@ function transferArrayBuffer(buffer) {
return res;
}

function isArrayBufferDetached(buffer) {
if (ArrayBufferGetByteLength(buffer) === 0) {
try {
new Uint8Array(buffer);
} catch {
return true;
}
function isDetachedBuffer(buffer) {
try {
new Uint8Array(buffer);
} catch {
return true;
}
return false;
}

function isViewedArrayBufferDetached(view) {
return (
ArrayBufferViewGetByteLength(view) === 0 &&
isArrayBufferDetached(ArrayBufferViewGetBuffer(view))
);
}

function dequeueValue(controller) {
assert(controller[kState].queue !== undefined);
assert(controller[kState].queueTotalSize !== undefined);
Expand Down Expand Up @@ -243,8 +234,8 @@ module.exports = {
extractSizeAlgorithm,
lazyTransfer,
isBrandCheck,
isDetachedBuffer,
isPromisePending,
isViewedArrayBufferDetached,
peekQueueValue,
resetQueue,
setPromiseHandled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,48 @@ let pass = 0;
reader.read(new Uint8Array([4, 5, 6]));
}

{
const stream = new ReadableStream({
start(c) {
c.enqueue(new Uint8Array([1, 2, 3]));
},
type: 'bytes',
});
const reader = stream.getReader({ mode: 'byob' });
const view = new Uint8Array();
reader
.read(view)
.then(common.mustNotCall())
.catch(
common.mustCall(
common.expectsError({
code: 'ERR_INVALID_STATE',
name: 'TypeError',
}),
),
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
reader
.read(view)
.then(common.mustNotCall())
.catch(
common.mustCall(
common.expectsError({
code: 'ERR_INVALID_STATE',
name: 'TypeError',
}),
),
);
assert.rejects(
reader.read(view),
{
code: 'ERR_INVALID_STATE',
name: 'TypeError',
}
).then(common.mustCall());

}

{
const stream = new ReadableStream({
start(c) {
c.enqueue(new Uint8Array([1, 2, 3]));
},
type: 'bytes',
});
const reader = stream.getReader({ mode: 'byob' });
const view = new Uint8Array(new ArrayBuffer(10), 0, 0);
reader
.read(view)
.then(common.mustNotCall())
.catch(
common.mustCall(
common.expectsError({
code: 'ERR_INVALID_STATE',
name: 'TypeError',
}),
),
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
reader
.read(view)
.then(common.mustNotCall())
.catch(
common.mustCall(
common.expectsError({
code: 'ERR_INVALID_STATE',
name: 'TypeError',
}),
),
);
assert.rejects(
reader.read(view),
{
code: 'ERR_INVALID_STATE',
name: 'TypeError',
}
).then(common.mustCall());

}

process.on('exit', () => assert.strictEqual(pass, 2));