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

Streams: tests for readable byte stream fixes #28557

Merged
merged 24 commits into from
May 26, 2021

Conversation

MattiasBuelens
Copy link
Contributor

@MattiasBuelens MattiasBuelens commented Apr 16, 2021

See whatwg/streams#1123 for the accompanying spec change.

To do:

  • Test whether passing a view on a WebAssembly.Memory's buffer to byobReader.read(), byteStreamController.enqueue() and byobRequest.respondWithNewView() throws an error.

Comment on lines +180 to +190
function transferArrayBufferView(view) {
const noopByteStream = new ReadableStream({
type: 'bytes',
pull(c) {
c.byobRequest.respond(c.byobRequest.view.byteLength);
c.close();
}
});
const reader = noopByteStream.getReader({ mode: 'byob' });
return reader.read(view).then((result) => result.value);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was the "easiest" solution I could come up with to transfer an ArrayBufferView.

We could also use postMessage() like we do with createTransferredReadableStream, but then we also need to change the test setup for the reference implementation... and I can't be bothered. 😛

Copy link
Member

Choose a reason for hiding this comment

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

enqueue-with-detached-buffer.window.js also uses postMessage, hmm. It might be worth changing https://github.com/whatwg/streams/blob/0ff6d4508b58acce28386696b2854b2a1570d205/reference-implementation/run-web-platform-tests.js#L43-L56 to just stick the "transferred" flag onto the transferred argument.

Copy link
Contributor Author

@MattiasBuelens MattiasBuelens May 17, 2021

Choose a reason for hiding this comment

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

You mean replacing window.postMessage(message, targetOrigin, transfer) to add our special "transferred" flag to every ArrayBuffer in transfer? It'll be a bit complicated:

  • We need to export the TransferArrayBuffer helper in index.js, so that we can access it after loading the bundle. We have to use the same helper, so that it'll use the exact same isFakeDetached symbol.
  • We have to transfer any ArrayBuffers inside transfer and replace all references to those buffers inside message. Sure, we can assume that for our tests, the message will always be an ArrayBufferView (and not some deeply nested JavaScript object). We'll then have to re-construct that view onto the transferred buffer.

Sure, I guess I can try it. Not sure if it'll be pretty though. 😛 (It's times like these where I wish tc39/proposal-resizablearraybuffer were already a reality... 😞)

Copy link
Member

Choose a reason for hiding this comment

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

To be clear, I'm not sure it's worth it; just an idea to throw out there!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All right, so this would work:

const windowPostMessage = window.postMessage.bind(window);
window.postMessage = function (message, targetOrigin, transfer) {
  if (targetOrigin === '*' && transfer.length === 1 && transfer[0] instanceof window.ArrayBuffer) {
    const originalBuffer = transfer[0];
    const transferredBuffer = window.whatwgStreamsInternals.TransferArrayBuffer(originalBuffer);
    if (window.ArrayBuffer.isView(message) && message.buffer === originalBuffer) {
      message = new message.constructor(transferredBuffer, message.byteOffset, message.byteLength);
    }
    transfer = [transferredBuffer];
  }
  windowPostMessage(message, targetOrigin, transfer);
};

window.whatwgStreamsInternals would be defined in lib/index.js as:

// ...
const { TransferArrayBuffer } = require('./abstract-ops/ecmascript.js');
window.whatwgStreamsInternals = { TransferArrayBuffer };

Then we can change transferArrayBufferView inside the test utilities:

function transferArrayBufferView(view) {
  return new Promise((resolve) => {
    self.addEventListener('message', (event) => resolve(event.data), { once: true });
    self.postMessage(view, '*', [view.buffer]);
  });
}

Do we like this better? I think it looks nicer in WPT, but the runner becomes a little bit more complicated. That said, many different browser developers need to read and understand the WPT tests, so I suppose we should favor simplicity of the tests over simplicity of our test runner.

Copy link
Contributor

Choose a reason for hiding this comment

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

I prefer the version that doesn't involve us monkey patching postMessage in the reference implementation. The weirdness of the byte-stream-based transferArrayBufferView() implementation is nicely encapsulated, so it's not hurting anyone.

Copy link
Member

Choose a reason for hiding this comment

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

I slightly prefer the monkeypatch, but I'll defer to @ricea.

Copy link
Member

@domenic domenic left a comment

Choose a reason for hiding this comment

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

This seems like it's not testing the wasm memory case? Should we add those? It probably causes some reference implementation headaches, but seems like important coverage since those kind of un-transferrable buffers are so different from already-transferred ones.

Comment on lines +180 to +190
function transferArrayBufferView(view) {
const noopByteStream = new ReadableStream({
type: 'bytes',
pull(c) {
c.byobRequest.respond(c.byobRequest.view.byteLength);
c.close();
}
});
const reader = noopByteStream.getReader({ mode: 'byob' });
return reader.read(view).then((result) => result.value);
}
Copy link
Member

Choose a reason for hiding this comment

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

enqueue-with-detached-buffer.window.js also uses postMessage, hmm. It might be worth changing https://github.com/whatwg/streams/blob/0ff6d4508b58acce28386696b2854b2a1570d205/reference-implementation/run-web-platform-tests.js#L43-L56 to just stick the "transferred" flag onto the transferred argument.

@MattiasBuelens
Copy link
Contributor Author

Indeed, I didn't write tests for WebAssembly memories yet because I don't know how to make those work in the reference implementation... 😕

Is there an easy way to test whether a given ArrayBuffer belongs to a WebAssembly.Memory instance? I guess we could install a Proxy on WebAssembly.Memory.prototype.buffer and put all returned buffers inside a WeakSet. But then the reference implementation's TransferArrayBuffer (or CanTransferArrayBuffer) needs to know about that WeakSet somehow? 🤷

Alternatively, we put all the WebAssembly-related tests in a separate file and filter them out for the reference implementation's test runs?

@domenic
Copy link
Member

domenic commented May 17, 2021

Yeah, separate file is probably the best way to go.

@MattiasBuelens MattiasBuelens marked this pull request as ready for review May 19, 2021 11:37
Copy link
Contributor

@ricea ricea left a comment

Choose a reason for hiding this comment

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

Great! lgtm.

@domenic domenic merged commit 7b29ee3 into web-platform-tests:master May 26, 2021
@MattiasBuelens MattiasBuelens deleted the byte-stream-fixes branch May 26, 2021 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants