Backed out 7 changesets (bug 1582348) for spidermonkey bustage at WritableStream.cpp on a CLOSED TREE.

Backed out changeset 912867da0987 (bug 1582348)
Backed out changeset 7d5a91059cc9 (bug 1582348)
Backed out changeset 98fda09d6d39 (bug 1582348)
Backed out changeset 7e6bd57c4493 (bug 1582348)
Backed out changeset 62bbc891288f (bug 1582348)
Backed out changeset 17992c3dd43e (bug 1582348)
Backed out changeset 461556631684 (bug 1582348)
This commit is contained in:
Gurzau Raul 2019-10-30 07:46:31 +02:00
parent 2f5c0aed74
commit 6728c0b9a9
10 changed files with 35 additions and 630 deletions

View File

@ -245,15 +245,6 @@ enum JSWhyMagic {
/** for local use */
JS_GENERIC_MAGIC,
/**
* Write records queued up in WritableStreamDefaultController.[[queue]] in the
* spec are either "close" (a String) or Record { [[chunk]]: chunk }, where
* chunk is an arbitrary user-provided (and therefore non-magic) value.
* Represent "close" the String as this magic value; represent Record records
* as the |chunk| value within each of them.
*/
JS_WRITABLESTREAM_CLOSE_RECORD,
JS_WHY_MAGIC_COUNT
};

View File

