зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1502355 - Fill in more of |ReadableStreamPipeTo|, as regards consing up a |PipeToState| closure to store state variables. r=arai
Differential Revision: https://phabricator.services.mozilla.com/D65058 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
d451364e60
Коммит
d92c45cab7
|
@ -8,25 +8,119 @@
|
|||
|
||||
#include "builtin/streams/PipeToState.h"
|
||||
|
||||
#include "js/Class.h" // JSClass, JSCLASS_HAS_RESERVED_SLOTS
|
||||
#include "js/RootingAPI.h" // JS::Rooted
|
||||
#include "mozilla/Assertions.h" // MOZ_ASSERT
|
||||
|
||||
#include "jsapi.h" // JS_ReportErrorNumberASCII
|
||||
#include "jsfriendapi.h" // js::GetErrorMessage, JSMSG_*
|
||||
|
||||
#include "builtin/Promise.h" // js::RejectPromiseWithPendingError
|
||||
#include "builtin/streams/ReadableStream.h" // js::ReadableStream
|
||||
#include "builtin/streams/ReadableStreamReader.h" // js::CreateReadableStreamDefaultReader
|
||||
#include "builtin/streams/WritableStream.h" // js::WritableStream
|
||||
#include "builtin/streams/WritableStreamDefaultWriter.h" // js::CreateWritableStreamDefaultWriter
|
||||
#include "js/Class.h" // JSClass, JSCLASS_HAS_RESERVED_SLOTS
|
||||
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
|
||||
#include "vm/PromiseObject.h" // js::PromiseObject
|
||||
|
||||
#include "vm/JSContext-inl.h" // JSContext::check
|
||||
#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance
|
||||
|
||||
using JS::Handle;
|
||||
using JS::Int32Value;
|
||||
using JS::ObjectValue;
|
||||
using JS::Rooted;
|
||||
|
||||
using js::PipeToState;
|
||||
|
||||
/* static */ PipeToState* PipeToState::create(JSContext* cx) {
|
||||
/**
|
||||
* Stream spec, 3.4.11. ReadableStreamPipeTo ( source, dest,
|
||||
* preventClose, preventAbort,
|
||||
* preventCancel, signal )
|
||||
* Steps 4-11, 13-14.
|
||||
*/
|
||||
/* static */ PipeToState* PipeToState::create(
|
||||
JSContext* cx, Handle<PromiseObject*> promise,
|
||||
Handle<ReadableStream*> unwrappedSource,
|
||||
Handle<WritableStream*> unwrappedDest, bool preventClose, bool preventAbort,
|
||||
bool preventCancel, Handle<JSObject*> signal) {
|
||||
cx->check(promise);
|
||||
|
||||
// Step 4. Assert: signal is undefined or signal is an instance of the
|
||||
// AbortSignal interface.
|
||||
#ifdef DEBUG
|
||||
if (signal) {
|
||||
// XXX jwalden need to add JSAPI hooks to recognize AbortSignal instances
|
||||
}
|
||||
#endif
|
||||
|
||||
// Step 5: Assert: ! IsReadableStreamLocked(source) is false.
|
||||
MOZ_ASSERT(!unwrappedSource->locked());
|
||||
|
||||
// Step 6: Assert: ! IsWritableStreamLocked(dest) is false.
|
||||
MOZ_ASSERT(!unwrappedDest->isLocked());
|
||||
|
||||
Rooted<PipeToState*> state(cx, NewBuiltinClassInstance<PipeToState>(cx));
|
||||
if (!state) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
state->setFixedSlot(Slot_Flags, Int32Value(0));
|
||||
MOZ_ASSERT(state->getFixedSlot(Slot_Promise).isUndefined());
|
||||
state->initFixedSlot(Slot_Promise, ObjectValue(*promise));
|
||||
|
||||
return state;
|
||||
// Step 7: If ! IsReadableByteStreamController(
|
||||
// source.[[readableStreamController]]) is true, let reader
|
||||
// be either ! AcquireReadableStreamBYOBReader(source) or
|
||||
// ! AcquireReadableStreamDefaultReader(source), at the user agent’s
|
||||
// discretion.
|
||||
// Step 8: Otherwise, let reader be
|
||||
// ! AcquireReadableStreamDefaultReader(source).
|
||||
// We don't implement byte streams, so we always acquire a default reader.
|
||||
{
|
||||
ReadableStreamDefaultReader* reader =
|
||||
CreateReadableStreamDefaultReader(cx, unwrappedSource);
|
||||
if (!reader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(state->getFixedSlot(Slot_Reader).isUndefined());
|
||||
state->initFixedSlot(Slot_Reader, ObjectValue(*reader));
|
||||
}
|
||||
|
||||
// Step 9: Let writer be ! AcquireWritableStreamDefaultWriter(dest).
|
||||
{
|
||||
WritableStreamDefaultWriter* writer =
|
||||
CreateWritableStreamDefaultWriter(cx, unwrappedDest);
|
||||
if (!writer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(state->getFixedSlot(Slot_Writer).isUndefined());
|
||||
state->initFixedSlot(Slot_Writer, ObjectValue(*writer));
|
||||
}
|
||||
|
||||
// Step 10: Set source.[[disturbed]] to true.
|
||||
unwrappedSource->setDisturbed();
|
||||
|
||||
state->initFlags(preventClose, preventAbort, preventCancel);
|
||||
MOZ_ASSERT(state->preventClose() == preventClose);
|
||||
MOZ_ASSERT(state->preventAbort() == preventAbort);
|
||||
MOZ_ASSERT(state->preventCancel() == preventCancel);
|
||||
|
||||
// Step 11: Let shuttingDown be false.
|
||||
MOZ_ASSERT(!state->shuttingDown(), "should be set to false by initFlags");
|
||||
|
||||
// Step 12 ("Let promise be a new promise.") was performed by the caller and
|
||||
// |promise| was its result.
|
||||
|
||||
// Step 13: If signal is not undefined,
|
||||
// XXX jwalden need JSAPI to add an algorithm/steps to an AbortSignal
|
||||
|
||||
// Step 14: In parallel, using reader and writer, read all chunks from source
|
||||
// and write them to dest.
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED,
|
||||
"pipeTo");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const JSClass PipeToState::class_ = {"PipeToState",
|
||||
|
|
|
@ -13,12 +13,21 @@
|
|||
|
||||
#include <stdint.h> // uint32_t
|
||||
|
||||
#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStreamDefaultReader
|
||||
#include "builtin/streams/WritableStreamDefaultWriter.h" // js::WritableStreamDefaultWriter
|
||||
#include "js/Class.h" // JSClass
|
||||
#include "js/Value.h" // JS::Int32Value
|
||||
#include "js/RootingAPI.h" // JS::Handle
|
||||
#include "js/Value.h" // JS::Int32Value, JS::ObjectValue
|
||||
#include "vm/NativeObject.h" // js::NativeObject
|
||||
#include "vm/PromiseObject.h" // js::PromiseObject
|
||||
|
||||
class JS_PUBLIC_API JSObject;
|
||||
|
||||
namespace js {
|
||||
|
||||
class ReadableStream;
|
||||
class WritableStream;
|
||||
|
||||
/**
|
||||
* PipeToState objects implement the local variables in Streams spec 3.4.11
|
||||
* ReadableStreamPipeTo across all sub-operations that occur in that algorithm.
|
||||
|
@ -28,11 +37,51 @@ class PipeToState : public NativeObject {
|
|||
/**
|
||||
* Memory layout for PipeToState instances.
|
||||
*/
|
||||
enum Slots { Slot_Flags, SlotCount };
|
||||
enum Slots {
|
||||
/** Integer bit field of various flags. */
|
||||
Slot_Flags = 0,
|
||||
|
||||
/**
|
||||
* The promise resolved or rejected when the overall pipe-to operation
|
||||
* completes.
|
||||
*
|
||||
* This promise is created directly under |ReadableStreamPipeTo|, at the
|
||||
* same time the corresponding |PipeToState| is created, so it is always
|
||||
* same-compartment with this and is guaranteed to hold a |PromiseObject*|
|
||||
* if initialization succeeded.
|
||||
*/
|
||||
Slot_Promise,
|
||||
|
||||
/**
|
||||
* A |ReadableStreamDefaultReader| used to read from the readable stream
|
||||
* being piped from.
|
||||
*
|
||||
* This reader is created at the same time as its |PipeToState|, so this
|
||||
* reader is same-compartment with this and is guaranteed to be a
|
||||
* |ReadableStreamDefaultReader*| if initialization succeeds.
|
||||
*/
|
||||
Slot_Reader,
|
||||
|
||||
/**
|
||||
* A |WritableStreamDefaultWriter| used to write to the writable stream
|
||||
* being piped to.
|
||||
*
|
||||
* This writer is created at the same time as its |PipeToState|, so this
|
||||
* writer is same-compartment with this and is guaranteed to be a
|
||||
* |WritableStreamDefaultWriter*| if initialization succeeds.
|
||||
*/
|
||||
Slot_Writer,
|
||||
|
||||
SlotCount,
|
||||
};
|
||||
|
||||
private:
|
||||
enum Flags {
|
||||
Flag_ShuttingDown = 1 << 0,
|
||||
enum Flags : uint32_t {
|
||||
Flag_ShuttingDown = 0b0001,
|
||||
|
||||
Flag_PreventClose = 0b0010,
|
||||
Flag_PreventAbort = 0b0100,
|
||||
Flag_PreventCancel = 0b1000,
|
||||
};
|
||||
|
||||
uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); }
|
||||
|
@ -43,13 +92,46 @@ class PipeToState : public NativeObject {
|
|||
public:
|
||||
static const JSClass class_;
|
||||
|
||||
PromiseObject* promise() const {
|
||||
return &getFixedSlot(Slot_Promise).toObject().as<PromiseObject>();
|
||||
}
|
||||
|
||||
ReadableStreamDefaultReader* reader() const {
|
||||
return &getFixedSlot(Slot_Reader)
|
||||
.toObject()
|
||||
.as<ReadableStreamDefaultReader>();
|
||||
}
|
||||
|
||||
WritableStreamDefaultWriter* writer() const {
|
||||
return &getFixedSlot(Slot_Writer)
|
||||
.toObject()
|
||||
.as<WritableStreamDefaultWriter>();
|
||||
}
|
||||
|
||||
bool shuttingDown() const { return flags() & Flag_ShuttingDown; }
|
||||
void setShuttingDown() {
|
||||
MOZ_ASSERT(!shuttingDown());
|
||||
setFlags(flags() | Flag_ShuttingDown);
|
||||
}
|
||||
|
||||
static PipeToState* create(JSContext* cx);
|
||||
bool preventClose() const { return flags() & Flag_PreventClose; }
|
||||
bool preventAbort() const { return flags() & Flag_PreventAbort; }
|
||||
bool preventCancel() const { return flags() & Flag_PreventCancel; }
|
||||
|
||||
void initFlags(bool preventClose, bool preventAbort, bool preventCancel) {
|
||||
MOZ_ASSERT(getFixedSlot(Slot_Flags).isUndefined());
|
||||
|
||||
uint32_t flagBits = (preventClose ? Flag_PreventClose : 0) |
|
||||
(preventAbort ? Flag_PreventAbort : 0) |
|
||||
(preventCancel ? Flag_PreventCancel : 0);
|
||||
setFlags(flagBits);
|
||||
}
|
||||
|
||||
static PipeToState* create(JSContext* cx, JS::Handle<PromiseObject*> promise,
|
||||
JS::Handle<ReadableStream*> unwrappedSource,
|
||||
JS::Handle<WritableStream*> unwrappedDest,
|
||||
bool preventClose, bool preventAbort,
|
||||
bool preventCancel, JS::Handle<JSObject*> signal);
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -368,10 +368,10 @@ static bool ReadableStream_pipeTo(JSContext* cx, unsigned argc, Value* vp) {
|
|||
// second argument destructuring pattern. But as |ToBoolean| is infallible
|
||||
// and has no observable side effects, we may as well do step 3 here too.
|
||||
bool preventClose, preventAbort, preventCancel;
|
||||
Rooted<Value> signal(cx);
|
||||
Rooted<Value> signalVal(cx);
|
||||
{
|
||||
// (P)(Re)use the |signal| root.
|
||||
auto& v = signal;
|
||||
auto& v = signalVal;
|
||||
|
||||
if (!GetProperty(cx, options, cx->names().preventClose, &v)) {
|
||||
return false;
|
||||
|
@ -388,7 +388,7 @@ static bool ReadableStream_pipeTo(JSContext* cx, unsigned argc, Value* vp) {
|
|||
}
|
||||
preventCancel = JS::ToBoolean(v);
|
||||
}
|
||||
if (!GetProperty(cx, options, cx->names().signal, &signal)) {
|
||||
if (!GetProperty(cx, options, cx->names().signal, &signalVal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -416,7 +416,23 @@ static bool ReadableStream_pipeTo(JSContext* cx, unsigned argc, Value* vp) {
|
|||
// Step 4: If signal is not undefined, and signal is not an instance of the
|
||||
// AbortSignal interface, return a promise rejected with a TypeError
|
||||
// exception.
|
||||
// XXX jwalden need some hooks for this, or something
|
||||
Rooted<JSObject*> signal(cx, nullptr);
|
||||
do {
|
||||
if (signalVal.isUndefined()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (signalVal.isObject()) {
|
||||
// XXX jwalden need some JSAPI hooks to detect AbortSignal instances, or
|
||||
// something
|
||||
|
||||
signal = &signalVal.toObject();
|
||||
}
|
||||
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_READABLESTREAM_PIPETO_BAD_SIGNAL);
|
||||
return ReturnPromiseRejectedWithPendingError(cx, args);
|
||||
} while (false);
|
||||
|
||||
// Step 5: If ! IsReadableStreamLocked(this) is true, return a promise
|
||||
// rejected with a TypeError exception.
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "builtin/Array.h" // js::NewDenseFullyAllocatedArray
|
||||
#include "builtin/Promise.h" // js::RejectPromiseWithPendingError
|
||||
#include "builtin/streams/PipeToState.h" // js::PipeToState
|
||||
#include "builtin/streams/ReadableStream.h" // js::ReadableStream
|
||||
#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller
|
||||
#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamDefaultController{Close,Enqueue}, js::ReadableStreamControllerError, js::SourceAlgorithms
|
||||
|
@ -617,7 +618,35 @@ PromiseObject* js::ReadableStreamPipeTo(JSContext* cx,
|
|||
Handle<WritableStream*> unwrappedDest,
|
||||
bool preventClose, bool preventAbort,
|
||||
bool preventCancel,
|
||||
Handle<Value> signal) {
|
||||
JS_ReportErrorASCII(cx, "XXX ceci n'est pas une pipe");
|
||||
return nullptr;
|
||||
Handle<JSObject*> signal) {
|
||||
// Step 1. Assert: ! IsReadableStream(source) is true.
|
||||
// Step 2. Assert: ! IsWritableStream(dest) is true.
|
||||
// Step 3. Assert: Type(preventClose) is Boolean, Type(preventAbort) is
|
||||
// Boolean, and Type(preventCancel) is Boolean.
|
||||
// (These are guaranteed by the type system.)
|
||||
|
||||
// Step 12: Let promise be a new promise.
|
||||
//
|
||||
// We reorder this so that this promise can be rejected and returned in case
|
||||
// of internal error.
|
||||
Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
|
||||
if (!promise) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Steps 4-11, 13-14.
|
||||
Rooted<PipeToState*> pipeToState(
|
||||
cx,
|
||||
PipeToState::create(cx, promise, unwrappedSource, unwrappedDest,
|
||||
preventClose, preventAbort, preventCancel, signal));
|
||||
if (!pipeToState) {
|
||||
if (!RejectPromiseWithPendingError(cx, promise)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
// Step 15.
|
||||
return promise;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "js/RootingAPI.h" // JS::Handle
|
||||
#include "js/Value.h" // JS::Value
|
||||
|
||||
class JS_PUBLIC_API JSObject;
|
||||
|
||||
namespace js {
|
||||
|
||||
class PromiseObject;
|
||||
|
@ -38,7 +40,7 @@ extern MOZ_MUST_USE bool ReadableStreamTee(
|
|||
extern MOZ_MUST_USE PromiseObject* ReadableStreamPipeTo(
|
||||
JSContext* cx, JS::Handle<ReadableStream*> unwrappedSource,
|
||||
JS::Handle<WritableStream*> unwrappedDest, bool preventClose,
|
||||
bool preventAbort, bool preventCancel, JS::Handle<JS::Value> signal);
|
||||
bool preventAbort, bool preventCancel, JS::Handle<JSObject*> signal);
|
||||
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -695,6 +695,7 @@ MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL, 0, JSEXN_TYPEERR,
|
|||
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_NO_CONTROLLER, 1, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method '{0}' called on a request with no controller.")
|
||||
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_RESPOND_CLOSED, 0, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method 'respond' called with non-zero number of bytes with a closed controller.")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "ReadableStream method {0} not yet implemented")
|
||||
MSG_DEF(JSMSG_READABLESTREAM_PIPETO_BAD_SIGNAL, 0, JSEXN_TYPEERR, "signal must be either undefined or an AbortSignal")
|
||||
|
||||
// WritableStream
|
||||
MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSINK_TYPE_WRONG, 0, JSEXN_RANGEERR,"'underlyingSink.type' must be undefined.")
|
||||
|
|
Загрузка…
Ссылка в новой задаче