Bug 1582348 - Implement |WritableStreamDefaultWriter.prototype.close|. r=arai
authorJeff Walden <jwalden@mit.edu>
Tue, 05 Nov 2019 05:34:03 +0000
changeset 500535 31d663782160650d9cdba9a5ad25d03f3f9a6aef
parent 500534 1de9eca9cdab1c39cc6b3bca1e73a9ef494c15cc
child 500536 bf009da407c80e88d7cd20454e708bb81a3d2a5d
push id114166
push userapavel@mozilla.com
push dateThu, 07 Nov 2019 10:04:01 +0000
treeherdermozilla-inbound@d271c572a9bc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1582348
milestone72.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
Bug 1582348 - Implement |WritableStreamDefaultWriter.prototype.close|. r=arai Differential Revision: https://phabricator.services.mozilla.com/D50104
js/src/builtin/streams/WritableStreamDefaultWriter.cpp
js/src/js.msg
--- a/js/src/builtin/streams/WritableStreamDefaultWriter.cpp
+++ b/js/src/builtin/streams/WritableStreamDefaultWriter.cpp
@@ -10,16 +10,18 @@
 
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include "jsapi.h"        // JS_ReportErrorASCII, JS_ReportErrorNumberASCII
 #include "jsfriendapi.h"  // js::GetErrorMessage, JSMSG_*
 
 #include "builtin/streams/ClassSpecMacro.h"  // JS_STREAMS_CLASS_SPEC
 #include "builtin/streams/MiscellaneousOperations.h"  // js::ReturnPromiseRejectedWithPendingError
+#include "builtin/streams/WritableStream.h"           // js::WritableStream
+#include "builtin/streams/WritableStreamOperations.h"  // js::WritableStreamCloseQueuedOrInFlight
 #include "builtin/streams/WritableStreamWriterOperations.h"  // js::WritableStreamDefaultWriter{GetDesiredSize,Write}
 #include "js/CallArgs.h"                              // JS::CallArgs{,FromVp}
 #include "js/Class.h"                        // js::ClassSpec, JS_NULL_CLASS_OPS
 #include "js/PropertySpec.h"  // JS{Function,Property}Spec, JS_{FS,PS}_END, JS_{FN,PSG}
 #include "js/RootingAPI.h"    // JS::Handle
 #include "js/Value.h"         // JS::Value
 #include "vm/Compartment.h"   // JS::Compartment
 #include "vm/JSContext.h"     // JSContext
@@ -31,16 +33,18 @@ using JS::CallArgsFromVp;
 using JS::Handle;
 using JS::Rooted;
 using JS::Value;
 
 using js::ClassSpec;
 using js::GetErrorMessage;
 using js::ReturnPromiseRejectedWithPendingError;
 using js::UnwrapAndTypeCheckThis;
+using js::WritableStream;
+using js::WritableStreamCloseQueuedOrInFlight;
 using js::WritableStreamDefaultWriter;
 using js::WritableStreamDefaultWriterGetDesiredSize;
 using js::WritableStreamDefaultWriterWrite;
 
 /*** 4.5. Class WritableStreamDefaultWriter *********************************/
 
 MOZ_MUST_USE WritableStreamDefaultWriter* js::CreateWritableStreamDefaultWriter(
     JSContext* cx, Handle<WritableStream*> unwrappedStream) {
@@ -144,16 +148,65 @@ static MOZ_MUST_USE bool WritableStream_
     return false;
   }
 
   args.rval().setObject(*readyPromise);
   return true;
 }
 
 /**
+ * Streams spec, 4.5.4.5. close()
+ */
+static MOZ_MUST_USE bool WritableStream_close(JSContext* cx, unsigned argc,
+                                              Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+
+  // Step 1: If ! IsWritableStreamDefaultWriter(this) is false, return a promise
+  //         rejected with a TypeError exception.
+  Rooted<WritableStreamDefaultWriter*> unwrappedWriter(
+      cx,
+      UnwrapAndTypeCheckThis<WritableStreamDefaultWriter>(cx, args, "close"));
+  if (!unwrappedWriter) {
+    return ReturnPromiseRejectedWithPendingError(cx, args);
+  }
+
+  // Step 2: Let stream be this.[[ownerWritableStream]].
+  // Step 3: If stream is undefined, return a promise rejected with a TypeError
+  //         exception.
+  if (!unwrappedWriter->hasStream()) {
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                              JSMSG_WRITABLESTREAMWRITER_NOT_OWNED, "write");
+    return ReturnPromiseRejectedWithPendingError(cx, args);
+  }
+
+  WritableStream* unwrappedStream = UnwrapStreamFromWriter(cx, unwrappedWriter);
+  if (!unwrappedStream) {
+    return false;
+  }
+
+  // Step 4: If ! WritableStreamCloseQueuedOrInFlight(stream) 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 5: Return ! WritableStreamDefaultWriterClose(this).
+  JSObject* promise = WritableStreamDefaultWriterClose(cx, unwrappedWriter);
+  if (!promise) {
+    return false;
+  }
+  cx->check(promise);
+
+  args.rval().setObject(*promise);
+  return true;
+}
+
+/**
  * Streams spec, 4.5.4.7. write(chunk)
  */
 static MOZ_MUST_USE bool WritableStream_write(JSContext* cx, unsigned argc,
                                               Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Step 1: If ! IsWritableStreamDefaultWriter(this) is false, return a promise
   //         rejected with a TypeError exception.
@@ -185,12 +238,13 @@ static MOZ_MUST_USE bool WritableStream_
 }
 
 static const JSPropertySpec WritableStreamDefaultWriter_properties[] = {
     JS_PSG("closed", WritableStream_closed, 0),
     JS_PSG("desiredSize", WritableStream_desiredSize, 0),
     JS_PSG("ready", WritableStream_ready, 0), JS_PS_END};
 
 static const JSFunctionSpec WritableStreamDefaultWriter_methods[] = {
+    JS_FN("close", WritableStream_close, 0, 0),
     JS_FN("write", WritableStream_write, 1, 0), JS_FS_END};
 
 JS_STREAMS_CLASS_SPEC(WritableStreamDefaultWriter, 0, SlotCount,
                       ClassSpec::DontDefineConstructor, 0, JS_NULL_CLASS_OPS);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -685,16 +685,17 @@ MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_
 
 // 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_WRITABLESTREAM_CLOSE_CLOSING_OR_CLOSED,    0, JSEXN_TYPEERR, "can't close a stream that's currently closing or already closed")
 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")
 
 // Response-related
 MSG_DEF(JSMSG_ERROR_CONSUMING_RESPONSE,                  0, JSEXN_TYPEERR,  "there was an error consuming the Response")