Bug 1502355 - Save to the side the action specified when we're supposed to "shutdown with an action". r=arai

Differential Revision: https://phabricator.services.mozilla.com/D80782
This commit is contained in:
Jeff Walden 2020-06-24 18:40:52 +00:00
Родитель 266888d6f0
Коммит a30e220cb7
2 изменённых файлов: 111 добавлений и 25 удалений

Просмотреть файл

@ -65,18 +65,6 @@ using js::WritableStream;
using js::WritableStreamDefaultWriter;
using js::WritableStreamDefaultWriterWrite;
// This typedef is undoubtedly not the right one for the long run, but it's
// enough to be placeholder for now.
using Action = bool (*)(JSContext*, Handle<PipeToState*> state);
static MOZ_MUST_USE bool DummyAction(JSContext* cx,
Handle<PipeToState*> state) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED,
"pipeTo dummy action");
return false;
}
static ReadableStream* GetUnwrappedSource(JSContext* cx,
Handle<PipeToState*> state) {
cx->check(state);
@ -134,8 +122,8 @@ static MOZ_MUST_USE bool Finalize(JSContext* cx, unsigned argc, Value* vp) {
// Shutdown with an action: if any of the above requirements ask to shutdown
// with an action action, optionally with an error originalError, then:
static MOZ_MUST_USE bool ShutdownWithAction(
JSContext* cx, Handle<PipeToState*> state, Action action,
Handle<Maybe<Value>> originalError) {
JSContext* cx, Handle<PipeToState*> state,
PipeToState::ShutdownAction action, Handle<Maybe<Value>> originalError) {
cx->check(state);
cx->check(originalError);
@ -147,6 +135,9 @@ static MOZ_MUST_USE bool ShutdownWithAction(
// Step b: Set shuttingDown to true.
state->setShuttingDown();
// Save the action away for later -- potentially asynchronous -- use.
state->setShutdownAction(action);
// Step c: If dest.[[state]] is "writable" and
// ! WritableStreamCloseQueuedOrInFlight(dest) is false,
WritableStream* unwrappedDest = GetUnwrappedDest(cx, state);
@ -285,7 +276,9 @@ static MOZ_MUST_USE bool OnSourceErrored(
// ! WritableStreamAbort(dest, source.[[storedError]]) and with
// source.[[storedError]].
else {
if (!ShutdownWithAction(cx, state, DummyAction, storedError)) {
if (!ShutdownWithAction(cx, state,
PipeToState::ShutdownAction::AbortDestStream,
storedError)) {
return false;
}
}
@ -327,7 +320,9 @@ static MOZ_MUST_USE bool OnDestErrored(JSContext* cx,
// ! ReadableStreamCancel(source, dest.[[storedError]]) and with
// dest.[[storedError]].
else {
if (!ShutdownWithAction(cx, state, DummyAction, storedError)) {
if (!ShutdownWithAction(cx, state,
PipeToState::ShutdownAction::CancelSource,
storedError)) {
return false;
}
}
@ -362,7 +357,10 @@ static MOZ_MUST_USE bool OnSourceClosed(JSContext* cx,
// i. If preventClose is false, shutdown with an action of
// ! WritableStreamDefaultWriterCloseWithErrorPropagation(writer).
else {
if (!ShutdownWithAction(cx, state, DummyAction, noError)) {
if (!ShutdownWithAction(
cx, state,
PipeToState::ShutdownAction::CloseWriterWithErrorPropagation,
noError)) {
return false;
}
}
@ -426,7 +424,8 @@ static MOZ_MUST_USE bool OnDestClosed(JSContext* cx,
// iii. If preventCancel is false, shutdown with an action of
// ! ReadableStreamCancel(source, destClosed) and with destClosed.
else {
if (!ShutdownWithAction(cx, state, DummyAction, destClosed)) {
if (!ShutdownWithAction(
cx, state, PipeToState::ShutdownAction::CancelSource, destClosed)) {
return false;
}
}

Просмотреть файл

@ -10,6 +10,7 @@
#define builtin_streams_PipeToState_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/WrappingOperations.h" // mozilla::WrapToSigned
#include <stdint.h> // uint32_t
@ -86,25 +87,87 @@ class PipeToState : public NativeObject {
SlotCount,
};
// The set of possible actions to be passed to the "shutdown with an action"
// algorithm.
//
// We store actions as numbers because 1) handler functions already devote
// their extra slots to target and extra value; and 2) storing a full function
// pointer would require an extra slot, while storing as number packs into
// existing flag storage.
enum class ShutdownAction {
/** The action used during |abortAlgorithm|.*/
AbortAlgorithm,
/**
* The action taken when |source| errors and aborting is not prevented, to
* abort |dest| with |source|'s error.
*/
AbortDestStream,
/**
* The action taken when |dest| becomes errored or closed and canceling is
* not prevented, to cancel |source| with |dest|'s error.
*/
CancelSource,
/**
* The action taken when |source| closes and closing is not prevented, to
* close the writer while propagating any error in it.
*/
CloseWriterWithErrorPropagation,
};
private:
enum Flags : uint32_t {
Flag_ShuttingDown = 0b0001,
/**
* The action passed to the "shutdown with an action" algorithm.
*
* Note that because only the first "shutdown" and "shutdown with an action"
* operation has any effect, we can store this action in |PipeToState| in
* the first invocation of either operation without worrying about it being
* overwritten.
*
* Purely for convenience, we encode this in the lowest bits so that the
* result of a mask is the underlying value of the correct |ShutdownAction|.
*/
Flag_ShutdownActionBits = 0b0000'0011,
Flag_PreventClose = 0b0010,
Flag_PreventAbort = 0b0100,
Flag_PreventCancel = 0b1000,
Flag_ShuttingDown = 0b0000'0100,
Flag_PendingRead = 0b1'0000,
Flag_PendingRead = 0b0000'1000,
#ifdef DEBUG
Flag_PendingReadWouldBeRejected = 0b10'0000,
Flag_PendingReadWouldBeRejected = 0b0001'0000,
#endif
Flag_PreventClose = 0b0010'0000,
Flag_PreventAbort = 0b0100'0000,
Flag_PreventCancel = 0b1000'0000,
};
uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); }
void setFlags(uint32_t flags) {
setFixedSlot(Slot_Flags, JS::Int32Value(flags));
setFixedSlot(Slot_Flags, JS::Int32Value(mozilla::WrapToSigned(flags)));
}
// Flags start out zeroed, so the initially-stored shutdown action value will
// be this value. (This is also the value of an *initialized* shutdown
// action, but it doesn't seem worth the trouble to store an extra bit to
// detect this specific action being recorded multiple times, purely for
// assertions.)
static constexpr ShutdownAction UninitializedAction =
ShutdownAction::AbortAlgorithm;
static_assert(Flag_ShutdownActionBits & 1,
"shutdown action bits must be low-order bits so that we can "
"cast ShutdownAction values directly to bits to store");
static constexpr uint32_t MaxAction =
static_cast<uint32_t>(ShutdownAction::CloseWriterWithErrorPropagation);
static_assert(MaxAction <= Flag_ShutdownActionBits,
"max action shouldn't overflow available bits to store it");
public:
static const JSClass class_;
@ -144,6 +207,30 @@ class PipeToState : public NativeObject {
setFlags(flags() | Flag_ShuttingDown);
}
ShutdownAction shutdownAction() const {
MOZ_ASSERT(shuttingDown(),
"must be shutting down to have a shutdown action");
uint32_t bits = flags() & Flag_ShutdownActionBits;
static_assert(Flag_ShutdownActionBits & 1,
"shutdown action bits are assumed to be low-order bits that "
"don't have to be shifted down to ShutdownAction's range");
MOZ_ASSERT(bits <= MaxAction, "bits must encode a valid action");
return static_cast<ShutdownAction>(bits);
}
void setShutdownAction(ShutdownAction action) {
MOZ_ASSERT(shuttingDown(),
"must be protected by the |shuttingDown| boolean to save the "
"shutdown action");
MOZ_ASSERT(shutdownAction() == UninitializedAction,
"should only set shutdown action once");
setFlags(flags() | static_cast<uint32_t>(action));
}
bool preventClose() const { return flags() & Flag_PreventClose; }
bool preventAbort() const { return flags() & Flag_PreventAbort; }
bool preventCancel() const { return flags() & Flag_PreventCancel; }