Bug 1582348 - Implement WritableStreamClose and WritableStream.prototype.close. r=arai

Differential Revision: https://phabricator.services.mozilla.com/D51965

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jeff Walden 2019-11-07 00:19:32 +00:00
parent 939dba64a3
commit 713c122186
4 changed files with 125 additions and 5 deletions

View File

@ -19,7 +19,7 @@
#include "builtin/streams/MiscellaneousOperations.h" // js::MakeSizeAlgorithmFromSizeFunction, js::ReturnPromiseRejectedWithPendingError, js::ValidateAndNormalizeHighWaterMark
#include "builtin/streams/WritableStreamDefaultControllerOperations.h" // js::SetUpWritableStreamDefaultControllerFromUnderlyingSink
#include "builtin/streams/WritableStreamDefaultWriter.h" // js::CreateWritableStreamDefaultWriter
#include "builtin/streams/WritableStreamOperations.h" // js::WritableStreamAbort
#include "builtin/streams/WritableStreamOperations.h" // js::WritableStream{Abort,Close{,QueuedOrInFlight}}
#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
@ -42,6 +42,8 @@ using js::ReturnPromiseRejectedWithPendingError;
using js::UnwrapAndTypeCheckThis;
using js::WritableStream;
using js::WritableStreamAbort;
using js::WritableStreamClose;
using js::WritableStreamCloseQueuedOrInFlight;
using JS::CallArgs;
using JS::CallArgsFromVp;
@ -189,7 +191,7 @@ static bool WritableStream_abort(JSContext* cx, unsigned argc, Value* vp) {
// rejected with a TypeError exception.
if (unwrappedStream->isLocked()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_CANT_ABORT_LOCKED_WRITABLESTREAM);
JSMSG_CANT_USE_LOCKED_WRITABLESTREAM, "abort");
return ReturnPromiseRejectedWithPendingError(cx, args);
}
@ -205,7 +207,47 @@ static bool WritableStream_abort(JSContext* cx, unsigned argc, Value* vp) {
}
/**
* Streams spec, 4.2.5.3. getWriter()
* Streams spec, 4.2.5.3. close()
*/
static bool WritableStream_close(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1: If ! IsWritableStream(this) is false, return a promise rejected
// with a TypeError exception.
Rooted<WritableStream*> unwrappedStream(
cx, UnwrapAndTypeCheckThis<WritableStream>(cx, args, "close"));
if (!unwrappedStream) {
return ReturnPromiseRejectedWithPendingError(cx, args);
}
// 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_USE_LOCKED_WRITABLESTREAM, "close");
return ReturnPromiseRejectedWithPendingError(cx, args);
}
// Step 3: If ! WritableStreamCloseQueuedOrInFlight(this) is true, return a
// promise rejected with a TypeError exception.
if (WritableStreamCloseQueuedOrInFlight(unwrappedStream)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_WRITABLESTREAM_CLOSE_CLOSING_OR_CLOSED);
return ReturnPromiseRejectedWithPendingError(cx, args);
}
// Step 4: Return ! WritableStreamClose(this).
JSObject* promise = WritableStreamClose(cx, unwrappedStream);
if (!promise) {
return false;
}
args.rval().setObject(*promise);
return true;
}
/**
* Streams spec, 4.2.5.4. getWriter()
*/
static bool WritableStream_getWriter(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
@ -228,6 +270,7 @@ static bool WritableStream_getWriter(JSContext* cx, unsigned argc, Value* vp) {
static const JSFunctionSpec WritableStream_methods[] = {
JS_FN("abort", WritableStream_abort, 1, 0),
JS_FN("close", WritableStream_close, 0, 0),
JS_FN("getWriter", WritableStream_getWriter, 0, 0), JS_FS_END};
static const JSPropertySpec WritableStream_properties[] = {

View File

@ -14,10 +14,12 @@
#include <stdint.h> // uint32_t
#include "jsapi.h" // JS_ReportErrorASCII, JS_SetPrivate
#include "jsfriendapi.h" // js::GetErrorMessage, JSMSG_*
#include "builtin/Promise.h" // js::PromiseObject
#include "builtin/streams/MiscellaneousOperations.h" // js::PromiseRejectedWithPendingError
#include "builtin/streams/WritableStream.h" // js::WritableStream
#include "builtin/streams/WritableStreamDefaultController.h" // js::WritableStreamDefaultController, js::WritableStream::controller
#include "builtin/streams/WritableStreamDefaultController.h" // js::WritableStreamDefaultController{,Close}, js::WritableStream::controller
#include "builtin/streams/WritableStreamDefaultControllerOperations.h" // js::WritableStreamControllerErrorSteps
#include "builtin/streams/WritableStreamWriterOperations.h" // js::WritableStreamDefaultWriterEnsureReadyPromiseRejected
#include "js/CallArgs.h" // JS::CallArgs{,FromVp}
@ -197,6 +199,78 @@ JSObject* js::WritableStreamAbort(JSContext* cx,
return promise;
}
/**
* Streams spec, 4.3.7.
* WritableStreamClose ( stream )
*
* 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::WritableStreamClose(JSContext* cx,
Handle<WritableStream*> unwrappedStream) {
// Step 1: Let state be stream.[[state]].
// Step 2: If state is "closed" or "errored", return a promise rejected with a
// TypeError exception.
if (unwrappedStream->closed() || unwrappedStream->errored()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_WRITABLESTREAM_CLOSED_OR_ERRORED);
return PromiseRejectedWithPendingError(cx);
}
// Step 3: Assert: state is "writable" or "erroring".
MOZ_ASSERT(unwrappedStream->writable() ^ unwrappedStream->erroring());
// Step 4: Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
MOZ_ASSERT(!WritableStreamCloseQueuedOrInFlight(unwrappedStream));
// Step 5: Let promise be a new promise.
Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
if (!promise) {
return nullptr;
}
// Step 6: Set stream.[[closeRequest]] to promise.
{
AutoRealm ar(cx, unwrappedStream);
Rooted<JSObject*> wrappedPromise(cx, promise);
if (!cx->compartment()->wrap(cx, &wrappedPromise)) {
return nullptr;
}
unwrappedStream->setCloseRequest(promise);
}
// Step 7: Let writer be stream.[[writer]].
// Step 8: If writer is not undefined, and stream.[[backpressure]] is true,
// and state is "writable", resolve writer.[[readyPromise]] with
// undefined.
if (unwrappedStream->hasWriter() && unwrappedStream->backpressure() &&
unwrappedStream->writable()) {
Rooted<WritableStreamDefaultWriter*> unwrappedWriter(
cx, UnwrapWriterFromStream(cx, unwrappedStream));
if (!unwrappedWriter) {
return nullptr;
}
if (!ResolveUnwrappedPromiseWithUndefined(
cx, unwrappedWriter->readyPromise())) {
return nullptr;
}
}
// Step 9: Perform
// ! WritableStreamDefaultControllerClose(
// stream.[[writableStreamController]]).
Rooted<WritableStreamDefaultController*> unwrappedController(
cx, unwrappedStream->controller());
if (!WritableStreamDefaultControllerClose(cx, unwrappedController)) {
return nullptr;
}
// Step 10: Return promise.
return promise;
}
/*** 4.4. Writable stream abstract operations used by controllers ***********/
/**

View File

@ -26,6 +26,9 @@ extern JSObject* WritableStreamAbort(
JSContext* cx, JS::Handle<WritableStream*> unwrappedStream,
JS::Handle<JS::Value> reason);
extern JSObject* WritableStreamClose(
JSContext* cx, JS::Handle<WritableStream*> unwrappedStream);
extern MOZ_MUST_USE PromiseObject* WritableStreamAddWriteRequest(
JSContext* cx, JS::Handle<WritableStream*> unwrappedStream);

View File

@ -689,7 +689,7 @@ MSG_DEF(JSMSG_WRITABLESTREAMWRITER_NOT_OWNED, 1, JSEXN_TYPEERR, "the
MSG_DEF(JSMSG_WRITABLESTREAM_CLOSED_OR_ERRORED, 0, JSEXN_TYPEERR, "writable stream is already closed or errored")
MSG_DEF(JSMSG_WRITABLESTREAM_RELEASED_DURING_WRITE, 0, JSEXN_TYPEERR, "writer's lock on the stream was released before writing completed")
MSG_DEF(JSMSG_WRITABLESTREAM_WRITE_CLOSING_OR_CLOSED, 0, JSEXN_TYPEERR, "can't write to a stream that's currently closing or already closed")
MSG_DEF(JSMSG_CANT_ABORT_LOCKED_WRITABLESTREAM, 0, JSEXN_TYPEERR, "can't abort a WritableStream that's locked to a writer")
MSG_DEF(JSMSG_CANT_USE_LOCKED_WRITABLESTREAM, 1, JSEXN_TYPEERR, "can't {0} a WritableStream that's locked to a writer")
MSG_DEF(JSMSG_WRITABLESTREAM_CLOSE_CLOSING_OR_CLOSED, 0, JSEXN_TYPEERR, "can't close a stream that's currently closing or already closed")
MSG_DEF(JSMSG_WRITABLESTREAM_CANT_RELEASE_ALREADY_CLOSED,0, JSEXN_TYPEERR, "writer has already been released and can't be closed")
MSG_DEF(JSMSG_WRITABLESTREAM_ALREADY_LOCKED, 0, JSEXN_TYPEERR, "writable stream is already locked by another writer")