@ -4,10 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Stream handler for operations that act on a target object, and possibly upon
* an extra value.
*/
/* Stream handler for operations bound to a provided target. */
#ifndef builtin_streams_HandlerFunction_inl_h
#define builtin_streams_HandlerFunction_inl_h
@ -21,7 +18,7 @@
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
#include "js/Value.h" // JS::ObjectValue
#include "vm/JSContext.h" // JSContext
#include "vm/JSFunction.h" // JSFunction, js::Native, js::NewNativeFunction
#include "vm/JSFunction.h" // js::NewNativeFunction
#include "vm/JSObject.h" // JSObject
#include "vm/ObjectGroup.h" // js::GenericObject
#include "vm/StringType.h" // js::PropertyName
@ -30,16 +27,7 @@
namespace js {
// Handler functions are extended functions, that close over a target object and
// (optionally) over an extra object, storing those objects in the function's
// extended slots.
constexpr size_t StreamHandlerFunctionSlot_Target = 0;
constexpr size_t StreamHandlerFunctionSlot_Extra = 1;
static_assert(StreamHandlerFunctionSlot_Extra <
FunctionExtended::NUM_EXTENDED_SLOTS,
"handler function slots shouldn't exceed available extended "
"slots");
inline MOZ_MUST_USE JSFunction* NewHandler(JSContext* cx, Native handler,
JS::Handle<JSObject*> target) {
@ -57,18 +45,6 @@ inline MOZ_MUST_USE JSFunction* NewHandler(JSContext* cx, Native handler,
return handlerFun;
}
inline MOZ_MUST_USE JSFunction* NewHandlerWithExtra(
JSContext* cx, Native handler, JS::Handle<JSObject*> target,
JS::Handle<JSObject*> extra) {
cx->check(extra);
JSFunction* handlerFun = NewHandler(cx, handler, target);
if (handlerFun) {
handlerFun->setExtendedSlot(StreamHandlerFunctionSlot_Extra,
JS::ObjectValue(*extra));
}
return handlerFun;
}
/**
* Helper for handler functions that "close over" a value that is always a
* direct reference to an object of class T, never a wrapper.
@ -81,18 +57,6 @@ inline MOZ_MUST_USE T* TargetFromHandler(const JS::CallArgs& args) {
.as<T>();
}
/**
* Helper for handler functions that "close over" a value that is always a
* direct reference to an object of class T, never a wrapper.
*/
template <class T>
inline MOZ_MUST_USE T* ExtraFromHandler(const JS::CallArgs& args) {
JSFunction& func = args.callee().as<JSFunction>();
return &func.getExtendedSlot(StreamHandlerFunctionSlot_Extra)
.toObject()
.as<T>();
}
} // namespace js
#endif // builtin_streams_HandlerFunction_inl_h

View File

@ -15,10 +15,9 @@
#include "jspubtd.h" // JSProto_WritableStream
#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC
#include "builtin/streams/MiscellaneousOperations.h" // js::MakeSizeAlgorithmFromSizeFunction, js::ReturnPromiseRejectedWithPendingError, js::ValidateAndNormalizeHighWaterMark
#include "builtin/streams/MiscellaneousOperations.h" // js::MakeSizeAlgorithmFromSizeFunction, js::ValidateAndNormalizeHighWaterMark
#include "builtin/streams/WritableStreamDefaultControllerOperations.h" // js::SetUpWritableStreamDefaultControllerFromUnderlyingSink
#include "builtin/streams/WritableStreamDefaultWriter.h" // js::CreateWritableStreamDefaultWriter
#include "builtin/streams/WritableStreamOperations.h" // js::WritableStreamAbort
#include "js/CallArgs.h" // JS::CallArgs{,FromVp}
#include "js/Class.h" // JS{Function,Property}Spec, JS_{FS,PS}_END, JSCLASS_PRIVATE_IS_NSISUPPORTS, JSCLASS_HAS_PRIVATE, JS_NULL_CLASS_OPS
#include "js/RealmOptions.h" // JS::RealmCreationOptions
@ -31,15 +30,12 @@
#include "vm/Realm.h" // JS::Realm
#include "vm/Compartment-inl.h" // js::UnwrapAndTypeCheckThis
#include "vm/JSContext-inl.h" // JSContext::check
#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance
#include "vm/NativeObject-inl.h" // js::ThrowIfNotConstructing
using js::CreateWritableStreamDefaultWriter;
using js::ReturnPromiseRejectedWithPendingError;
using js::UnwrapAndTypeCheckThis;
using js::WritableStream;
using js::WritableStreamAbort;
using JS::CallArgs;
using JS::CallArgsFromVp;
@ -152,56 +148,6 @@ bool WritableStream::constructor(JSContext* cx, unsigned argc, Value* vp) {
return true;
}
/**
* Streams spec, 4.2.5.1. get locked
*/
static bool WritableStream_locked(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1: If ! WritableStream(this) is false, throw a TypeError exception.
Rooted<WritableStream*> unwrappedStream(
cx, UnwrapAndTypeCheckThis<WritableStream>(cx, args, "get locked"));
if (!unwrappedStream) {
return false;
}
// Step 2: Return ! IsWritableStreamLocked(this).
args.rval().setBoolean(unwrappedStream->isLocked());
return true;
}
/**
* Streams spec, 4.2.5.2. abort(reason)
*/
static bool WritableStream_abort(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1: If ! WritableStream(this) is false, throw a TypeError exception.
Rooted<WritableStream*> unwrappedStream(
cx, UnwrapAndTypeCheckThis<WritableStream>(cx, args, "abort"));
if (!unwrappedStream) {
return false;
}
// Step 2: If ! IsWritableStreamLocked(this) is true, return a promise
// rejected with a TypeError exception.
if (unwrappedStream->isLocked()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CANT_ABORT_LOCKED_WRITABLESTREAM);
return ReturnPromiseRejectedWithPendingError(cx, args);
}
// Step 3: Return ! WritableStreamAbort(this, reason).
JSObject* promise = WritableStreamAbort(cx, unwrappedStream, args.get(0));
if (!promise) {
return false;
}
cx->check(promise);
args.rval().setObject(*promise);
return true;
}
/**
* Streams spec, 4.2.5.3. getWriter()
*/
@ -225,11 +171,9 @@ static bool WritableStream_getWriter(JSContext* cx, unsigned argc, Value* vp) {
}
static const JSFunctionSpec WritableStream_methods[] = {
JS_FN("abort", WritableStream_abort, 1, 0),
JS_FN("getWriter", WritableStream_getWriter, 0, 0), JS_FS_END};
static const JSPropertySpec WritableStream_properties[] = {
JS_PSG("locked", WritableStream_locked, 0), JS_PS_END};
static const JSPropertySpec WritableStream_properties[] = {JS_PS_END};
JS_STREAMS_CLASS_SPEC(WritableStream, 0, SlotCount, 0,
JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE,

View File

@ -70,17 +70,9 @@ class WritableStream : public NativeObject {
Slot_StoredError,
/**
* Very briefly for newborn writable streams before they are initialized,
* |undefined|.
*
* After initialization, a |ListObject| consisting of the value of the
* [[inFlightWriteRequest]] spec field (if it is not |undefined|) followed
* by the elements of the [[queue]] List. |this| and the |ListObject| are
* same-compartment.
*
* After a stream has gone irrevocably into an error state (specifically,
* |stream.[[state]]| is "errored") and requests can no longer be enqueued,
* |undefined| yet again.
* A |ListObject| consisting of the value of the [[inFlightWriteRequest]]
* spec field (if it is not |undefined|) followed by the elements of the
* [[queue]] List. |this| and the |ListObject| are same-compartment.
*
* If the |HaveInFlightWriteRequest| flag is set, the first element of this
* List is the non-|undefined| value of [[inFlightWriteRequest]]. If it is
@ -360,16 +352,9 @@ class WritableStream : public NativeObject {
}
ListObject* writeRequests() const {
MOZ_ASSERT(!getFixedSlot(Slot_WriteRequests).isUndefined(),
"shouldn't be accessing [[writeRequests]] on a newborn and "
"uninitialized stream, or on a stream that's errored and no "
"longer has any write requests");
return &getFixedSlot(Slot_WriteRequests).toObject().as<ListObject>();
}
void clearWriteRequests() {
// Setting [[writeRequests]] to an empty List in the irrevocably-in-error
// case (in which [[writeRequests]] is never again accessed) is optimized to
// just clearing the field. See the comment on the slot constant above.
MOZ_ASSERT(stateIsInitialized());
MOZ_ASSERT(!haveInFlightWriteRequest(),
"must clear the in-flight request flag before clearing "
@ -394,15 +379,6 @@ class WritableStream : public NativeObject {
return flags() & PendingAbortRequestWasAlreadyErroring;
}
void setPendingAbortRequest(JSObject* promise, const JS::Value& reason,
bool wasAlreadyErroring) {
MOZ_ASSERT(!hasPendingAbortRequest());
MOZ_ASSERT(!(flags() & PendingAbortRequestWasAlreadyErroring));
setFixedSlot(Slot_PendingAbortRequestPromise, JS::ObjectValue(*promise));
setFixedSlot(Slot_PendingAbortRequestReason, reason);
setFlag(PendingAbortRequestWasAlreadyErroring, wasAlreadyErroring);
}
void clearPendingAbortRequest() {
MOZ_ASSERT(stateIsInitialized());
MOZ_ASSERT(hasPendingAbortRequest());

View File

@ -51,22 +51,6 @@ class WritableStreamDefaultController : public StreamController {
*/
Slot_StrategySize,
/**
* Slots containing the core of each of the write/close/abort algorithms the
* spec creates from the underlying sink passed in when creating a
* |WritableStream|. ("core", as in the value produced by
* |CreateAlgorithmFromUnderlyingMethod| after validating the user-provided
* input.)
*
* These slots are initialized underneath the |WritableStream| constructor,
* so they are same-compartment with both stream and controller. (They
* could be wrappers around arbitrary callable objects from other
* compartments, tho.)
*/
Slot_WriteMethod,
Slot_CloseMethod,
Slot_AbortMethod,
/** Bit field stored as Int32Value. */
Slot_Flags,
@ -114,22 +98,13 @@ class WritableStreamDefaultController : public StreamController {
controller->setUnderlyingSink(JS::UndefinedHandleValue);
}
JS::Value writeMethod() const { return getFixedSlot(Slot_WriteMethod); }
void setWriteMethod(const JS::Value& writeMethod) {
setFixedSlot(Slot_WriteMethod, writeMethod);
}
void setWriteMethod(const JS::Value& writeMethod) {}
void clearWriteMethod() { setWriteMethod(JS::UndefinedValue()); }
JS::Value closeMethod() const { return getFixedSlot(Slot_CloseMethod); }
void setCloseMethod(const JS::Value& closeMethod) {
setFixedSlot(Slot_CloseMethod, closeMethod);
}
void setCloseMethod(const JS::Value& closeMethod) {}
void clearCloseMethod() { setCloseMethod(JS::UndefinedValue()); }
JS::Value abortMethod() const { return getFixedSlot(Slot_AbortMethod); }
void setAbortMethod(const JS::Value& abortMethod) {
setFixedSlot(Slot_AbortMethod, abortMethod);
}
void setAbortMethod(const JS::Value& abortMethod) {}
void clearAbortMethod() { setAbortMethod(JS::UndefinedValue()); }
double strategyHWM() const {
@ -139,10 +114,7 @@ class WritableStreamDefaultController : public StreamController {
setFixedSlot(Slot_StrategyHWM, DoubleValue(highWaterMark));
}
JS::Value strategySize() const { return getFixedSlot(Slot_StrategySize); }
void setStrategySize(const JS::Value& size) {
setFixedSlot(Slot_StrategySize, size);
}
void setStrategySize(const JS::Value& size) {}
void clearStrategySize() { setStrategySize(JS::UndefinedValue()); }
uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); }

View File

@ -15,34 +15,27 @@
#include "builtin/Promise.h" // js::PromiseObject
#include "builtin/streams/MiscellaneousOperations.h" // js::CreateAlgorithmFromUnderlyingMethod, js::InvokeOrNoop
#include "builtin/streams/QueueWithSizes.h" // js::{EnqueueValueWithSize,ResetQueue}
#include "builtin/streams/QueueWithSizes.h" // js::ResetQueue
#include "builtin/streams/WritableStream.h" // js::WritableStream
#include "builtin/streams/WritableStreamDefaultController.h" // js::WritableStreamDefaultController
#include "builtin/streams/WritableStreamOperations.h" // js::WritableStream{DealWithRejection,{Start,Finish}Erroring,UpdateBackpressure}
#include "js/CallArgs.h" // JS::CallArgs{,FromVp}
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
#include "js/Value.h" // JS::{,Int32,Magic,Object}Value, JS::UndefinedHandleValue, JS_WRITABLESTREAM_CLOSE_RECORD
#include "vm/Compartment.h" // JS::Compartment
#include "js/Value.h" // JS::{,Object}Value
#include "vm/JSContext.h" // JSContext
#include "vm/JSObject.h" // JSObject
#include "vm/List.h" // js::ListObject
#include "vm/Runtime.h" // JSAtomState
#include "builtin/streams/HandlerFunction-inl.h" // js::TargetFromHandler
#include "builtin/streams/MiscellaneousOperations-inl.h" // js::PromiseCall
#include "vm/Compartment-inl.h" // JS::Compartment::wrap
#include "vm/JSContext-inl.h" // JSContext::check
#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance, js::NewObjectWithClassProto
#include "vm/Realm-inl.h" // js::AutoRealm
using JS::CallArgs;
using JS::CallArgsFromVp;
using JS::Handle;
using JS::Int32Value;
using JS::MagicValue;
using JS::ObjectValue;
using JS::Rooted;
using JS::UndefinedHandleValue;
using JS::Value;
using js::ListObject;
@ -50,71 +43,6 @@ using js::WritableStream;
using js::WritableStreamDefaultController;
using js::WritableStreamFinishErroring;
/*** 4.7. Writable stream default controller internal methods ***************/
/**
* Streams spec, 4.7.5.1.
* [[AbortSteps]]( reason )
*/
JSObject* js::WritableStreamControllerAbortSteps(
JSContext* cx, Handle<WritableStreamDefaultController*> unwrappedController,
Handle<Value> reason) {
cx->check(reason);
// Step 1: Let result be the result of performing this.[[abortAlgorithm]],
// passing reason.
// CreateAlgorithmFromUnderlyingMethod(underlyingSink, "abort", 1, « »)
Rooted<Value> unwrappedAbortMethod(cx, unwrappedController->abortMethod());
Rooted<JSObject*> result(cx);
if (unwrappedAbortMethod.isUndefined()) {
// CreateAlgorithmFromUnderlyingMethod step 7.
result = PromiseObject::unforgeableResolve(cx, UndefinedHandleValue);
if (!result) {
return nullptr;
}
} else {
// CreateAlgorithmFromUnderlyingMethod step 6.c.i-ii.
{
AutoRealm ar(cx, unwrappedController);
cx->check(unwrappedAbortMethod);
Rooted<Value> underlyingSink(cx, unwrappedController->underlyingSink());
cx->check(underlyingSink);
Rooted<Value> wrappedReason(cx, reason);
if (!cx->compartment()->wrap(cx, &wrappedReason)) {
return nullptr;
}
result =
PromiseCall(cx, unwrappedAbortMethod, underlyingSink, wrappedReason);
if (!result) {
return nullptr;
}
}
if (!cx->compartment()->wrap(cx, &result)) {
return nullptr;
}
}
// Step 2: Perform ! WritableStreamDefaultControllerClearAlgorithms(this).
WritableStreamDefaultControllerClearAlgorithms(unwrappedController);
// Step 3: Return result.
return result;
}
/**
* Streams spec, 4.7.5.2.
* [[ErrorSteps]]()
*/
bool js::WritableStreamControllerErrorSteps(
JSContext* cx,
Handle<WritableStreamDefaultController*> unwrappedController) {
// Step 1: Perform ! ResetQueue(this).
return ResetQueue(cx, unwrappedController);
}
/*** 4.8. Writable stream default controller abstract operations ************/
static MOZ_MUST_USE bool WritableStreamDefaultControllerAdvanceQueueIfNeeded(
@ -224,16 +152,11 @@ MOZ_MUST_USE bool js::SetUpWritableStreamDefaultController(
SinkAlgorithms sinkAlgorithms, Handle<Value> underlyingSink,
Handle<Value> writeMethod, Handle<Value> closeMethod,
Handle<Value> abortMethod, double highWaterMark, Handle<Value> size) {
cx->check(stream);
cx->check(underlyingSink);
cx->check(writeMethod);
cx->check(stream, underlyingSink, size);
MOZ_ASSERT(writeMethod.isUndefined() || IsCallable(writeMethod));
cx->check(closeMethod);
MOZ_ASSERT(closeMethod.isUndefined() || IsCallable(closeMethod));
cx->check(abortMethod);
MOZ_ASSERT(abortMethod.isUndefined() || IsCallable(abortMethod));
MOZ_ASSERT(highWaterMark >= 0);
cx->check(size);
MOZ_ASSERT(size.isUndefined() || IsCallable(size));
// Done elsewhere in the standard: Create the new controller.
@ -418,97 +341,15 @@ void js::WritableStreamDefaultControllerClearAlgorithms(
* Streams spec, 4.8.5.
* WritableStreamDefaultControllerClose ( controller )
*/
bool js::WritableStreamDefaultControllerClose(
MOZ_MUST_USE bool js::WritableStreamDefaultControllerClose(
JSContext* cx,
Handle<WritableStreamDefaultController*> unwrappedController) {
// Step 1: Perform ! EnqueueValueWithSize(controller, "close", 0).
{
Rooted<Value> v(cx, MagicValue(JS_WRITABLESTREAM_CLOSE_RECORD));
Rooted<Value> size(cx, Int32Value(0));
if (!EnqueueValueWithSize(cx, unwrappedController, v, size)) {
return false;
}
}
// Step 2: Perform
// ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
return WritableStreamDefaultControllerAdvanceQueueIfNeeded(
cx, unwrappedController);
}
/**
* Streams spec, 4.8.6.
* WritableStreamDefaultControllerGetChunkSize ( controller, chunk )
*/
bool js::WritableStreamDefaultControllerGetChunkSize(
JSContext* cx, Handle<WritableStreamDefaultController*> unwrappedController,
Handle<Value> chunk, MutableHandle<Value> returnValue) {
cx->check(chunk);
// Step 1: Let returnValue be the result of performing
// controller.[[strategySizeAlgorithm]], passing in chunk, and
// interpreting the result as an ECMAScript completion value.
// We don't store a literal [[strategySizeAlgorithm]], only the value that if
// passed through |MakeSizeAlgorithmFromSizeFunction| wouldn't have triggered
// an error. Perform the algorithm that function would return.
Rooted<Value> unwrappedStrategySize(cx, unwrappedController->strategySize());
if (unwrappedStrategySize.isUndefined()) {
// 6.3.8 step 1: If size is undefined, return an algorithm that returns 1.
// ...and then from this function...
// Step 3: Return returnValue.[[Value]].
returnValue.setInt32(1);
return true;
}
MOZ_ASSERT(IsCallable(unwrappedStrategySize));
{
bool success;
{
AutoRealm ar(cx, unwrappedController);
cx->check(unwrappedStrategySize);
Rooted<Value> wrappedChunk(cx, chunk);
if (!cx->compartment()->wrap(cx, &wrappedChunk)) {
return false;
}
// 6.3.8 step 3 (of |MakeSizeAlgorithmFromSizeFunction|):
// Return an algorithm that performs the following steps, taking a
// chunk argument:
// a. Return ? Call(size, undefined, « chunk »).
success = Call(cx, unwrappedStrategySize, UndefinedHandleValue,
wrappedChunk, returnValue);
}
// Step 3: (If returnValue is [not] an abrupt completion, )
// Return returnValue.[[Value]]. (reordered for readability)
if (success) {
return cx->compartment()->wrap(cx, returnValue);
}
}
// Step 2: If returnValue is an abrupt completion,
if (!cx->isExceptionPending() || !cx->getPendingException(returnValue)) {
// Uncatchable error. Die immediately without erroring the stream.
return false;
}
cx->check(returnValue);
cx->clearPendingException();
// Step 2.a: Perform
// ! WritableStreamDefaultControllerErrorIfNeeded(
// controller, returnValue.[[Value]]).
if (!WritableStreamDefaultControllerErrorIfNeeded(cx, unwrappedController,
returnValue)) {
return false;
}
// Step 2.b: Return 1.
returnValue.setInt32(1);
return true;
// XXX jwalden fill me in!
JS_ReportErrorASCII(cx, "nope");
return false;
}
/**
@ -568,26 +409,6 @@ MOZ_MUST_USE bool WritableStreamDefaultControllerAdvanceQueueIfNeeded(
return false;
}
/**
* Streams spec, 4.8.10.
* WritableStreamDefaultControllerErrorIfNeeded ( controller, error )
*/
bool js::WritableStreamDefaultControllerErrorIfNeeded(
JSContext* cx, Handle<WritableStreamDefaultController*> unwrappedController,
Handle<Value> error) {
cx->check(error);
// Step 1: If controller.[[controlledWritableStream]].[[state]] is "writable",
// perform ! WritableStreamDefaultControllerError(controller, error).
if (unwrappedController->stream()->writable()) {
if (!WritableStreamDefaultControllerError(cx, unwrappedController, error)) {
return false;
}
}
return true;
}
/**
* Streams spec, 4.8.13.
* WritableStreamDefaultControllerGetBackpressure ( controller )

View File

@ -22,15 +22,6 @@ namespace js {
class WritableStream;
class WritableStreamDefaultController;
extern JSObject* WritableStreamControllerAbortSteps(
JSContext* cx,
JS::Handle<WritableStreamDefaultController*> unwrappedController,
JS::Handle<JS::Value> reason);
extern MOZ_MUST_USE bool WritableStreamControllerErrorSteps(
JSContext* cx,
JS::Handle<WritableStreamDefaultController*> unwrappedController);
extern MOZ_MUST_USE bool WritableStreamControllerStartHandler(JSContext* cx,
unsigned argc,
JS::Value* vp);
@ -68,19 +59,9 @@ extern MOZ_MUST_USE bool WritableStreamDefaultControllerClose(
JSContext* cx,
JS::Handle<WritableStreamDefaultController*> unwrappedController);
extern MOZ_MUST_USE bool WritableStreamDefaultControllerGetChunkSize(
JSContext* cx,
JS::Handle<WritableStreamDefaultController*> unwrappedController,
JS::Handle<JS::Value> chunk, JS::MutableHandle<JS::Value> returnValue);
extern double WritableStreamDefaultControllerGetDesiredSize(
const WritableStreamDefaultController* controller);
extern MOZ_MUST_USE bool WritableStreamDefaultControllerErrorIfNeeded(
JSContext* cx,
JS::Handle<WritableStreamDefaultController*> unwrappedController,
JS::Handle<JS::Value> error);
extern bool WritableStreamDefaultControllerGetBackpressure(
const WritableStreamDefaultController* unwrappedController);

View File

@ -11,49 +11,35 @@
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/Attributes.h" // MOZ_MUST_USE
#include <stdint.h> // uint32_t
#include "jsapi.h" // JS_ReportErrorASCII, JS_SetPrivate
#include "builtin/Promise.h" // js::PromiseObject
#include "builtin/streams/WritableStream.h" // js::WritableStream
#include "builtin/streams/WritableStreamDefaultController.h" // js::WritableStreamDefaultController, js::WritableStream::controller
#include "builtin/streams/WritableStreamDefaultControllerOperations.h" // js::WritableStreamControllerErrorSteps
#include "builtin/streams/WritableStreamWriterOperations.h" // js::WritableStreamDefaultWriterEnsureReadyPromiseRejected
#include "js/CallArgs.h" // JS::CallArgs{,FromVp}
#include "js/Promise.h" // JS::{Reject,Resolve}Promise
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
#include "js/Value.h" // JS::Value, JS::ObjecValue, JS::UndefinedHandleValue
#include "js/Value.h" // JS::Value, JS::ObjecValue
#include "vm/Compartment.h" // JS::Compartment
#include "vm/JSContext.h" // JSContext
#include "vm/List.h" // js::ListObject
#include "builtin/streams/HandlerFunction-inl.h" // js::NewHandler, js::TargetFromHandler
#include "builtin/streams/MiscellaneousOperations-inl.h" // js::ResolveUnwrappedPromiseWithUndefined, js::RejectUnwrappedPromiseWithError
#include "builtin/streams/WritableStream-inl.h" // js::UnwrapWriterFromStream
#include "builtin/streams/WritableStreamDefaultWriter-inl.h" // js::WritableStreamDefaultWriter::closedPromise
#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapAndDowncastObject
#include "vm/Compartment-inl.h" // JS::Compartment::wrap
#include "vm/JSContext-inl.h" // JSContext::check
#include "vm/JSObject-inl.h" // js::NewObjectWithClassProto
#include "vm/List-inl.h" // js::{AppendTo,StoreNew}ListInFixedSlot
#include "vm/Realm-inl.h" // js::AutoRealm
using js::ExtraFromHandler;
using js::PromiseObject;
using js::TargetFromHandler;
using js::UnwrapAndDowncastObject;
using js::WritableStream;
using js::WritableStreamDefaultController;
using js::WritableStreamRejectCloseAndClosedPromiseIfNeeded;
using JS::CallArgs;
using JS::CallArgsFromVp;
using JS::Handle;
using JS::ObjectValue;
using JS::RejectPromise;
using JS::ResolvePromise;
using JS::Rooted;
using JS::UndefinedHandleValue;
using JS::Value;
/*** 4.3. General writable stream abstract operations. **********************/
@ -119,84 +105,6 @@ void WritableStream::clearInFlightWriteRequest(JSContext* cx) {
MOZ_ASSERT(inFlightWriteRequest().isUndefined());
}
/**
* Streams spec, 4.3.6.
* WritableStreamAbort ( stream, reason )
*
* Note: The object (a promise) returned by this function is in the current
* compartment and does not require special wrapping to be put to use.
*/
JSObject* js::WritableStreamAbort(JSContext* cx,
Handle<WritableStream*> unwrappedStream,
Handle<Value> reason) {
cx->check(reason);
// Step 1: Let state be stream.[[state]].
// Step 2: If state is "closed" or "errored", return a promise resolved with
// undefined.
if (unwrappedStream->closed() || unwrappedStream->errored()) {
return PromiseObject::unforgeableResolve(cx, UndefinedHandleValue);
}
// Step 3: If stream.[[pendingAbortRequest]] is not undefined, return
// stream.[[pendingAbortRequest]].[[promise]].
if (unwrappedStream->hasPendingAbortRequest()) {
Rooted<JSObject*> pendingPromise(
cx, unwrappedStream->pendingAbortRequestPromise());
if (!cx->compartment()->wrap(cx, &pendingPromise)) {
return nullptr;
}
return pendingPromise;
}
// Step 4: Assert: state is "writable" or "erroring".
MOZ_ASSERT(unwrappedStream->writable() ^ unwrappedStream->erroring());
// Step 7: Let promise be a new promise (reordered).
Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
if (!promise) {
return nullptr;
}
// Step 5: Let wasAlreadyErroring be false.
// Step 6: If state is "erroring",
// Step 6.a: Set wasAlreadyErroring to true.
// Step 6.b: Set reason to undefined.
bool wasAlreadyErroring = unwrappedStream->erroring();
Handle<Value> pendingReason =
wasAlreadyErroring ? UndefinedHandleValue : reason;
// Step 8: Set stream.[[pendingAbortRequest]] to
// Record {[[promise]]: promise, [[reason]]: reason,
// [[wasAlreadyErroring]]: wasAlreadyErroring}.
{
AutoRealm ar(cx, unwrappedStream);
Rooted<JSObject*> wrappedPromise(cx, promise);
Rooted<Value> wrappedPendingReason(cx, pendingReason);
JS::Compartment* comp = cx->compartment();
if (!comp->wrap(cx, &wrappedPromise) ||
!comp->wrap(cx, &wrappedPendingReason)) {
return nullptr;
}
unwrappedStream->setPendingAbortRequest(
wrappedPromise, wrappedPendingReason, wasAlreadyErroring);
}
// Step 9: If wasAlreadyErroring is false, perform
// ! WritableStreamStartErroring(stream, reason).
if (!wasAlreadyErroring) {
if (!WritableStreamStartErroring(cx, unwrappedStream, pendingReason)) {
return nullptr;
}
}
// Step 10: Return promise.
return promise;
}
/*** 4.4. Writable stream abstract operations used by controllers ***********/
/**
@ -318,68 +226,6 @@ MOZ_MUST_USE bool js::WritableStreamStartErroring(
return true;
}
/**
* Streams spec, 4.4.4 WritableStreamFinishErroring ( stream )
* Step 13: Upon fulfillment of promise, [...]
*/
static bool AbortRequestPromiseFulfilledHandler(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 13.a: Resolve abortRequest.[[promise]] with undefined.
Rooted<JSObject*> abortRequestPromise(cx, TargetFromHandler<JSObject>(args));
if (!ResolvePromise(cx, abortRequestPromise, UndefinedHandleValue)) {
return false;
}
// Step 13.b: Perform
// ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
Rooted<WritableStream*> unwrappedStream(
cx, UnwrapAndDowncastObject<WritableStream>(
cx, ExtraFromHandler<JSObject>(args)));
if (!unwrappedStream) {
return false;
}
if (!WritableStreamRejectCloseAndClosedPromiseIfNeeded(cx, unwrappedStream)) {
return false;
}
args.rval().setUndefined();
return false;
}
/**
* Streams spec, 4.4.4 WritableStreamFinishErroring ( stream )
* Step 14: Upon rejection of promise with reason reason, [...]
*/
static bool AbortRequestPromiseRejectedHandler(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 14.a: Reject abortRequest.[[promise]] with reason.
Rooted<JSObject*> abortRequestPromise(cx, TargetFromHandler<JSObject>(args));
if (!RejectPromise(cx, abortRequestPromise, args.get(0))) {
return false;
}
// Step 14.b: Perform
// ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
Rooted<WritableStream*> unwrappedStream(
cx, UnwrapAndDowncastObject<WritableStream>(
cx, ExtraFromHandler<JSObject>(args)));
if (!unwrappedStream) {
return false;
}
if (!WritableStreamRejectCloseAndClosedPromiseIfNeeded(cx, unwrappedStream)) {
return false;
}
args.rval().setUndefined();
return false;
}
/**
* Streams spec, 4.4.4.
* WritableStreamFinishErroring ( stream )
@ -397,117 +243,32 @@ MOZ_MUST_USE bool js::WritableStreamFinishErroring(
unwrappedStream->setErrored();
// Step 4: Perform ! stream.[[writableStreamController]].[[ErrorSteps]]().
{
Rooted<WritableStreamDefaultController*> unwrappedController(
cx, unwrappedStream->controller());
if (!WritableStreamControllerErrorSteps(cx, unwrappedController)) {
return false;
}
}
// Step 5: Let storedError be stream.[[storedError]].
Rooted<Value> storedError(cx, unwrappedStream->storedError());
if (!cx->compartment()->wrap(cx, &storedError)) {
return false;
}
// Step 6: Repeat for each writeRequest that is an element of
// stream.[[writeRequests]],
{
Rooted<ListObject*> unwrappedWriteRequests(
cx, unwrappedStream->writeRequests());
Rooted<JSObject*> writeRequest(cx);
uint32_t len = unwrappedWriteRequests->length();
for (uint32_t i = 0; i < len; i++) {
// Step 6.a: Reject writeRequest with storedError.
writeRequest = &unwrappedWriteRequests->get(i).toObject();
if (!RejectUnwrappedPromiseWithError(cx, &writeRequest, storedError)) {
return false;
}
}
}
// a: Reject writeRequest with storedError.
// Step 7: Set stream.[[writeRequests]] to an empty List.
// We optimize this to discard the list entirely. (A brief scan of the
// streams spec should verify that [[writeRequests]] is never accessed on a
// stream when |stream.[[state]] === "errored"|, set in step 3 above.)
unwrappedStream->clearWriteRequests();
// Step 8: If stream.[[pendingAbortRequest]] is undefined,
if (!unwrappedStream->hasPendingAbortRequest()) {
// Step 8.a: Perform
// ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
// Step 8.b: Return.
return WritableStreamRejectCloseAndClosedPromiseIfNeeded(cx,
unwrappedStream);
}
// a: Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
// b: Return.
// Step 9: Let abortRequest be stream.[[pendingAbortRequest]].
// Step 10: Set stream.[[pendingAbortRequest]] to undefined.
Rooted<Value> abortRequestReason(
cx, unwrappedStream->pendingAbortRequestReason());
if (!cx->compartment()->wrap(cx, &abortRequestReason)) {
return false;
}
Rooted<JSObject*> abortRequestPromise(
cx, unwrappedStream->pendingAbortRequestPromise());
bool wasAlreadyErroring =
unwrappedStream->pendingAbortRequestWasAlreadyErroring();
unwrappedStream->clearPendingAbortRequest();
// Step 11: If abortRequest.[[wasAlreadyErroring]] is true,
if (wasAlreadyErroring) {
// Step 11.a: Reject abortRequest.[[promise]] with storedError.
if (!RejectUnwrappedPromiseWithError(cx, &abortRequestPromise,
storedError)) {
return false;
}
// Step 11.b: Perform
// ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
// Step 11.c: Return.
return WritableStreamRejectCloseAndClosedPromiseIfNeeded(cx,
unwrappedStream);
}
// a: Reject abortRequest.[[promise]] with storedError.
// b: Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
// c: Return.
// Step 12: Let promise be
// ! stream.[[writableStreamController]].[[AbortSteps]](
// abortRequest.[[reason]]).
Rooted<WritableStreamDefaultController*> unwrappedController(
cx, unwrappedStream->controller());
Rooted<JSObject*> promise(
cx, WritableStreamControllerAbortSteps(cx, unwrappedController,
abortRequestReason));
if (!promise) {
return false;
}
cx->check(promise);
if (!cx->compartment()->wrap(cx, &abortRequestPromise)) {
return false;
}
Rooted<JSObject*> stream(cx, unwrappedStream);
if (!cx->compartment()->wrap(cx, &stream)) {
return false;
}
// Step 13: Upon fulfillment of promise, [...]
// Step 14: Upon rejection of promise with reason reason, [...]
Rooted<JSObject*> onFulfilled(
cx, NewHandlerWithExtra(cx, AbortRequestPromiseFulfilledHandler,
abortRequestPromise, stream));
if (!onFulfilled) {
return false;
}
Rooted<JSObject*> onRejected(
cx, NewHandlerWithExtra(cx, AbortRequestPromiseRejectedHandler,
abortRequestPromise, stream));
if (!onRejected) {
return false;
}
return JS::AddPromiseReactions(cx, promise, onFulfilled, onRejected);
// Step 13: Upon fulfillment of promise,
// a: Resolve abortRequest.[[promise]] with undefined.
// b: Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
// Step 14: Upon rejection of promise with reason reason,
// c: Reject abortRequest.[[promise]] with reason.
// d: Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
// XXX jwalden flesh me out!
JS_ReportErrorASCII(cx, "epic fail");
return false;
}
/**

View File

@ -22,10 +22,6 @@ namespace js {
class PromiseObject;
class WritableStream;
extern JSObject* WritableStreamAbort(
JSContext* cx, JS::Handle<WritableStream*> unwrappedStream,
JS::Handle<JS::Value> reason);
extern MOZ_MUST_USE PromiseObject* WritableStreamAddWriteRequest(
JSContext* cx, JS::Handle<WritableStream*> unwrappedStream);

View File

@ -684,7 +684,6 @@ MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "Read
MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSINK_TYPE_WRONG, 0, JSEXN_RANGEERR,"'underlyingSink.type' must be undefined.")
MSG_DEF(JSMSG_WRITABLESTREAMWRITER_NOT_OWNED, 1, JSEXN_TYPEERR, "the WritableStream writer method '{0}' may only be called on a writer owned by a stream")
MSG_DEF(JSMSG_WRITABLESTREAM_CLOSED_OR_ERRORED, 0, JSEXN_TYPEERR, "writable stream is already closed or errored")
MSG_DEF(JSMSG_CANT_ABORT_LOCKED_WRITABLESTREAM, 0, JSEXN_TYPEERR, "can't abort a WritableStream that's locked to a writer")
MSG_DEF(JSMSG_READABLESTREAM_NYI, 0, JSEXN_ERR, "full WritableStream support is not yet implemented")
// Other Stream-related