author | Jeff Walden <jwalden@mit.edu> |
Thu, 07 Nov 2019 00:19:32 +0000 | |
changeset 501030 | 875c829c57ceb711fbd0f6ed36613cff83380fb1 |
parent 501029 | eadda7d0287cc25ef6d5eb332a98b0d7b6142eee |
child 501031 | 979d77ef56928092343868192f4d632ea074a892 |
push id | 99940 |
push user | jwalden@mit.edu |
push date | Thu, 07 Nov 2019 00:41:19 +0000 |
treeherder | autoland@8caeb3eb603c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | arai |
bugs | 1582348 |
milestone | 72.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/streams/WritableStream.cpp +++ b/js/src/builtin/streams/WritableStream.cpp @@ -14,17 +14,17 @@ #include "jsapi.h" // JS_ReportErrorNumberASCII #include "jsfriendapi.h" // js::GetErrorMessage, JSMSG_* #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/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 #include "js/RootingAPI.h" // JS::Handle, JS::Rooted #include "js/Value.h" // JS::{,Object}Value #include "vm/JSContext.h" // JSContext #include "vm/JSObject.h" // js::GetPrototypeFromBuiltinConstructor #include "vm/NativeObject.h" // js::PlainObject @@ -37,16 +37,18 @@ #include "vm/NativeObject-inl.h" // js::ThrowIfNotConstructing using js::CreateWritableStreamDefaultWriter; using js::GetErrorMessage; using js::ReturnPromiseRejectedWithPendingError; using js::UnwrapAndTypeCheckThis; using js::WritableStream; using js::WritableStreamAbort; +using js::WritableStreamClose; +using js::WritableStreamCloseQueuedOrInFlight; using JS::CallArgs; using JS::CallArgsFromVp; using JS::Handle; using JS::ObjectValue; using JS::Rooted; using JS::Value; @@ -184,33 +186,73 @@ static bool WritableStream_abort(JSConte 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); + JSMSG_CANT_USE_LOCKED_WRITABLESTREAM, "abort"); 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() + * 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); // Step 1: If ! WritableStream(this) is false, throw a TypeError exception. Rooted<WritableStream*> unwrappedStream( cx, UnwrapAndTypeCheckThis<WritableStream>(cx, args, "getWriter")); if (!unwrappedStream) { @@ -223,16 +265,17 @@ static bool WritableStream_getWriter(JSC } args.rval().setObject(*writer); return true; } 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[] = { JS_PSG("locked", WritableStream_locked, 0), JS_PS_END}; JS_STREAMS_CLASS_SPEC(WritableStream, 0, SlotCount, 0, JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_HAS_PRIVATE, JS_NULL_CLASS_OPS);
--- a/js/src/builtin/streams/WritableStreamOperations.cpp +++ b/js/src/builtin/streams/WritableStreamOperations.cpp @@ -9,20 +9,22 @@ #include "builtin/streams/WritableStreamOperations.h" #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 "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} #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 "vm/Compartment.h" // JS::Compartment #include "vm/JSContext.h" // JSContext @@ -192,16 +194,88 @@ JSObject* js::WritableStreamAbort(JSCont return nullptr; } } // Step 10: Return promise. 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 ***********/ /** * Streams spec, 4.4.1. * WritableStreamAddWriteRequest ( stream ) */ MOZ_MUST_USE PromiseObject* js::WritableStreamAddWriteRequest( JSContext* cx, Handle<WritableStream*> unwrappedStream) {
--- a/js/src/builtin/streams/WritableStreamOperations.h +++ b/js/src/builtin/streams/WritableStreamOperations.h @@ -21,16 +21,19 @@ namespace js { class PromiseObject; class WritableStream; 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); extern MOZ_MUST_USE bool WritableStreamDealWithRejection( JSContext* cx, JS::Handle<WritableStream*> unwrappedStream, JS::Handle<JS::Value> error); extern MOZ_MUST_USE bool WritableStreamStartErroring(
--- a/js/src/js.msg +++ b/js/src/js.msg @@ -684,17 +684,17 @@ MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_ MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "ReadableStream method {0} not yet implemented") // WritableStream 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_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") MSG_DEF(JSMSG_READABLESTREAM_NYI, 0, JSEXN_ERR, "full WritableStream support is not yet implemented") // Other Stream-related MSG_DEF(JSMSG_STREAM_INVALID_HIGHWATERMARK, 0, JSEXN_RANGEERR, "'highWaterMark' must be a non-negative, non-NaN number.") MSG_DEF(JSMSG_STREAM_CONSUME_ERROR, 0, JSEXN_TYPEERR, "error consuming stream body")