Skip to content

Instantly share code, notes, and snippets.

@jorendorff
Created November 13, 2018 14:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jorendorff/caac74098713ceea170907f862f3a78a to your computer and use it in GitHub Desktop.
Save jorendorff/caac74098713ceea170907f862f3a78a to your computer and use it in GitHub Desktop.
changeset: 511721:b9b50b3dec73
tag: tip
parent: 511719:8584c117d229
user: Jason Orendorff <jorendorff@mozilla.com>
date: Mon Nov 12 18:08:38 2018 -0600
files: js/public/Stream.h js/src/builtin/Stream.cpp js/src/builtin/Stream.h js/src/jsapi-tests/testReadableStream.cpp js/src/vm/Runtime.cpp js/src/vm/Runtime.h
description:
No bug yet - Abstract base class for external underlying sources. r?baku,jwalden
diff --git a/js/public/Stream.h b/js/public/Stream.h
--- a/js/public/Stream.h
+++ b/js/public/Stream.h
@@ -16,32 +16,31 @@
*
* Embeddings can create ReadableStreams that read from custom C++ data
* sources. Such streams are always byte streams: the chunks they produce are
* typed arrays (and they will support ReadableStreamBYOBReader once we have
* it).
*
* When creating an "external readable stream" using
* JS::NewReadableExternalSourceStreamObject, an underlying source can be
- * passed to be stored on the stream. The underlying source is treated as an
- * opaque void* pointer by the JS engine: it's purely meant as a reference to
- * be used by the embedding to identify whatever actual source it uses to
- * supply data for the stream.
+ * passed to be stored on the stream. The underlying source is ???treated as an
+ * ???opaque void*??? pointer by the JS engine: it's purely meant as a
+ * reference to be used by the embedding to identify whatever actual source it
+ * uses to supply data for the stream.
*
* External readable streams are optimized to allow the embedding to interact
* with them with a minimum of overhead: chunks aren't enqueued as individual
* typed arrays; instead, the embedding only updates the amount of data
* available using ReadableStreamUpdateDataAvailableFromSource. When JS
- * requests data from a reader, WriteIntoReadRequestBufferCallback is invoked,
- * asking the embedding to write data directly into the buffer we're about to
- * hand to JS.
+ * requests data from a reader, writeIntoReadRequestBuffer is invoked, asking the
+ * embedding to write data directly into the buffer we're about to hand to JS.
*
* Additionally, ReadableStreamGetExternalUnderlyingSource can be used to get
- * the void* pointer to the underlying source. This locks the stream until it
- * is released again using JS::ReadableStreamReleaseExternalUnderlyingSource.
+ * the pointer to the underlying source. This locks the stream until it is
+ * released again using JS::ReadableStreamReleaseExternalUnderlyingSource.
*
* Embeddings can use this to optimize away the JS `ReadableStream` overhead
* when an embedding-defined C++ stream is passed to an embedding-defined C++
* consumer. For example, consider a ServiceWorker piping a `fetch` Response
* body to a TextDecoder. Instead of copying chunks of data into JS typed array
* buffers and creating a Promise per chunk, only to immediately resolve the
* Promises and read the data out again, the embedding can directly feed the
* incoming data to the TextDecoder.
@@ -54,117 +53,93 @@
#include "js/TypeDecls.h"
namespace JS {
/**
* ## Readable stream callbacks
*
- * Compartment safety: All callbacks (except Finalize) receive `cx` and
+ * Compartment safety: All methods (except Finalize) receive `cx` and
* `stream` arguments. SpiderMonkey enters the realm of the stream object
- * before invoking these callbacks, so `stream` is never a wrapper. Other
+ * before invoking these methods, so `stream` is never a wrapper. Other
* arguments may be wrappers.
*/
+class ReadableStreamUnderlyingSource {
+ public:
+ virtual ~ReadableStreamUnderlyingSource() {}
-/**
- * Invoked whenever a reader desires more data from a ReadableStream's
- * embedding-provided underlying source.
- *
- * The given |desiredSize| is the absolute size, not a delta from the previous
- * desired size.
- */
-typedef void
-(* RequestReadableStreamDataCallback)(JSContext* cx, HandleObject stream,
- void* underlyingSource, size_t desiredSize);
+ /**
+ * Invoked whenever a reader desires more data from a ReadableStream's
+ * embedding-provided underlying source.
+ *
+ * The given |desiredSize| is the absolute size, not a delta from the previous
+ * desired size.
+ */
+ virtual void requestData(JSContext* cx, HandleObject stream, size_t desiredSize) = 0;
-/**
- * Invoked to cause the embedding to fill the given |buffer| with data from
- * the given embedding-provided underlying source.
- *
- * This can only happen after the embedding has updated the amount of data
- * available using JS::ReadableStreamUpdateDataAvailableFromSource. If at
- * least one read request is pending when
- * JS::ReadableStreamUpdateDataAvailableFromSource is called,
- * the WriteIntoReadRequestBufferCallback is invoked immediately from under
- * the call to JS::WriteIntoReadRequestBufferCallback. If not, it is invoked
- * if and when a new read request is made.
- *
- * Note: This callback *must not cause GC*, because that could potentially
- * invalidate the |buffer| pointer.
- */
-typedef void
-(* WriteIntoReadRequestBufferCallback)(JSContext* cx, HandleObject stream,
- void* underlyingSource, void* buffer, size_t length,
- size_t* bytesWritten);
+ /**
+ * Invoked to cause the embedding to fill the given |buffer| with data from
+ * the given embedding-provided underlying source.
+ *
+ * This can only happen after the embedding has updated the amount of data
+ * available using JS::ReadableStreamUpdateDataAvailableFromSource. If at
+ * least one read request is pending when
+ * JS::ReadableStreamUpdateDataAvailableFromSource is called, the
+ * writeIntoReadRequestBuffer method is invoked immediately from under the
+ * call to JS::ReadableStreamUpdateDataAvailableFromSource. If not, it is
+ * invoked if and when a new read request is made.
+ *
+ * Note: This method *must not cause GC*, because that could potentially
+ * invalidate the |buffer| pointer.
+ */
+ virtual void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream,
+ void* buffer, size_t length,
+ size_t* bytesWritten) = 0;
-/**
- * Invoked in reaction to the ReadableStream being canceled to allow the
- * embedding to free the underlying source.
- *
- * This is equivalent to calling |cancel| on non-external underlying sources
- * provided to the ReadableStream constructor in JavaScript.
- *
- * The given |reason| is the JS::Value that was passed as an argument to
- * ReadableStream#cancel().
- *
- * The returned JS::Value will be used to resolve the Promise returned by
- * ReadableStream#cancel().
- */
-typedef Value
-(* CancelReadableStreamCallback)(JSContext* cx, HandleObject stream,
- void* underlyingSource, HandleValue reason);
-
-/**
- * Invoked in reaction to a ReadableStream with an embedding-provided
- * underlying source being closed.
- */
-typedef void
-(* ReadableStreamClosedCallback)(JSContext* cx, HandleObject stream, void* underlyingSource);
+ /**
+ * Invoked in reaction to the ReadableStream being canceled to allow the
+ * embedding to free the underlying source.
+ *
+ * This is equivalent to calling |cancel| on non-external underlying sources
+ * provided to the ReadableStream constructor in JavaScript.
+ *
+ * The given |reason| is the JS::Value that was passed as an argument to
+ * ReadableStream#cancel().
+ *
+ * The returned JS::Value will be used to resolve the Promise returned by
+ * ReadableStream#cancel().
+ */
+ virtual Value cancel(JSContext* cx, HandleObject stream, HandleValue reason) = 0;
-/**
- * Invoked in reaction to a ReadableStream with an embedding-provided
- * underlying source being errored with the
- * given reason.
- */
-typedef void
-(* ReadableStreamErroredCallback)(JSContext* cx, HandleObject stream, void* underlyingSource,
- HandleValue reason);
+ /**
+ * Invoked in reaction to a ReadableStream with an embedding-provided
+ * underlying source being closed.
+ */
+ virtual void onClosed(JSContext* cx, HandleObject stream) = 0;
+
+ /**
+ * Invoked in reaction to a ReadableStream with an embedding-provided
+ * underlying source being errored with the given reason.
+ */
+ virtual void onErrored(JSContext* cx, HandleObject stream, HandleValue reason) = 0;
-/**
- * Invoked in reaction to a ReadableStream with an embedding-provided
- * underlying source being finalized. Only the underlying source is passed
- * as an argument, while the ReadableStream itself is not to prevent the
- * embedding from operating on a JSObject that might not be in a valid state
- * anymore.
- *
- * Note: the ReadableStream might be finalized on a background thread. That
- * means this callback might be invoked from an arbitrary thread, which the
- * embedding must be able to handle.
- */
-typedef void
-(* ReadableStreamFinalizeCallback)(void* underlyingSource);
-
-/**
- * Sets runtime-wide callbacks to use for interacting with embedding-provided
- * hooks for operating on ReadableStream instances.
- *
- * See the documentation for the individual callback types for details.
- */
-extern JS_PUBLIC_API(void)
-SetReadableStreamCallbacks(JSContext* cx,
- RequestReadableStreamDataCallback dataRequestCallback,
- WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
- CancelReadableStreamCallback cancelCallback,
- ReadableStreamClosedCallback closedCallback,
- ReadableStreamErroredCallback erroredCallback,
- ReadableStreamFinalizeCallback finalizeCallback);
-
-extern JS_PUBLIC_API(bool)
-HasReadableStreamCallbacks(JSContext* cx);
+ /**
+ * Invoked in reaction to a ReadableStream with an embedding-provided
+ * underlying source being finalized. Only the underlying source is passed
+ * as an argument, while the ReadableStream itself is not to prevent the
+ * embedding from operating on a JSObject that might not be in a valid state
+ * anymore.
+ *
+ * Note: the ReadableStream might be finalized on a background thread. That
+ * means this method might be invoked from an arbitrary thread, which the
+ * embedding must be able to handle.
+ */
+ virtual void finalize() = 0;
+};
/**
* Returns a new instance of the ReadableStream builtin class in the current
* compartment, configured as a default stream.
* If a |proto| is passed, that gets set as the instance's [[Prototype]]
* instead of the original value of |ReadableStream.prototype|.
*/
extern JS_PUBLIC_API(JSObject*)
@@ -174,29 +149,28 @@ NewReadableDefaultStreamObject(JSContext
/**
* Returns a new instance of the ReadableStream builtin class in the current
* compartment, with the right slot layout. If a |proto| is passed, that gets
* set as the instance's [[Prototype]] instead of the original value of
* |ReadableStream.prototype|.
*
* The instance is optimized for operating as a byte stream backed by an
- * embedding-provided underlying source, using the callbacks set via
- * |JS::SetReadableStreamCallbacks|.
+ * embedding-provided underlying source, using the virtual methods of
+ * |underlyingSource| as callbacks.
*
* Note: the embedding is responsible for ensuring that the pointer to the
* underlying source stays valid as long as the stream can be read from.
* The underlying source can be freed if the tree is canceled or errored.
* It can also be freed if the stream is destroyed. The embedding is notified
- * of that using ReadableStreamFinalizeCallback.
- *
- * Note: |underlyingSource| must have an even address.
+ * of that using the finalize() method.
*/
extern JS_PUBLIC_API(JSObject*)
-NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
+NewReadableExternalSourceStreamObject(JSContext* cx,
+ ReadableStreamUnderlyingSource* underlyingSource,
HandleObject proto = nullptr);
/**
* Returns the embedding-provided underlying source of the given |stream|.
*
* Can be used to optimize operations if both the underlying source and the
* intended sink are embedding-provided. In that case it might be
* preferrable to pipe data directly from source to sink without interacting
@@ -214,17 +188,18 @@ NewReadableExternalSourceStreamObject(JS
* have a Promise to resolve/reject, which a reader provides.
*
* Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
* for one.
*
* Asserts that the stream has an embedding-provided underlying source.
*/
extern JS_PUBLIC_API(bool)
-ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject stream, void** source);
+ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject stream,
+ ReadableStreamUnderlyingSource** source);
/**
* Releases the embedding-provided underlying source of the given |stream|,
* returning the stream into an unlocked state.
*
* Asserts that the stream was locked through
* ReadableStreamGetExternalUnderlyingSource.
*
@@ -237,17 +212,17 @@ extern JS_PUBLIC_API(bool)
ReadableStreamReleaseExternalUnderlyingSource(JSContext* cx, HandleObject stream);
/**
* Update the amount of data available at the underlying source of the given
* |stream|.
*
* Can only be used for streams with an embedding-provided underlying source.
* The JS engine will use the given value to satisfy read requests for the
- * stream by invoking the JS::WriteIntoReadRequestBuffer callback.
+ * stream by invoking the writeIntoReadRequestBuffer method.
*
* Asserts that |stream| is a ReadableStream object or an unwrappable wrapper
* for one.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, HandleObject stream,
uint32_t availableData);
diff --git a/js/src/builtin/Stream.cpp b/js/src/builtin/Stream.cpp
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -562,29 +562,30 @@ ReadableStream::createDefaultStream(JSCo
return nullptr;
}
stream->setController(controller);
return stream;
}
static MOZ_MUST_USE ReadableByteStreamController*
CreateExternalReadableByteStreamController(JSContext* cx, Handle<ReadableStream*> stream,
- void* underlyingSource);
+ JS::ReadableStreamUnderlyingSource* source);
ReadableStream*
-ReadableStream::createExternalSourceStream(JSContext* cx, void* underlyingSource,
+ReadableStream::createExternalSourceStream(JSContext* cx,
+ JS::ReadableStreamUnderlyingSource* source,
HandleObject proto /* = nullptr */)
{
Rooted<ReadableStream*> stream(cx, createStream(cx, proto));
if (!stream) {
return nullptr;
}
Rooted<ReadableStreamController*> controller(cx);
- controller = CreateExternalReadableByteStreamController(cx, stream, underlyingSource);
+ controller = CreateExternalReadableByteStreamController(cx, stream, source);
if (!controller) {
return nullptr;
}
stream->setController(controller);
return stream;
}
@@ -1449,24 +1450,21 @@ ReadableStreamCloseInternal(JSContext* c
RootedObject closedPromise(cx, unwrappedReader->closedPromise());
if (!cx->compartment()->wrap(cx, &closedPromise)) {
return false;
}
if (!ResolvePromise(cx, closedPromise, UndefinedHandleValue)) {
return false;
}
- if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource &&
- cx->runtime()->readableStreamClosedCallback)
- {
+ if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource) {
// Make sure we're in the stream's compartment.
AutoRealm ar(cx, unwrappedStream);
- ReadableStreamController* controller = unwrappedStream->controller();
- void* source = controller->underlyingSource().toPrivate();
- cx->runtime()->readableStreamClosedCallback(cx, unwrappedStream, source);
+ JS::ReadableStreamUnderlyingSource* source = unwrappedStream->controller()->externalSource();
+ source->onClosed(cx, unwrappedStream);
}
return true;
}
/**
* Streams spec, 3.4.6. ReadableStreamError ( stream, e )
*/
@@ -1540,32 +1538,29 @@ ReadableStreamErrorInternal(JSContext* c
RootedObject closedPromise(cx, unwrappedReader->closedPromise());
if (!cx->compartment()->wrap(cx, &closedPromise)) {
return false;
}
if (!RejectPromise(cx, closedPromise, e)) {
return false;
}
- if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource &&
- cx->runtime()->readableStreamErroredCallback)
+ if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource)
{
// Make sure we're in the stream's compartment.
AutoRealm ar(cx, unwrappedStream);
- ReadableStreamController* controller = unwrappedStream->controller();
- void* source = controller->underlyingSource().toPrivate();
+ JS::ReadableStreamUnderlyingSource* source = unwrappedStream->controller()->externalSource();
// Ensure that the embedding doesn't have to deal with
// mixed-compartment arguments to the callback.
RootedValue error(cx, e);
if (!cx->compartment()->wrap(cx, &error)) {
return false;
}
-
- cx->runtime()->readableStreamErroredCallback(cx, unwrappedStream, source, error);
+ source->onErrored(cx, unwrappedStream, error);
}
return true;
}
/**
* Streams spec, 3.4.7. ReadableStreamFulfillReadIntoRequest( stream, chunk, done )
* Streams spec, 3.4.8. ReadableStreamFulfillReadRequest ( stream, chunk, done )
@@ -2556,25 +2551,25 @@ ReadableStreamControllerCancelSteps(JSCo
return ReadableStreamTee_Cancel(cx, unwrappedteeState, unwrappedDefaultController,
reason);
}
if (unwrappedController->hasExternalSource()) {
RootedValue rval(cx);
{
AutoRealm ar(cx, unwrappedController);
+ JS::ReadableStreamUnderlyingSource* source = unwrappedController->externalSource();
Rooted<ReadableStream*> stream(cx, unwrappedController->stream());
- void* source = unwrappedUnderlyingSource.toPrivate();
RootedValue wrappedReason(cx, reason);
if (!cx->compartment()->wrap(cx, &wrappedReason)) {
return nullptr;
}
cx->check(stream, wrappedReason);
- rval = cx->runtime()->readableStreamCancelCallback(cx, stream, source, wrappedReason);
+ rval = source->cancel(cx, stream, wrappedReason);
}
if (!cx->compartment()->wrap(cx, &rval)) {
return nullptr;
}
return PromiseObject::unforgeableResolve(cx, rval);
}
@@ -2778,20 +2773,20 @@ ReadableStreamControllerCallPullIfNeeded
MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is<TeeState>(),
"tee streams and controllers are always same-compartment with the TeeState object");
Rooted<TeeState*> unwrappedTeeState(cx,
&unwrappedUnderlyingSource.toObject().as<TeeState>());
pullPromise = ReadableStreamTee_Pull(cx, unwrappedTeeState);
} else if (unwrappedController->hasExternalSource()) {
{
AutoRealm ar(cx, unwrappedController);
+ JS::ReadableStreamUnderlyingSource* source = unwrappedController->externalSource();
Rooted<ReadableStream*> stream(cx, unwrappedController->stream());
- void* source = unwrappedUnderlyingSource.toPrivate();
double desiredSize = ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController);
- cx->runtime()->readableStreamDataRequestCallback(cx, stream, source, desiredSize);
+ source->requestData(cx, stream, desiredSize);
}
pullPromise = PromiseObject::unforgeableResolve(cx, UndefinedHandleValue);
} else {
RootedValue underlyingSource(cx, unwrappedUnderlyingSource);
if (!cx->compartment()->wrap(cx, &underlyingSource)) {
return false;
}
pullPromise = PromiseInvokeOrNoop(cx, underlyingSource, cx->names().pull, controllerVal);
@@ -3183,29 +3178,29 @@ ReadableByteStreamController::constructo
/**
* Version of the ReadableByteStreamConstructor that's specialized for
* handling external, embedding-provided, underlying sources.
*/
static MOZ_MUST_USE ReadableByteStreamController*
CreateExternalReadableByteStreamController(JSContext* cx,
Handle<ReadableStream*> stream,
- void* underlyingSource)
+ JS::ReadableStreamUnderlyingSource* source)
{
Rooted<ReadableByteStreamController*> controller(cx,
NewBuiltinClassInstance<ReadableByteStreamController>(cx));
if (!controller) {
return nullptr;
}
// Step 3: Set this.[[controlledReadableStream]] to stream.
controller->setStream(stream);
// Step 4: Set this.[[underlyingByteSource]] to underlyingByteSource.
- controller->setUnderlyingSource(PrivateValue(underlyingSource));
+ controller->setUnderlyingSource(PrivateValue(source));
// Step 5: Set this.[[pullAgain]], and this.[[pulling]] to false.
controller->setFlags(ReadableStreamController::Flag_ExternalSource);
// Step 6: Perform ! ReadableByteStreamControllerClearPendingPullIntos(this).
// Omitted.
// Step 7: Perform ! ResetQueue(this).
@@ -3271,18 +3266,17 @@ ReadableByteStreamControllerFinalize(Fre
if (controller.getFixedSlot(ReadableStreamController::Slot_Flags).isUndefined()) {
return;
}
if (!controller.hasExternalSource()) {
return;
}
- void* underlyingSource = controller.underlyingSource().toPrivate();
- obj->runtimeFromAnyThread()->readableStreamFinalizeCallback(underlyingSource);
+ controller.externalSource()->finalize();
}
static const ClassOps ReadableByteStreamControllerClassOps = {
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* enumerate */
nullptr, /* newEnumerate */
nullptr, /* resolve */
@@ -3328,34 +3322,33 @@ ReadableByteStreamControllerPullSteps(JS
double queueTotalSize = unwrappedController->queueTotalSize();
if (queueTotalSize > 0) {
// Step 3.a: Assert: ! ReadableStreamGetNumReadRequests(_stream_) is 0.
MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0);
RootedObject view(cx);
if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource) {
- void* underlyingSource = unwrappedController->underlyingSource().toPrivate();
+ JS::ReadableStreamUnderlyingSource* source = unwrappedController->externalSource();
view = JS_NewUint8Array(cx, queueTotalSize);
if (!view) {
return nullptr;
}
size_t bytesWritten;
{
AutoRealm ar(cx, unwrappedStream);
JS::AutoSuppressGCAnalysis suppressGC(cx);
JS::AutoCheckCannotGC noGC;
bool dummy;
void* buffer = JS_GetArrayBufferViewData(view, &dummy, noGC);
- auto cb = cx->runtime()->readableStreamWriteIntoReadRequestCallback;
- MOZ_ASSERT(cb);
- cb(cx, unwrappedStream, underlyingSource, buffer, queueTotalSize, &bytesWritten);
+ source->writeIntoReadRequestBuffer(cx, unwrappedStream, buffer, queueTotalSize,
+ &bytesWritten);
}
queueTotalSize = queueTotalSize - bytesWritten;
} else {
// Step 3.b: Let entry be the first element of this.[[queue]].
// Step 3.c: Remove entry from this.[[queue]], shifting all other elements
// downward (so that the second becomes the first, and so on).
RootedNativeObject unwrappedQueue(cx, unwrappedController->queue());
@@ -4002,55 +3995,16 @@ JS_FRIEND_API(JSObject*)
js::UnwrapReadableStream(JSObject* obj)
{
if (JSObject* unwrapped = CheckedUnwrap(obj)) {
return unwrapped->is<ReadableStream>() ? unwrapped : nullptr;
}
return nullptr;
}
-extern JS_PUBLIC_API(void)
-JS::SetReadableStreamCallbacks(JSContext* cx,
- JS::RequestReadableStreamDataCallback dataRequestCallback,
- JS::WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
- JS::CancelReadableStreamCallback cancelCallback,
- JS::ReadableStreamClosedCallback closedCallback,
- JS::ReadableStreamErroredCallback erroredCallback,
- JS::ReadableStreamFinalizeCallback finalizeCallback)
-{
- MOZ_ASSERT(dataRequestCallback);
- MOZ_ASSERT(writeIntoReadRequestCallback);
- MOZ_ASSERT(cancelCallback);
- MOZ_ASSERT(closedCallback);
- MOZ_ASSERT(erroredCallback);
- MOZ_ASSERT(finalizeCallback);
-
- JSRuntime* rt = cx->runtime();
-
- MOZ_ASSERT(!rt->readableStreamDataRequestCallback);
- MOZ_ASSERT(!rt->readableStreamWriteIntoReadRequestCallback);
- MOZ_ASSERT(!rt->readableStreamCancelCallback);
- MOZ_ASSERT(!rt->readableStreamClosedCallback);
- MOZ_ASSERT(!rt->readableStreamErroredCallback);
- MOZ_ASSERT(!rt->readableStreamFinalizeCallback);
-
- rt->readableStreamDataRequestCallback = dataRequestCallback;
- rt->readableStreamWriteIntoReadRequestCallback = writeIntoReadRequestCallback;
- rt->readableStreamCancelCallback = cancelCallback;
- rt->readableStreamClosedCallback = closedCallback;
- rt->readableStreamErroredCallback = erroredCallback;
- rt->readableStreamFinalizeCallback = finalizeCallback;
-}
-
-JS_PUBLIC_API(bool)
-JS::HasReadableStreamCallbacks(JSContext* cx)
-{
- return cx->runtime()->readableStreamDataRequestCallback;
-}
-
JS_PUBLIC_API(JSObject*)
JS::NewReadableDefaultStreamObject(JSContext* cx,
JS::HandleObject underlyingSource /* = nullptr */,
JS::HandleFunction size /* = nullptr */,
double highWaterMark /* = 1 */,
JS::HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!cx->zone()->isAtomsZone());
@@ -4068,34 +4022,26 @@ JS::NewReadableDefaultStreamObject(JSCon
RootedValue sourceVal(cx, ObjectValue(*source));
RootedValue sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue());
RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark));
return ReadableStream::createDefaultStream(cx, sourceVal, sizeVal, highWaterMarkVal, proto);
}
JS_PUBLIC_API(JSObject*)
JS::NewReadableExternalSourceStreamObject(JSContext* cx,
- void* underlyingSource,
+ JS::ReadableStreamUnderlyingSource* underlyingSource,
HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!cx->zone()->isAtomsZone());
AssertHeapIsIdle();
CHECK_THREAD(cx);
+ MOZ_ASSERT(underlyingSource);
MOZ_ASSERT((uintptr_t(underlyingSource) & 1) == 0,
"external underlying source pointers must be aligned");
cx->check(proto);
-#ifdef DEBUG
- JSRuntime* rt = cx->runtime();
- MOZ_ASSERT(rt->readableStreamDataRequestCallback);
- MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback);
- MOZ_ASSERT(rt->readableStreamCancelCallback);
- MOZ_ASSERT(rt->readableStreamClosedCallback);
- MOZ_ASSERT(rt->readableStreamErroredCallback);
- MOZ_ASSERT(rt->readableStreamFinalizeCallback);
-#endif // DEBUG
return ReadableStream::createExternalSourceStream(cx, underlyingSource, proto);
}
JS_PUBLIC_API(bool)
JS::IsReadableStream(JSObject* obj)
{
return obj->canUnwrapAs<ReadableStream>();
@@ -4196,17 +4142,18 @@ JS::ReadableStreamGetReader(JSContext* c
}
JSObject* result = CreateReadableStreamDefaultReader(cx, unwrappedStream);
MOZ_ASSERT_IF(result, IsObjectInContextCompartment(result, cx));
return result;
}
JS_PUBLIC_API(bool)
-JS::ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject streamObj, void** source)
+JS::ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject streamObj,
+ JS::ReadableStreamUnderlyingSource** source)
{
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStream*> unwrappedStream(cx, APIToUnwrapped<ReadableStream>(cx, streamObj));
if (!unwrappedStream) {
return false;
}
@@ -4220,17 +4167,17 @@ JS::ReadableStreamGetExternalUnderlyingS
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE,
"ReadableStreamGetExternalUnderlyingSource");
return false;
}
auto unwrappedController = &unwrappedStream->controller()->as<ReadableByteStreamController>();
unwrappedController->setSourceLocked();
- *source = unwrappedController->underlyingSource().toPrivate();
+ *source = unwrappedController->externalSource();
return true;
}
JS_PUBLIC_API(bool)
JS::ReadableStreamReleaseExternalUnderlyingSource(JSContext* cx, HandleObject streamObj)
{
ReadableStream* unwrappedStream = APIToUnwrapped<ReadableStream>(cx, streamObj);
if (!unwrappedStream) {
@@ -4316,28 +4263,27 @@ JS::ReadableStreamUpdateDataAvailableFro
if (!viewObj) {
return false;
}
Rooted<ArrayBufferViewObject*> transferredView(cx, &viewObj->as<ArrayBufferViewObject>());
if (!transferredView) {
return false;
}
- void* underlyingSource = unwrappedController->underlyingSource().toPrivate();
+ JS::ReadableStreamUnderlyingSource* source = unwrappedController->externalSource();
size_t bytesWritten;
{
AutoRealm ar(cx, unwrappedStream);
JS::AutoSuppressGCAnalysis suppressGC(cx);
JS::AutoCheckCannotGC noGC;
bool dummy;
void* buffer = JS_GetArrayBufferViewData(transferredView, &dummy, noGC);
- auto cb = cx->runtime()->readableStreamWriteIntoReadRequestCallback;
- MOZ_ASSERT(cb);
- cb(cx, unwrappedStream, underlyingSource, buffer, availableData, &bytesWritten);
+ source->writeIntoReadRequestBuffer(cx, unwrappedStream, buffer, availableData,
+ &bytesWritten);
}
// Step iii: Perform ! ReadableStreamFulfillReadRequest(stream, transferredView, false).
RootedValue chunk(cx, ObjectValue(*transferredView));
if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk, false)) {
return false;
}
diff --git a/js/src/builtin/Stream.h b/js/src/builtin/Stream.h
--- a/js/src/builtin/Stream.h
+++ b/js/src/builtin/Stream.h
@@ -95,17 +95,18 @@ class ReadableStream : public NativeObje
JS::ReadableStreamMode mode() const;
bool locked() const;
public:
static ReadableStream* createDefaultStream(JSContext* cx, HandleValue underlyingSource,
HandleValue size, HandleValue highWaterMark,
HandleObject proto = nullptr);
- static ReadableStream* createExternalSourceStream(JSContext* cx, void* underlyingSource,
+ static ReadableStream* createExternalSourceStream(JSContext* cx,
+ JS::ReadableStreamUnderlyingSource* source,
HandleObject proto = nullptr);
private:
static MOZ_MUST_USE ReadableStream* createStream(JSContext* cx, HandleObject proto = nullptr);
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
@@ -239,16 +240,23 @@ class ReadableStreamController : public
ReadableStream* stream() const {
return &getFixedSlot(Slot_Stream).toObject().as<ReadableStream>();
}
void setStream(ReadableStream* stream) { setFixedSlot(Slot_Stream, ObjectValue(*stream)); }
Value underlyingSource() const { return getFixedSlot(Slot_UnderlyingSource); }
void setUnderlyingSource(const Value& underlyingSource) {
setFixedSlot(Slot_UnderlyingSource, underlyingSource);
}
+ JS::ReadableStreamUnderlyingSource* externalSource() const {
+ static_assert(alignof(JS::ReadableStreamUnderlyingSource) >= 2,
+ "External underling sources are stored as PrivateValues, "
+ "so they must have even addresses");
+ MOZ_ASSERT(hasExternalSource());
+ return static_cast<JS::ReadableStreamUnderlyingSource*>(underlyingSource().toPrivate());
+ }
double strategyHWM() const { return getFixedSlot(Slot_StrategyHWM).toNumber(); }
void setStrategyHWM(double highWaterMark) {
setFixedSlot(Slot_StrategyHWM, NumberValue(highWaterMark));
}
uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); }
void setFlags(uint32_t flags) { setFixedSlot(Slot_Flags, Int32Value(flags)); }
void addFlags(uint32_t flags) { setFlags(this->flags() | flags); }
void removeFlags(uint32_t flags) { setFlags(this->flags() & ~flags); }
@@ -301,18 +309,19 @@ class ReadableStreamDefaultController :
class ReadableByteStreamController : public ReadableStreamController
{
public:
/**
* Memory layout for ReadableByteStreamControllers, starting after the
* slots shared among all types of controllers.
*
- * PendingPullIntos is guaranteed to be in the same compartment as the
- * controller, but might contain wrappers for objects from other compartments.
+ * PendingPullIntos is guaranteed to be in the same compartment as the
+ * controller, but might contain wrappers for objects from other
+ * compartments.
*
* AutoAllocateSize is a primitive (numeric) value.
*/
enum Slots {
Slot_BYOBRequest = ReadableStreamController::SlotCount,
Slot_PendingPullIntos,
Slot_AutoAllocateSize,
SlotCount
diff --git a/js/src/jsapi-tests/testReadableStream.cpp b/js/src/jsapi-tests/testReadableStream.cpp
--- a/js/src/jsapi-tests/testReadableStream.cpp
+++ b/js/src/jsapi-tests/testReadableStream.cpp
@@ -6,123 +6,109 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsapi.h"
#include "jsfriendapi.h"
#include "jsapi-tests/tests.h"
using namespace JS;
-struct StubExternalUnderlyingSource {
- void* buffer;
+char testBufferData[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+struct StubExternalUnderlyingSource : public JS::ReadableStreamUnderlyingSource {
+ void* buffer = testBufferData;
+ bool dataRequestCBCalled = false;
+ bool writeIntoRequestBufferCBCalled = false;
+ bool cancelStreamCBCalled = false;
+ Value cancelStreamReason;
+ bool streamClosedCBCalled = false;
+ Value streamClosedReason;
+ bool streamErroredCBCalled = false;
+ Value streamErroredReason;
+ bool finalizeStreamCBCalled = false;
+ void* finalizedStreamUnderlyingSource;
+
+ static StubExternalUnderlyingSource instance;
+
+ void requestData(JSContext* cx, HandleObject stream, size_t desiredSize) override {
+ js::AssertSameCompartment(cx, stream);
+ MOZ_ASSERT(!dataRequestCBCalled, "Invalid test setup");
+ dataRequestCBCalled = true;
+ }
+
+ void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream,
+ void* buffer, size_t length, size_t* bytesWritten) override
+ {
+ js::AssertSameCompartment(cx, stream);
+ MOZ_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup");
+ writeIntoRequestBufferCBCalled = true;
+
+ MOZ_ASSERT(this == &StubExternalUnderlyingSource::instance);
+ MOZ_ASSERT(StubExternalUnderlyingSource::instance.buffer == testBufferData);
+ MOZ_ASSERT(length <= sizeof(testBufferData));
+ memcpy(buffer, testBufferData, length);
+ *bytesWritten = length;
+ }
+
+ Value cancel(JSContext* cx, HandleObject stream, HandleValue reason) override {
+ js::AssertSameCompartment(cx, stream);
+ js::AssertSameCompartment(cx, reason);
+ MOZ_ASSERT(!cancelStreamCBCalled, "Invalid test setup");
+ cancelStreamCBCalled = true;
+ cancelStreamReason = reason;
+ return reason;
+ }
+
+ void onClosed(JSContext* cx, HandleObject stream) override {
+ js::AssertSameCompartment(cx, stream);
+ MOZ_ASSERT(!streamClosedCBCalled, "Invalid test setup");
+ streamClosedCBCalled = true;
+ }
+
+ void onErrored(JSContext* cx, HandleObject stream, HandleValue reason) override {
+ js::AssertSameCompartment(cx, stream);
+ js::AssertSameCompartment(cx, reason);
+ MOZ_ASSERT(!streamErroredCBCalled, "Invalid test setup");
+ streamErroredCBCalled = true;
+ streamErroredReason = reason;
+ }
+
+ void finalize() override {
+ MOZ_ASSERT(!finalizeStreamCBCalled, "Invalid test setup");
+ finalizeStreamCBCalled = true;
+ finalizedStreamUnderlyingSource = this;
+ }
+
+ void reset() {
+ dataRequestCBCalled = false;
+ writeIntoRequestBufferCBCalled = false;
+ cancelStreamReason = UndefinedValue();
+ cancelStreamCBCalled = false;
+ streamClosedCBCalled = false;
+ streamErroredCBCalled = false;
+ finalizeStreamCBCalled = false;
+ }
};
-char testBufferData[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+StubExternalUnderlyingSource StubExternalUnderlyingSource::instance;
-StubExternalUnderlyingSource stubExternalUnderlyingSource = {
- testBufferData
-};
static_assert(MOZ_ALIGNOF(StubExternalUnderlyingSource) > 1,
"UnderlyingSource pointers must not have the low bit set");
static JSObject*
NewDefaultStream(JSContext* cx, HandleObject source = nullptr, HandleFunction size = nullptr,
double highWaterMark = 1, HandleObject proto = nullptr)
{
RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size, highWaterMark,
proto));
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
return stream;
}
-static bool dataRequestCBCalled = false;
-static void
-DataRequestCB(JSContext* cx, HandleObject stream, void* underlyingSource,
- size_t desiredSize)
-{
- js::AssertSameCompartment(cx, stream);
- MOZ_ASSERT(!dataRequestCBCalled, "Invalid test setup");
- dataRequestCBCalled = true;
-}
-
-static bool writeIntoRequestBufferCBCalled = false;
-static void
-WriteIntoRequestBufferCB(JSContext* cx, HandleObject stream, void* underlyingSource,
- void* buffer, size_t length, size_t* bytesWritten)
-{
- js::AssertSameCompartment(cx, stream);
- MOZ_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup");
- writeIntoRequestBufferCBCalled = true;
-
- MOZ_ASSERT(underlyingSource == &stubExternalUnderlyingSource);
- MOZ_ASSERT(stubExternalUnderlyingSource.buffer == testBufferData);
- MOZ_ASSERT(length <= sizeof(testBufferData));
- memcpy(buffer, testBufferData, length);
- *bytesWritten = length;
-}
-
-static bool cancelStreamCBCalled = false;
-static Value cancelStreamReason;
-static Value
-CancelStreamCB(JSContext* cx, HandleObject stream, void* underlyingSource,
- HandleValue reason)
-{
- js::AssertSameCompartment(cx, stream);
- js::AssertSameCompartment(cx, reason);
- MOZ_ASSERT(!cancelStreamCBCalled, "Invalid test setup");
- cancelStreamCBCalled = true;
- cancelStreamReason = reason;
- return reason;
-}
-
-static bool streamClosedCBCalled = false;
-static Value streamClosedReason;
-static void
-StreamClosedCB(JSContext* cx, HandleObject stream, void* underlyingSource)
-{
- js::AssertSameCompartment(cx, stream);
- MOZ_ASSERT(!streamClosedCBCalled, "Invalid test setup");
- streamClosedCBCalled = true;
-}
-
-static bool streamErroredCBCalled = false;
-static Value streamErroredReason;
-static void
-StreamErroredCB(JSContext* cx, HandleObject stream, void* underlyingSource,
- HandleValue reason)
-{
- js::AssertSameCompartment(cx, stream);
- js::AssertSameCompartment(cx, reason);
- MOZ_ASSERT(!streamErroredCBCalled, "Invalid test setup");
- streamErroredCBCalled = true;
- streamErroredReason = reason;
-}
-
-static bool finalizeStreamCBCalled = false;
-static void* finalizedStreamUnderlyingSource;
-static void
-FinalizeStreamCB(void* underlyingSource)
-{
- MOZ_ASSERT(!finalizeStreamCBCalled, "Invalid test setup");
- finalizeStreamCBCalled = true;
- finalizedStreamUnderlyingSource = underlyingSource;
-}
-
-static void
-ResetCallbacks()
-{
- dataRequestCBCalled = false;
- writeIntoRequestBufferCBCalled = false;
- cancelStreamReason = UndefinedValue();
- cancelStreamCBCalled = false;
- streamClosedCBCalled = false;
- streamErroredCBCalled = false;
- finalizeStreamCBCalled = false;
-}
static bool
GetIterResult(JSContext* cx, HandleObject promise, MutableHandleValue value, bool* done)
{
RootedObject iterResult(cx, &GetPromiseResult(promise).toObject());
bool found;
if (!JS_HasProperty(cx, iterResult, "value", &found)) {
@@ -260,19 +246,16 @@ BEGIN_FIXTURE_TEST(StreamTestFixture,
return true;
}
END_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ReadableStreamDefaultReaderRead)
BEGIN_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ReadableStreamDefaultReaderClose)
{
- SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB,
- &CancelStreamCB, &StreamClosedCB, &StreamErroredCB,
- &FinalizeStreamCB);
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
@@ -283,30 +266,27 @@ BEGIN_FIXTURE_TEST(StreamTestFixture,
bool done;
RootedValue value(cx);
CHECK(GetPromiseState(request) == PromiseState::Fulfilled);
CHECK(GetIterResult(cx, request, &value, &done));
CHECK(value.isUndefined());
CHECK(done);
// The callbacks are only invoked for external streams.
- CHECK(!streamClosedCBCalled);
+ CHECK(!StubExternalUnderlyingSource::instance.streamClosedCBCalled);
return true;
}
END_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ReadableStreamDefaultReaderClose)
BEGIN_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ReadableStreamDefaultReaderError)
{
- ResetCallbacks();
- SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB,
- &CancelStreamCB, &StreamClosedCB, &StreamErroredCB,
- &FinalizeStreamCB);
+ StubExternalUnderlyingSource::instance.reset();
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
@@ -322,97 +302,81 @@ BEGIN_FIXTURE_TEST(StreamTestFixture,
CHECK(ReadableStreamError(cx, stream, error));
CHECK(GetPromiseState(request) == PromiseState::Rejected);
RootedValue reason(cx, GetPromiseResult(request));
CHECK(reason.isInt32());
CHECK(reason.toInt32() == 42);
// The callbacks are only invoked for external streams.
- CHECK(!streamErroredCBCalled);
+ CHECK(!StubExternalUnderlyingSource::instance.streamErroredCBCalled);
return true;
}
END_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ReadableStreamDefaultReaderError)
static JSObject*
-NewExternalSourceStream(JSContext* cx, void* underlyingSource,
- RequestReadableStreamDataCallback dataRequestCallback,
- WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
- CancelReadableStreamCallback cancelCallback,
- ReadableStreamClosedCallback closedCallback,
- ReadableStreamErroredCallback erroredCallback,
- ReadableStreamFinalizeCallback finalizeCallback)
+NewExternalSourceStream(JSContext* cx, ReadableStreamUnderlyingSource* source)
{
- SetReadableStreamCallbacks(cx, dataRequestCallback, writeIntoReadRequestCallback,
- cancelCallback, closedCallback, erroredCallback,
- finalizeCallback);
- RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, underlyingSource));
+ RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, source));
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
return stream;
}
static JSObject*
NewExternalSourceStream(JSContext* cx)
{
- return NewExternalSourceStream(cx,
- &stubExternalUnderlyingSource,
- &DataRequestCB,
- &WriteIntoRequestBufferCB,
- &CancelStreamCB,
- &StreamClosedCB,
- &StreamErroredCB,
- &FinalizeStreamCB);
+ return NewExternalSourceStream(cx, &StubExternalUnderlyingSource::instance);
}
BEGIN_FIXTURE_TEST(StreamTestFixture,
testReadableStream_CreateReadableByteStreamWithExternalSource)
{
- ResetCallbacks();
+ StubExternalUnderlyingSource::instance.reset();
RootedObject stream(cx, NewExternalSourceStream(cx));
CHECK(stream);
ReadableStreamMode mode;
CHECK(ReadableStreamGetMode(cx, stream, &mode));
CHECK(mode == ReadableStreamMode::ExternalSource);
- void* underlyingSource;
+ ReadableStreamUnderlyingSource* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
- CHECK(underlyingSource == &stubExternalUnderlyingSource);
+ CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
bool locked;
CHECK(ReadableStreamIsLocked(cx, stream, &locked));
CHECK(locked);
CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
return true;
}
END_FIXTURE_TEST(StreamTestFixture,
testReadableStream_CreateReadableByteStreamWithExternalSource)
BEGIN_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ExternalSourceCancel)
{
- ResetCallbacks();
+ StubExternalUnderlyingSource::instance.reset();
RootedObject stream(cx, NewExternalSourceStream(cx));
CHECK(stream);
RootedValue reason(cx, Int32Value(42));
CHECK(ReadableStreamCancel(cx, stream, reason));
- CHECK(cancelStreamCBCalled);
- CHECK(cancelStreamReason == reason);
+ CHECK(StubExternalUnderlyingSource::instance.cancelStreamCBCalled);
+ CHECK(StubExternalUnderlyingSource::instance.cancelStreamReason == reason);
return true;
}
END_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ExternalSourceCancel)
BEGIN_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ExternalSourceGetReader)
{
- ResetCallbacks();
+ StubExternalUnderlyingSource::instance.reset();
RootedObject stream(cx, NewExternalSourceStream(cx));
CHECK(stream);
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL("stream.getReader()", &rval);
@@ -469,51 +433,51 @@ struct ReadFromExternalSourceFixture : p
return true;
}
bool readWithoutDataAvailable(CompartmentMode compartmentMode,
const char* evalSrc,
const char* evalSrc2,
uint32_t writtenLength)
{
- ResetCallbacks();
+ StubExternalUnderlyingSource::instance.reset();
definePrint();
// Create the stream.
RootedObject streamGlobal(cx);
RootedObject stream(cx); // can be a wrapper
CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream));
js::RunJobs(cx);
// GetExternalUnderlyingSource locks the stream.
- void* underlyingSource;
+ ReadableStreamUnderlyingSource* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
- CHECK(underlyingSource == &stubExternalUnderlyingSource);
+ CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
bool locked;
CHECK(ReadableStreamIsLocked(cx, stream, &locked));
CHECK(locked);
CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
// Run caller-supplied JS code to read from the stream.
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL(evalSrc, &rval);
- CHECK(dataRequestCBCalled);
- CHECK(!writeIntoRequestBufferCBCalled);
+ CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled);
+ CHECK(!StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
CHECK(rval.isObject());
RootedObject unwrappedPromise(cx, js::CheckedUnwrap(&rval.toObject()));
CHECK(unwrappedPromise);
CHECK(IsPromiseObject(unwrappedPromise));
CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Pending);
// Stream in some data; this resolves the read() result promise.
size_t length = sizeof(testBufferData);
CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length));
- CHECK(writeIntoRequestBufferCBCalled);
+ CHECK(StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled);
RootedObject chunk(cx);
{
JSAutoRealm ar(cx, unwrappedPromise);
RootedValue iterVal(cx);
bool done;
if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) {
return false;
@@ -527,56 +491,56 @@ struct ReadFromExternalSourceFixture : p
{
JS::AutoCheckCannotGC noGC(cx);
bool dummy;
void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
CHECK(!memcmp(buffer, testBufferData, writtenLength));
}
// Check the callbacks fired by calling read() again.
- dataRequestCBCalled = false;
- writeIntoRequestBufferCBCalled = false;
+ StubExternalUnderlyingSource::instance.dataRequestCBCalled = false;
+ StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled = false;
EVAL(evalSrc2, &rval);
- CHECK(dataRequestCBCalled);
- CHECK(!writeIntoRequestBufferCBCalled);
+ CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled);
+ CHECK(!StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
return true;
}
bool readWithDataAvailable(CompartmentMode compartmentMode,
const char* evalSrc,
uint32_t writtenLength)
{
- ResetCallbacks();
+ StubExternalUnderlyingSource::instance.reset();
definePrint();
// Create a stream.
RootedObject streamGlobal(cx);
RootedObject stream(cx);
CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream));
// Getting the underlying source locks the stream.
- void* underlyingSource;
+ ReadableStreamUnderlyingSource* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
- CHECK(underlyingSource == &stubExternalUnderlyingSource);
+ CHECK(underlyingSource == &StubExternalUnderlyingSource::instance);
bool locked;
CHECK(ReadableStreamIsLocked(cx, stream, &locked));
CHECK(locked);
CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
// Make some data available.
size_t length = sizeof(testBufferData);
CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length));
// Read from the stream.
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL(evalSrc, &rval);
- CHECK(writeIntoRequestBufferCBCalled);
+ CHECK(StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled);
CHECK(rval.isObject());
RootedObject unwrappedPromise(cx, js::CheckedUnwrap(&rval.toObject()));
CHECK(unwrappedPromise);
CHECK(IsPromiseObject(unwrappedPromise));
CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled);
RootedObject chunk(cx);
{
JSAutoRealm ar(cx, unwrappedPromise);
@@ -764,33 +728,33 @@ BEGIN_FIXTURE_TEST(StreamTestFixture,
return true;
}
END_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead)
BEGIN_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ReadableStreamGetExternalUnderlyingSource)
{
- ResetCallbacks();
+ StubExternalUnderlyingSource::instance.reset();
RootedObject stream(cx, NewExternalSourceStream(cx));
CHECK(stream);
- void* source;
+ ReadableStreamUnderlyingSource* source;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source));
- CHECK(source == &stubExternalUnderlyingSource);
+ CHECK(source == &StubExternalUnderlyingSource::instance);
CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
RootedObject otherGlobal(cx, createGlobal());
CHECK(otherGlobal);
{
JSAutoRealm ar(cx, otherGlobal);
CHECK(JS_WrapObject(cx, &stream));
- void* source;
+ ReadableStreamUnderlyingSource* source;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source));
- CHECK(source == &stubExternalUnderlyingSource);
+ CHECK(source == &StubExternalUnderlyingSource::instance);
CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream));
}
return true;
}
END_FIXTURE_TEST(StreamTestFixture,
testReadableStream_ReadableStreamGetExternalUnderlyingSource)
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -95,22 +95,16 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
#ifdef DEBUG
updateChildRuntimeCount(parentRuntime),
initialized_(false),
#endif
mainContext_(nullptr),
profilerSampleBufferRangeStart_(0),
telemetryCallback(nullptr),
consumeStreamCallback(nullptr),
- readableStreamDataRequestCallback(nullptr),
- readableStreamWriteIntoReadRequestCallback(nullptr),
- readableStreamCancelCallback(nullptr),
- readableStreamClosedCallback(nullptr),
- readableStreamErroredCallback(nullptr),
- readableStreamFinalizeCallback(nullptr),
hadOutOfMemory(false),
allowRelazificationForTesting(false),
destroyCompartmentCallback(nullptr),
sizeOfIncludingThisCompartmentCallback(nullptr),
destroyRealmCallback(nullptr),
realmNameCallback(nullptr),
externalStringSizeofCallback(nullptr),
securityCallbacks(&NullSecurityCallbacks),
diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -341,23 +341,16 @@ struct JSRuntime : public js::MallocProv
js::UnprotectedData<JS::ConsumeStreamCallback> consumeStreamCallback;
js::GlobalObject* getIncumbentGlobal(JSContext* cx);
bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise,
js::Handle<js::GlobalObject*> incumbentGlobal);
void addUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
void removeUnhandledRejectedPromise(JSContext* cx, js::HandleObject promise);
- js::UnprotectedData<JS::RequestReadableStreamDataCallback> readableStreamDataRequestCallback;
- js::UnprotectedData<JS::WriteIntoReadRequestBufferCallback> readableStreamWriteIntoReadRequestCallback;
- js::UnprotectedData<JS::CancelReadableStreamCallback> readableStreamCancelCallback;
- js::UnprotectedData<JS::ReadableStreamClosedCallback> readableStreamClosedCallback;
- js::UnprotectedData<JS::ReadableStreamErroredCallback> readableStreamErroredCallback;
- js::UnprotectedData<JS::ReadableStreamFinalizeCallback> readableStreamFinalizeCallback;
-
/* Had an out-of-memory error which did not populate an exception. */
mozilla::Atomic<bool, mozilla::SequentiallyConsistent,
mozilla::recordreplay::Behavior::DontPreserve> hadOutOfMemory;
/*
* Allow relazifying functions in compartments that are active. This is
* only used by the relazifyFunctions() testing function.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment