зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
266888d6f0
Коммит
a30e220cb7
|
@ -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; }
|
||||
|
|
Загрузка…
Ссылка в новой задаче