diff --git a/dom/base/BodyStream.h b/dom/base/BodyStream.h index 6038eb9d0dad..3dc0bc4f3653 100644 --- a/dom/base/BodyStream.h +++ b/dom/base/BodyStream.h @@ -8,7 +8,6 @@ #define mozilla_dom_BodyStream_h #include "jsapi.h" -#include "js/Stream.h" #include "mozilla/AlreadyAddRefed.h" #include "mozilla/dom/ByteStreamHelpers.h" #include "mozilla/dom/BindingDeclarations.h" diff --git a/dom/fetch/FetchStreamReader.cpp b/dom/fetch/FetchStreamReader.cpp index 25c60246276e..7c25a72fd823 100644 --- a/dom/fetch/FetchStreamReader.cpp +++ b/dom/fetch/FetchStreamReader.cpp @@ -6,7 +6,6 @@ #include "FetchStreamReader.h" #include "InternalResponse.h" -#include "js/Stream.h" #include "mozilla/ConsoleReportCollector.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/AutoEntryScript.h" diff --git a/js/moz.configure b/js/moz.configure index 9a231c8b4265..17571bf66ee3 100644 --- a/js/moz.configure +++ b/js/moz.configure @@ -168,24 +168,6 @@ def enable_decorators(value): set_config("ENABLE_DECORATORS", enable_decorators) set_define("ENABLE_DECORATORS", enable_decorators) -# Enable JS Streams -# =================================================== -option( - "--enable-js-streams", - default=False, - help="Enable non-default JS Streams implementation", -) - - -@depends("--enable-js-streams") -def enable_js_streams(value): - if value: - return True - - -set_config("MOZ_JS_STREAMS", enable_js_streams) -set_define("MOZ_JS_STREAMS", enable_js_streams) - # JIT support # ======================================================= @depends(target, "--enable-record-tuple") diff --git a/js/public/ProtoKey.h b/js/public/ProtoKey.h index 8b96f1621ce7..ec8c0925254f 100644 --- a/js/public/ProtoKey.h +++ b/js/public/ProtoKey.h @@ -46,94 +46,84 @@ # define IF_WASM_TYPE(REAL, IMAGINARY) IMAGINARY #endif -#define JS_FOR_PROTOTYPES_(REAL, IMAGINARY, REAL_IF_INTL, REAL_IF_WASM_TYPE) \ - IMAGINARY(Null, dummy) \ - REAL(Object, OCLASP(Plain)) \ - REAL(Function, &FunctionClass) \ - REAL(Array, OCLASP(Array)) \ - REAL(Boolean, OCLASP(Boolean)) \ - REAL(JSON, CLASP(JSON)) \ - REAL(Date, OCLASP(Date)) \ - REAL(Math, CLASP(Math)) \ - REAL(Number, OCLASP(Number)) \ - REAL(String, OCLASP(String)) \ - REAL(RegExp, OCLASP(RegExp)) \ - REAL(Error, ERROR_CLASP(JSEXN_ERR)) \ - REAL(InternalError, ERROR_CLASP(JSEXN_INTERNALERR)) \ - REAL(AggregateError, ERROR_CLASP(JSEXN_AGGREGATEERR)) \ - REAL(EvalError, ERROR_CLASP(JSEXN_EVALERR)) \ - REAL(RangeError, ERROR_CLASP(JSEXN_RANGEERR)) \ - REAL(ReferenceError, ERROR_CLASP(JSEXN_REFERENCEERR)) \ - REAL(SyntaxError, ERROR_CLASP(JSEXN_SYNTAXERR)) \ - REAL(TypeError, ERROR_CLASP(JSEXN_TYPEERR)) \ - REAL(URIError, ERROR_CLASP(JSEXN_URIERR)) \ - REAL(DebuggeeWouldRun, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN)) \ - REAL(CompileError, ERROR_CLASP(JSEXN_WASMCOMPILEERROR)) \ - REAL(LinkError, ERROR_CLASP(JSEXN_WASMLINKERROR)) \ - REAL(RuntimeError, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR)) \ - REAL(ArrayBuffer, OCLASP(ArrayBuffer)) \ - REAL(Int8Array, TYPED_ARRAY_CLASP(Int8)) \ - REAL(Uint8Array, TYPED_ARRAY_CLASP(Uint8)) \ - REAL(Int16Array, TYPED_ARRAY_CLASP(Int16)) \ - REAL(Uint16Array, TYPED_ARRAY_CLASP(Uint16)) \ - REAL(Int32Array, TYPED_ARRAY_CLASP(Int32)) \ - REAL(Uint32Array, TYPED_ARRAY_CLASP(Uint32)) \ - REAL(Float32Array, TYPED_ARRAY_CLASP(Float32)) \ - REAL(Float64Array, TYPED_ARRAY_CLASP(Float64)) \ - REAL(Uint8ClampedArray, TYPED_ARRAY_CLASP(Uint8Clamped)) \ - REAL(BigInt64Array, TYPED_ARRAY_CLASP(BigInt64)) \ - REAL(BigUint64Array, TYPED_ARRAY_CLASP(BigUint64)) \ - REAL(BigInt, OCLASP(BigInt)) \ - REAL(Proxy, CLASP(Proxy)) \ - REAL(WeakMap, OCLASP(WeakMap)) \ - REAL(Map, OCLASP(Map)) \ - REAL(Set, OCLASP(Set)) \ - REAL(DataView, OCLASP(DataView)) \ - REAL(Symbol, OCLASP(Symbol)) \ - REAL(ShadowRealm, OCLASP(ShadowRealm)) \ - REAL(SharedArrayBuffer, OCLASP(SharedArrayBuffer)) \ - REAL_IF_INTL(Intl, CLASP(Intl)) \ - REAL_IF_INTL(Collator, OCLASP(Collator)) \ - REAL_IF_INTL(DateTimeFormat, OCLASP(DateTimeFormat)) \ - REAL_IF_INTL(DisplayNames, OCLASP(DisplayNames)) \ - REAL_IF_INTL(ListFormat, OCLASP(ListFormat)) \ - REAL_IF_INTL(Locale, OCLASP(Locale)) \ - REAL_IF_INTL(NumberFormat, OCLASP(NumberFormat)) \ - REAL_IF_INTL(PluralRules, OCLASP(PluralRules)) \ - REAL_IF_INTL(RelativeTimeFormat, OCLASP(RelativeTimeFormat)) \ - REAL(Reflect, CLASP(Reflect)) \ - REAL(WeakSet, OCLASP(WeakSet)) \ - REAL(TypedArray, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \ - REAL(Atomics, OCLASP(Atomics)) \ - REAL(SavedFrame, &js::SavedFrame::class_) \ - REAL(Promise, OCLASP(Promise)) \ - REAL(AsyncFunction, CLASP(AsyncFunction)) \ - REAL(GeneratorFunction, CLASP(GeneratorFunction)) \ - REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction)) \ - IF_JS_STREAMS(REAL(ReadableStream, &js::ReadableStream::class_)) \ - IF_JS_STREAMS(REAL(ReadableStreamDefaultReader, \ - &js::ReadableStreamDefaultReader::class_)) \ - IF_JS_STREAMS(REAL(ReadableStreamDefaultController, \ - &js::ReadableStreamDefaultController::class_)) \ - IF_JS_STREAMS(REAL(ReadableByteStreamController, \ - &js::ReadableByteStreamController::class_)) \ - IF_JS_STREAMS( \ - REAL(ByteLengthQueuingStrategy, &js::ByteLengthQueuingStrategy::class_)) \ - IF_JS_STREAMS(REAL(CountQueuingStrategy, &js::CountQueuingStrategy::class_)) \ - REAL(WebAssembly, OCLASP(WasmNamespace)) \ - REAL(WasmModule, OCLASP(WasmModule)) \ - REAL(WasmInstance, OCLASP(WasmInstance)) \ - REAL(WasmMemory, OCLASP(WasmMemory)) \ - REAL(WasmTable, OCLASP(WasmTable)) \ - REAL(WasmGlobal, OCLASP(WasmGlobal)) \ - REAL(WasmTag, OCLASP(WasmTag)) \ - REAL_IF_WASM_TYPE(WasmFunction, CLASP(WasmFunction)) \ - REAL(WasmException, OCLASP(WasmException)) \ - REAL(FinalizationRegistry, OCLASP(FinalizationRegistry)) \ - REAL(WeakRef, OCLASP(WeakRef)) \ - REAL(Iterator, OCLASP(Iterator)) \ - REAL(AsyncIterator, OCLASP(AsyncIterator)) \ - IF_RECORD_TUPLE(REAL(Record, (&RecordType::class_))) \ +#define JS_FOR_PROTOTYPES_(REAL, IMAGINARY, REAL_IF_INTL, REAL_IF_WASM_TYPE) \ + IMAGINARY(Null, dummy) \ + REAL(Object, OCLASP(Plain)) \ + REAL(Function, &FunctionClass) \ + REAL(Array, OCLASP(Array)) \ + REAL(Boolean, OCLASP(Boolean)) \ + REAL(JSON, CLASP(JSON)) \ + REAL(Date, OCLASP(Date)) \ + REAL(Math, CLASP(Math)) \ + REAL(Number, OCLASP(Number)) \ + REAL(String, OCLASP(String)) \ + REAL(RegExp, OCLASP(RegExp)) \ + REAL(Error, ERROR_CLASP(JSEXN_ERR)) \ + REAL(InternalError, ERROR_CLASP(JSEXN_INTERNALERR)) \ + REAL(AggregateError, ERROR_CLASP(JSEXN_AGGREGATEERR)) \ + REAL(EvalError, ERROR_CLASP(JSEXN_EVALERR)) \ + REAL(RangeError, ERROR_CLASP(JSEXN_RANGEERR)) \ + REAL(ReferenceError, ERROR_CLASP(JSEXN_REFERENCEERR)) \ + REAL(SyntaxError, ERROR_CLASP(JSEXN_SYNTAXERR)) \ + REAL(TypeError, ERROR_CLASP(JSEXN_TYPEERR)) \ + REAL(URIError, ERROR_CLASP(JSEXN_URIERR)) \ + REAL(DebuggeeWouldRun, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN)) \ + REAL(CompileError, ERROR_CLASP(JSEXN_WASMCOMPILEERROR)) \ + REAL(LinkError, ERROR_CLASP(JSEXN_WASMLINKERROR)) \ + REAL(RuntimeError, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR)) \ + REAL(ArrayBuffer, OCLASP(ArrayBuffer)) \ + REAL(Int8Array, TYPED_ARRAY_CLASP(Int8)) \ + REAL(Uint8Array, TYPED_ARRAY_CLASP(Uint8)) \ + REAL(Int16Array, TYPED_ARRAY_CLASP(Int16)) \ + REAL(Uint16Array, TYPED_ARRAY_CLASP(Uint16)) \ + REAL(Int32Array, TYPED_ARRAY_CLASP(Int32)) \ + REAL(Uint32Array, TYPED_ARRAY_CLASP(Uint32)) \ + REAL(Float32Array, TYPED_ARRAY_CLASP(Float32)) \ + REAL(Float64Array, TYPED_ARRAY_CLASP(Float64)) \ + REAL(Uint8ClampedArray, TYPED_ARRAY_CLASP(Uint8Clamped)) \ + REAL(BigInt64Array, TYPED_ARRAY_CLASP(BigInt64)) \ + REAL(BigUint64Array, TYPED_ARRAY_CLASP(BigUint64)) \ + REAL(BigInt, OCLASP(BigInt)) \ + REAL(Proxy, CLASP(Proxy)) \ + REAL(WeakMap, OCLASP(WeakMap)) \ + REAL(Map, OCLASP(Map)) \ + REAL(Set, OCLASP(Set)) \ + REAL(DataView, OCLASP(DataView)) \ + REAL(Symbol, OCLASP(Symbol)) \ + REAL(ShadowRealm, OCLASP(ShadowRealm)) \ + REAL(SharedArrayBuffer, OCLASP(SharedArrayBuffer)) \ + REAL_IF_INTL(Intl, CLASP(Intl)) \ + REAL_IF_INTL(Collator, OCLASP(Collator)) \ + REAL_IF_INTL(DateTimeFormat, OCLASP(DateTimeFormat)) \ + REAL_IF_INTL(DisplayNames, OCLASP(DisplayNames)) \ + REAL_IF_INTL(ListFormat, OCLASP(ListFormat)) \ + REAL_IF_INTL(Locale, OCLASP(Locale)) \ + REAL_IF_INTL(NumberFormat, OCLASP(NumberFormat)) \ + REAL_IF_INTL(PluralRules, OCLASP(PluralRules)) \ + REAL_IF_INTL(RelativeTimeFormat, OCLASP(RelativeTimeFormat)) \ + REAL(Reflect, CLASP(Reflect)) \ + REAL(WeakSet, OCLASP(WeakSet)) \ + REAL(TypedArray, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \ + REAL(Atomics, OCLASP(Atomics)) \ + REAL(SavedFrame, &js::SavedFrame::class_) \ + REAL(Promise, OCLASP(Promise)) \ + REAL(AsyncFunction, CLASP(AsyncFunction)) \ + REAL(GeneratorFunction, CLASP(GeneratorFunction)) \ + REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction)) \ + REAL(WebAssembly, OCLASP(WasmNamespace)) \ + REAL(WasmModule, OCLASP(WasmModule)) \ + REAL(WasmInstance, OCLASP(WasmInstance)) \ + REAL(WasmMemory, OCLASP(WasmMemory)) \ + REAL(WasmTable, OCLASP(WasmTable)) \ + REAL(WasmGlobal, OCLASP(WasmGlobal)) \ + REAL(WasmTag, OCLASP(WasmTag)) \ + REAL_IF_WASM_TYPE(WasmFunction, CLASP(WasmFunction)) \ + REAL(WasmException, OCLASP(WasmException)) \ + REAL(FinalizationRegistry, OCLASP(FinalizationRegistry)) \ + REAL(WeakRef, OCLASP(WeakRef)) \ + REAL(Iterator, OCLASP(Iterator)) \ + REAL(AsyncIterator, OCLASP(AsyncIterator)) \ + IF_RECORD_TUPLE(REAL(Record, (&RecordType::class_))) \ IF_RECORD_TUPLE(REAL(Tuple, (&TupleType::class_))) #define JS_FOR_PROTOTYPES(REAL, IMAGINARY) \ diff --git a/js/public/RealmOptions.h b/js/public/RealmOptions.h index e4151a72d682..f9f74daf795d 100644 --- a/js/public/RealmOptions.h +++ b/js/public/RealmOptions.h @@ -172,19 +172,6 @@ class JS_PUBLIC_API RealmCreationOptions { bool getCoopAndCoepEnabled() const; RealmCreationOptions& setCoopAndCoepEnabled(bool flag); - bool getStreamsEnabled() const { return streams_; } - RealmCreationOptions& setStreamsEnabled(bool flag) { -#ifdef MOZ_JS_STREAMS -# ifdef MOZ_DOM_STREAMS -# error "JS and DOM streams shouldn't be simultaneously configured" -# endif - streams_ = flag; -#else - MOZ_ASSERT(!streams_); -#endif - return *this; - } - WeakRefSpecifier getWeakRefsEnabled() const { return weakRefs_; } RealmCreationOptions& setWeakRefsEnabled(WeakRefSpecifier spec) { weakRefs_ = spec; @@ -280,7 +267,6 @@ class JS_PUBLIC_API RealmCreationOptions { bool sharedMemoryAndAtomics_ = false; bool defineSharedArrayBufferConstructor_ = true; bool coopAndCoep_ = false; - bool streams_ = false; bool toSource_ = false; bool propertyErrorMessageFix_ = false; bool iteratorHelpers_ = false; diff --git a/js/public/Stream.h b/js/public/Stream.h deleted file mode 100644 index 1b889d248848..000000000000 --- a/js/public/Stream.h +++ /dev/null @@ -1,491 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* - * JSAPI functions and callbacks related to WHATWG Stream objects. - * - * Much of the API here mirrors the standard algorithms and standard JS methods - * of the objects defined in the Streams standard. One difference is that the - * functionality of the JS controller object is exposed to C++ as functions - * taking ReadableStream instances instead, for convenience. - */ - -#ifndef js_Stream_h -#define js_Stream_h - -#include - -#include "jstypes.h" - -#include "js/RootingAPI.h" -#include "js/TypeDecls.h" - -struct JSClass; - -namespace JS { - -/** - * Abstract base class for external underlying sources. - * - * The term "underlying source" is defined in the Streams spec: - * https://streams.spec.whatwg.org/#underlying-source - * - * A `ReadableStreamUnderlyingSource` is an underlying source that is - * implemented in C++ rather than JS. It can be passed to - * `JS::NewReadableExternalSourceStreamObject` to create a custom, - * embedding-defined ReadableStream. - * - * There are several API difference between this class and the standard API for - * underlying sources implemented in JS: - * - * - JS underlying sources can be either byte sources or non-byte sources. - * External underlying source are always byte sources. - * - * - The C++ API does not bother with controller objects. Instead of using - * controller methods, the underlying source directly calls API functions - * like JS::ReadableStream{UpdateDataAvailableFromSource,Close,Error}. - * - * - External readable streams are optimized to allow the embedding to - * interact with them with a minimum of overhead: chunks aren't enqueued as - * individual typed arrays; instead, the embedding only updates the amount - * of data available using - * JS::ReadableStreamUpdateDataAvailableFromSource. When JS requests data - * from a reader, writeIntoReadRequestBuffer is invoked, asking the - * embedding to write data directly into the buffer we're about to hand to - * JS. - * - * - The C++ API provides extra callbacks onClosed() and onErrored(). - * - * - This class has a `finalize()` method, because C++ cares about lifetimes. - * - * Additionally, ReadableStreamGetExternalUnderlyingSource can be used to get - * the pointer to the underlying source. This locks the stream until it is - * released again using JS::ReadableStreamReleaseExternalUnderlyingSource. - * - * Embeddings can use this to optimize away the JS `ReadableStream` overhead - * when an embedding-defined C++ stream is passed to an embedding-defined C++ - * consumer. For example, consider a ServiceWorker piping a `fetch` Response - * body to a TextDecoder. Instead of copying chunks of data into JS typed array - * buffers and creating a Promise per chunk, only to immediately resolve the - * Promises and read the data out again, the embedding can directly feed the - * incoming data to the TextDecoder. - * - * Compartment safety: All methods (except `finalize`) receive `cx` and - * `stream` arguments. SpiderMonkey enters the realm of the stream object - * before invoking these methods, so `stream` is never a wrapper. Other - * arguments may be wrappers. - */ -class JS_PUBLIC_API ReadableStreamUnderlyingSource { - public: - virtual ~ReadableStreamUnderlyingSource() = default; - - /** - * Invoked whenever a reader desires more data from this source. - * - * The given `desiredSize` is the absolute size, not a delta from the - * previous desired size. - */ - virtual void requestData(JSContext* cx, HandleObject stream, - size_t desiredSize) = 0; - - /** - * Invoked to cause the embedding to fill the given `buffer` with data from - * this underlying source. - * - * This is called only after the embedding has updated the amount of data - * available using JS::ReadableStreamUpdateDataAvailableFromSource. If at - * least one read request is pending when - * JS::ReadableStreamUpdateDataAvailableFromSource is called, this method - * is invoked immediately from under the call to - * JS::ReadableStreamUpdateDataAvailableFromSource. If not, it is invoked - * if and when a new read request is made. - * - */ - virtual void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream, - JS::Handle aChunk, - size_t length, - size_t* bytesWritten) = 0; - - /** - * Invoked in reaction to the ReadableStream being canceled. This is - * equivalent to the `cancel` method on non-external underlying sources - * provided to the ReadableStream constructor in JavaScript. - * - * The underlying source may free up some resources in this method, but - * `*this` must not be destroyed until `finalize()` is called. - * - * The given `reason` is the JS::Value that was passed as an argument to - * ReadableStream#cancel(). - * - * The returned JS::Value will be used to resolve the Promise returned by - * ReadableStream#cancel(). - */ - virtual Value cancel(JSContext* cx, HandleObject stream, - HandleValue reason) = 0; - - /** - * Invoked when the associated ReadableStream becomes closed. - * - * The underlying source may free up some resources in this method, but - * `*this` must not be destroyed until `finalize()` is called. - */ - virtual void onClosed(JSContext* cx, HandleObject stream) = 0; - - /** - * Invoked when the associated ReadableStream becomes errored. - * - * The underlying source may free up some resources in this method, but - * `*this` must not be destroyed until `finalize()` is called. - */ - virtual void onErrored(JSContext* cx, HandleObject stream, - HandleValue reason) = 0; - - /** - * Invoked when the associated ReadableStream object is finalized. The - * stream object is not passed as an argument, as it might not be in a - * valid state anymore. - * - * Note: Finalization can happen on a background thread, so the embedding - * must be prepared for `finalize()` to be invoked from any thread. - */ - virtual void finalize() = 0; -}; - -/** - * Returns a new instance of the ReadableStream builtin class in the current - * compartment, configured as a default stream. - * If a |proto| is passed, that gets set as the instance's [[Prototype]] - * instead of the original value of |ReadableStream.prototype|. - */ -extern JS_PUBLIC_API JSObject* NewReadableDefaultStreamObject( - JSContext* cx, HandleObject underlyingSource = nullptr, - HandleFunction size = nullptr, double highWaterMark = 1, - HandleObject proto = nullptr); - -/** - * Returns a new instance of the ReadableStream builtin class in the current - * compartment. - * - * The instance is a byte stream backed by an embedding-provided underlying - * source, using the virtual methods of `underlyingSource` as callbacks. The - * embedding must ensure that `*underlyingSource` lives as long as the new - * stream object. The JS engine will call the finalize() method when the stream - * object is destroyed. - * - * `nsISupportsObject_alreadyAddreffed` is an optional pointer that can be used - * to make the new stream participate in Gecko's cycle collection. Here are the - * rules for using this parameter properly: - * - * - `*underlyingSource` must not be a cycle-collected object. (It would lead - * to memory leaks as the cycle collector would not be able to collect - * cycles containing that object.) - * - * - `*underlyingSource` must not contain nsCOMPtrs that point to cycle- - * collected objects. (Same reason.) - * - * - `*underlyingSource` may contain a pointer to a single cycle-collected - * object. - * - * - The pointer may be stored in `*underlyingSource` as a raw pointer. - * - * - The pointer to the nsISupports interface of the same object must be - * passed as the `nsISupportsObject_alreadyAddreffed` parameter to this - * function. (This is how the cycle collector knows about it, so omitting - * this would again cause leaks.) - * - * If `proto` is non-null, it is used as the instance's [[Prototype]] instead - * of the original value of `ReadableStream.prototype`. - */ -extern JS_PUBLIC_API JSObject* NewReadableExternalSourceStreamObject( - JSContext* cx, ReadableStreamUnderlyingSource* underlyingSource, - void* nsISupportsObject_alreadyAddreffed = nullptr, - HandleObject proto = nullptr); - -/** - * Returns the embedding-provided underlying source of the given |stream|. - * - * Can be used to optimize operations if both the underlying source and the - * intended sink are embedding-provided. In that case it might be - * preferrable to pipe data directly from source to sink without interacting - * with the stream at all. - * - * Locks the stream until ReadableStreamReleaseExternalUnderlyingSource is - * called. - * - * Throws an exception if the stream is locked, i.e. if a reader has been - * acquired for the stream, or if ReadableStreamGetExternalUnderlyingSource - * has been used previously without releasing the external source again. - * - * Throws an exception if the stream isn't readable, i.e if it is errored or - * closed. This is different from ReadableStreamGetReader because we don't - * have a Promise to resolve/reject, which a reader provides. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - * - * Asserts that the stream has an embedding-provided underlying source. - */ -extern JS_PUBLIC_API bool ReadableStreamGetExternalUnderlyingSource( - JSContext* cx, HandleObject stream, - ReadableStreamUnderlyingSource** source); - -/** - * Releases the embedding-provided underlying source of the given |stream|, - * returning the stream into an unlocked state. - * - * Asserts that the stream was locked through - * ReadableStreamGetExternalUnderlyingSource. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - * - * Asserts that the stream has an embedding-provided underlying source. - */ -extern JS_PUBLIC_API bool ReadableStreamReleaseExternalUnderlyingSource( - JSContext* cx, HandleObject stream); - -/** - * Update the amount of data available at the underlying source of the given - * |stream|. - * - * Can only be used for streams with an embedding-provided underlying source. - * The JS engine will use the given value to satisfy read requests for the - * stream by invoking the writeIntoReadRequestBuffer method. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamUpdateDataAvailableFromSource( - JSContext* cx, HandleObject stream, uint32_t availableData); - -/** - * Break the cycle between this object and the - * nsISupportsObject_alreadyAddreffed passed in - * NewReadableExternalSourceStreamObject(). - */ -extern JS_PUBLIC_API void ReadableStreamReleaseCCObject(JSObject* stream); - -/** - * Returns true if the given object is a ReadableStream object or an - * unwrappable wrapper for one, false otherwise. - */ -extern JS_PUBLIC_API bool IsReadableStream(JSObject* obj); - -/** - * Returns true if the given object is a ReadableStreamDefaultReader or - * ReadableStreamBYOBReader object or an unwrappable wrapper for one, false - * otherwise. - */ -extern JS_PUBLIC_API bool IsReadableStreamReader(JSObject* obj); - -/** - * Returns true if the given object is a ReadableStreamDefaultReader object - * or an unwrappable wrapper for one, false otherwise. - */ -extern JS_PUBLIC_API bool IsReadableStreamDefaultReader(JSObject* obj); - -enum class ReadableStreamMode { Default, Byte, ExternalSource }; - -/** - * Returns the stream's ReadableStreamMode. If the mode is |Byte| or - * |ExternalSource|, it's possible to acquire a BYOB reader for more optimized - * operations. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamGetMode(JSContext* cx, - HandleObject stream, - ReadableStreamMode* mode); - -enum class ReadableStreamReaderMode { Default, Byob }; - -/** - * Returns true if the given ReadableStream is readable, false if not. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamIsReadable(JSContext* cx, - HandleObject stream, - bool* result); - -/** - * Returns true if the given ReadableStream is locked, false if not. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamIsLocked(JSContext* cx, - HandleObject stream, - bool* result); - -/** - * Returns true if the given ReadableStream is disturbed, false if not. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamIsDisturbed(JSContext* cx, - HandleObject stream, - bool* result); - -/** - * Cancels the given ReadableStream with the given reason and returns a - * Promise resolved according to the result. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API JSObject* ReadableStreamCancel(JSContext* cx, - HandleObject stream, - HandleValue reason); - -/** - * Creates a reader of the type specified by the mode option and locks the - * stream to the new reader. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. The returned object will always be created in the - * current cx compartment. - */ -extern JS_PUBLIC_API JSObject* ReadableStreamGetReader( - JSContext* cx, HandleObject stream, ReadableStreamReaderMode mode); - -/** - * Tees the given ReadableStream and stores the two resulting streams in - * outparams. Returns false if the operation fails, e.g. because the stream is - * locked. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamTee(JSContext* cx, HandleObject stream, - MutableHandleObject branch1Stream, - MutableHandleObject branch2Stream); - -/** - * Retrieves the desired combined size of additional chunks to fill the given - * ReadableStream's queue. Stores the result in |value| and sets |hasValue| to - * true on success, returns false on failure. - * - * If the stream is errored, the call will succeed but no value will be stored - * in |value| and |hasValue| will be set to false. - * - * Note: This is semantically equivalent to the |desiredSize| getter on - * the stream controller's prototype in JS. We expose it with the stream - * itself as a target for simplicity. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamGetDesiredSize(JSContext* cx, - JSObject* stream, - bool* hasValue, - double* value); - -/** - * Close the given ReadableStream. This is equivalent to `controller.close()` - * in JS. - * - * This can fail with or without an exception pending under a variety of - * circumstances. On failure, the stream may or may not be closed, and - * downstream consumers may or may not have been notified. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamClose(JSContext* cx, - HandleObject stream); - -/** - * Returns true if the given ReadableStream reader is locked, false otherwise. - * - * Asserts that |reader| is a ReadableStreamDefaultReader or - * ReadableStreamBYOBReader object or an unwrappable wrapper for one. - */ -extern JS_PUBLIC_API bool ReadableStreamReaderIsClosed(JSContext* cx, - HandleObject reader, - bool* result); - -/** - * Enqueues the given chunk in the given ReadableStream. - * - * Throws a TypeError and returns false if the enqueing operation fails. - * - * Note: This is semantically equivalent to the |enqueue| method on - * the stream controller's prototype in JS. We expose it with the stream - * itself as a target for simplicity. - * - * If the ReadableStream has an underlying byte source, the given chunk must - * be a typed array or a DataView. Consider using - * ReadableByteStreamEnqueueBuffer. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamEnqueue(JSContext* cx, - HandleObject stream, - HandleValue chunk); - -/** - * Errors the given ReadableStream, causing all future interactions to fail - * with the given error value. - * - * Throws a TypeError and returns false if the erroring operation fails. - * - * Note: This is semantically equivalent to the |error| method on - * the stream controller's prototype in JS. We expose it with the stream - * itself as a target for simplicity. - * - * Asserts that |stream| is a ReadableStream object or an unwrappable wrapper - * for one. - */ -extern JS_PUBLIC_API bool ReadableStreamError(JSContext* cx, - HandleObject stream, - HandleValue error); - -/** - * C++ equivalent of `reader.cancel(reason)` - * (both and - * ). - * - * `reader` must be a stream reader created using `JS::ReadableStreamGetReader` - * or an unwrappable wrapper for one. (This function is meant to support using - * C++ to read from streams. It's not meant to allow C++ code to operate on - * readers created by scripts.) - */ -extern JS_PUBLIC_API bool ReadableStreamReaderCancel(JSContext* cx, - HandleObject reader, - HandleValue reason); - -/** - * C++ equivalent of `reader.releaseLock()` - * (both and - * ). - * - * `reader` must be a stream reader created using `JS::ReadableStreamGetReader` - * or an unwrappable wrapper for one. - */ -extern JS_PUBLIC_API bool ReadableStreamReaderReleaseLock(JSContext* cx, - HandleObject reader); - -/** - * C++ equivalent of the `reader.read()` method on default readers - * (). - * - * The result is a new Promise object, or null on OOM. - * - * `reader` must be the result of calling `JS::ReadableStreamGetReader` with - * `ReadableStreamReaderMode::Default` mode, or an unwrappable wrapper for such - * a reader. - */ -extern JS_PUBLIC_API JSObject* ReadableStreamDefaultReaderRead( - JSContext* cx, HandleObject reader); - -} // namespace JS - -#endif // js_Stream_h diff --git a/js/public/TypeDecls.h b/js/public/TypeDecls.h index 779cee75fc53..1686b19173df 100644 --- a/js/public/TypeDecls.h +++ b/js/public/TypeDecls.h @@ -141,13 +141,6 @@ using jsid = JS::PropertyKey; # define IF_RECORD_TUPLE(x, ...) __VA_ARGS__ #endif -// Follows the same pattern as IF_RECORD_TUPLE -#ifdef MOZ_JS_STREAMS -# define IF_JS_STREAMS(x, ...) x -#else -# define IF_JS_STREAMS(x, ...) __VA_ARGS__ -#endif - // Follows the same pattern as IF_RECORD_TUPLE #ifdef ENABLE_DECORATORS # define IF_DECORATORS(x, ...) x diff --git a/js/src/builtin/Stream.cpp b/js/src/builtin/Stream.cpp deleted file mode 100644 index c108b2cd0d28..000000000000 --- a/js/src/builtin/Stream.cpp +++ /dev/null @@ -1,775 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -#include "builtin/Stream.h" - -#include "js/Stream.h" - -#include // int32_t - -#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC -#include "builtin/streams/MiscellaneousOperations.h" // js::CreateAlgorithmFromUnderlyingMethod, js::InvokeOrNoop, js::IsMaybeWrapped, js::PromiseCall, js::PromiseRejectedWithPendingError -#include "builtin/streams/PullIntoDescriptor.h" // js::PullIntoDescriptor -#include "builtin/streams/QueueWithSizes.h" // js::{EnqueueValueWithSize,ResetQueue} -#include "builtin/streams/ReadableStream.h" // js::ReadableStream, js::SetUpExternalReadableByteStreamController -#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableStreamDefaultControllerPullSteps, js::ReadableStreamControllerStart{,Failed}Handler -#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamControllerClearAlgorithms -#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{AddReadOrReadIntoRequest,CloseInternal,CreateReadResult,ErrorInternal,FulfillReadOrReadIntoRequest,GetNumReadRequests,HasDefaultReader} -#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::CreateReadableStreamDefaultReader, js::ReadableStreamReaderGeneric{Cancel,Initialize,Release}, js::ReadableStreamDefaultReaderRead -#include "js/ArrayBuffer.h" // JS::NewArrayBuffer -#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_NewUint8Array{,WithBuffer} -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/PropertySpec.h" -#include "vm/Interpreter.h" -#include "vm/JSContext.h" -#include "vm/PlainObject.h" // js::PlainObject -#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined -#include "vm/SelfHosting.h" - -#include "builtin/HandlerFunction-inl.h" // js::NewHandler -#include "builtin/streams/ReadableStreamReader-inl.h" // js::Unwrap{ReaderFromStream{,NoThrow},StreamFromReader} -#include "vm/Compartment-inl.h" -#include "vm/List-inl.h" // js::ListObject, js::StoreNewListInFixedSlot -#include "vm/NativeObject-inl.h" - -using namespace js; - -#if 0 // disable user-defined byte streams - -class ByteStreamChunk : public NativeObject -{ - private: - enum Slots { - Slot_Buffer = 0, - Slot_ByteOffset, - Slot_ByteLength, - SlotCount - }; - - public: - static const JSClass class_; - - ArrayBufferObject* buffer() { - return &getFixedSlot(Slot_Buffer).toObject().as(); - } - uint32_t byteOffset() { return getFixedSlot(Slot_ByteOffset).toInt32(); } - void SetByteOffset(uint32_t offset) { - setFixedSlot(Slot_ByteOffset, Int32Value(offset)); - } - uint32_t byteLength() { return getFixedSlot(Slot_ByteLength).toInt32(); } - void SetByteLength(uint32_t length) { - setFixedSlot(Slot_ByteLength, Int32Value(length)); - } - - static ByteStreamChunk* create(JSContext* cx, HandleObject buffer, uint32_t byteOffset, - uint32_t byteLength) - { - Rooted chunk(cx, NewBuiltinClassInstance(cx)); - if (!chunk) { - return nullptr; - } - - chunk->setFixedSlot(Slot_Buffer, ObjectValue(*buffer)); - chunk->setFixedSlot(Slot_ByteOffset, Int32Value(byteOffset)); - chunk->setFixedSlot(Slot_ByteLength, Int32Value(byteLength)); - return chunk; - } -}; - -const JSClass ByteStreamChunk::class_ = { - "ByteStreamChunk", - JSCLASS_HAS_RESERVED_SLOTS(SlotCount) -}; - -#endif // user-defined byte streams - -/*** 3.3. ReadableStreamAsyncIteratorPrototype ******************************/ - -// Not implemented. - -/*** 3.7. Class ReadableStreamBYOBReader ************************************/ - -// Not implemented. - -/*** 3.11. Class ReadableByteStreamController *******************************/ - -#if 0 // disable user-defined byte streams - -/** - * Streams spec, 3.10.3 - * new ReadableByteStreamController ( stream, underlyingSource, - * highWaterMark ) - * Steps 3 - 16. - * - * Note: All arguments must be same-compartment with cx. ReadableStream - * controllers are always created in the same compartment as the stream. - */ -[[nodiscard]] static ReadableByteStreamController* -CreateReadableByteStreamController(JSContext* cx, - Handle stream, - HandleValue underlyingByteSource, - HandleValue highWaterMarkVal) -{ - cx->check(stream, underlyingByteSource, highWaterMarkVal); - - Rooted controller(cx, - NewBuiltinClassInstance(cx)); - if (!controller) { - return nullptr; - } - - // Step 3: Set this.[[controlledReadableStream]] to stream. - controller->setStream(stream); - - // Step 4: Set this.[[underlyingByteSource]] to underlyingByteSource. - controller->setUnderlyingSource(underlyingByteSource); - - // Step 5: Set this.[[pullAgain]], and this.[[pulling]] to false. - controller->setFlags(0); - - // Step 6: Perform ! ReadableByteStreamControllerClearPendingPullIntos(this). - if (!ReadableByteStreamControllerClearPendingPullIntos(cx, controller)) { - return nullptr; - } - - // Step 7: Perform ! ResetQueue(this). - if (!ResetQueue(cx, controller)) { - return nullptr; - } - - // Step 8: Set this.[[started]] and this.[[closeRequested]] to false. - // These should be false by default, unchanged since step 5. - MOZ_ASSERT(controller->flags() == 0); - - // Step 9: Set this.[[strategyHWM]] to - // ? ValidateAndNormalizeHighWaterMark(highWaterMark). - double highWaterMark; - if (!ValidateAndNormalizeHighWaterMark(cx, highWaterMarkVal, &highWaterMark)) { - return nullptr; - } - controller->setStrategyHWM(highWaterMark); - - // Step 10: Let autoAllocateChunkSize be - // ? GetV(underlyingByteSource, "autoAllocateChunkSize"). - RootedValue autoAllocateChunkSize(cx); - if (!GetProperty(cx, underlyingByteSource, cx->names().autoAllocateChunkSize, - &autoAllocateChunkSize)) - { - return nullptr; - } - - // Step 11: If autoAllocateChunkSize is not undefined, - if (!autoAllocateChunkSize.isUndefined()) { - // Step a: If ! IsInteger(autoAllocateChunkSize) is false, or if - // autoAllocateChunkSize ≤ 0, throw a RangeError exception. - if (!IsInteger(autoAllocateChunkSize) || autoAllocateChunkSize.toNumber() <= 0) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE); - return nullptr; - } - } - - // Step 12: Set this.[[autoAllocateChunkSize]] to autoAllocateChunkSize. - controller->setAutoAllocateChunkSize(autoAllocateChunkSize); - - // Step 13: Set this.[[pendingPullIntos]] to a new empty List. - if (!StoreNewListInFixedSlot(cx, controller, - ReadableByteStreamController::Slot_PendingPullIntos)) { - return nullptr; - } - - // Step 14: Let controller be this (implicit). - - // Step 15: Let startResult be - // ? InvokeOrNoop(underlyingSource, "start", « this »). - RootedValue startResult(cx); - RootedValue controllerVal(cx, ObjectValue(*controller)); - if (!InvokeOrNoop(cx, underlyingByteSource, cx->names().start, controllerVal, &startResult)) { - return nullptr; - } - - // Step 16: Let startPromise be a promise resolved with startResult: - RootedObject startPromise(cx, PromiseObject::unforgeableResolve(cx, startResult)); - if (!startPromise) { - return nullptr; - } - - RootedObject onStartFulfilled(cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller)); - if (!onStartFulfilled) { - return nullptr; - } - - RootedObject onStartRejected(cx, NewHandler(cx, ControllerStartFailedHandler, controller)); - if (!onStartRejected) { - return nullptr; - } - - if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled, onStartRejected)) { - return nullptr; - } - - return controller; -} - -#endif // user-defined byte streams - -/** - * Streams spec, 3.11.3. - * new ReadableByteStreamController ( stream, underlyingByteSource, - * highWaterMark ) - */ -bool ReadableByteStreamController::constructor(JSContext* cx, unsigned argc, - Value* vp) { - // Step 1: Throw a TypeError exception. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_BOGUS_CONSTRUCTOR, - "ReadableByteStreamController"); - return false; -} - -// Disconnect the source from a controller without calling finalize() on it, -// unless this class is reset(). This ensures that finalize() will not be called -// on the source if setting up the controller fails. -class MOZ_RAII AutoClearUnderlyingSource { - Rooted controller_; - - public: - AutoClearUnderlyingSource(JSContext* cx, ReadableStreamController* controller) - : controller_(cx, controller) {} - - ~AutoClearUnderlyingSource() { - if (controller_) { - ReadableStreamController::clearUnderlyingSource( - controller_, /* finalizeSource */ false); - } - } - - void reset() { controller_ = nullptr; } -}; - -/** - * Version of SetUpReadableByteStreamController that's specialized for handling - * external, embedding-provided, underlying sources. - */ -[[nodiscard]] bool js::SetUpExternalReadableByteStreamController( - JSContext* cx, Handle stream, - JS::ReadableStreamUnderlyingSource* source) { - // Done elsewhere in the standard: Create the controller object. - Rooted controller( - cx, NewBuiltinClassInstance(cx)); - if (!controller) { - return false; - } - - AutoClearUnderlyingSource autoClear(cx, controller); - - // Step 1: Assert: stream.[[readableStreamController]] is undefined. - MOZ_ASSERT(!stream->hasController()); - - // Step 2: If autoAllocateChunkSize is not undefined, [...] - // (It's treated as undefined.) - - // Step 3: Set controller.[[controlledReadableByteStream]] to stream. - controller->setStream(stream); - - // Step 4: Set controller.[[pullAgain]] and controller.[[pulling]] to false. - controller->setFlags(0); - MOZ_ASSERT(!controller->pullAgain()); - MOZ_ASSERT(!controller->pulling()); - - // Step 5: Perform - // ! ReadableByteStreamControllerClearPendingPullIntos(controller). - // Omitted. This step is apparently redundant; see - // . - - // Step 6: Perform ! ResetQueue(this). - controller->setQueueTotalSize(0); - - // Step 7: Set controller.[[closeRequested]] and controller.[[started]] to - // false (implicit). - MOZ_ASSERT(!controller->closeRequested()); - MOZ_ASSERT(!controller->started()); - - // Step 8: Set controller.[[strategyHWM]] to - // ? ValidateAndNormalizeHighWaterMark(highWaterMark). - controller->setStrategyHWM(0); - - // Step 9: Set controller.[[pullAlgorithm]] to pullAlgorithm. - // Step 10: Set controller.[[cancelAlgorithm]] to cancelAlgorithm. - // (These algorithms are given by source's virtual methods.) - controller->setExternalSource(source); - - // Step 11: Set controller.[[autoAllocateChunkSize]] to - // autoAllocateChunkSize (implicit). - MOZ_ASSERT(controller->autoAllocateChunkSize().isUndefined()); - - // Step 12: Set this.[[pendingPullIntos]] to a new empty List. - if (!StoreNewListInFixedSlot( - cx, controller, - ReadableByteStreamController::Slot_PendingPullIntos)) { - return false; - } - - // Step 13: Set stream.[[readableStreamController]] to controller. - stream->setController(controller); - - // Step 14: Let startResult be the result of performing startAlgorithm. - // (For external sources, this algorithm does nothing and returns undefined.) - // Step 15: Let startPromise be a promise resolved with startResult. - Rooted startPromise(cx, PromiseResolvedWithUndefined(cx)); - if (!startPromise) { - return false; - } - - // Step 16: Upon fulfillment of startPromise, [...] - // Step 17: Upon rejection of startPromise with reason r, [...] - RootedObject onStartFulfilled( - cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller)); - if (!onStartFulfilled) { - return false; - } - RootedObject onStartRejected( - cx, - NewHandler(cx, ReadableStreamControllerStartFailedHandler, controller)); - if (!onStartRejected) { - return false; - } - if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled, - onStartRejected)) { - return false; - } - - autoClear.reset(); - return true; -} - -static const JSPropertySpec ReadableByteStreamController_properties[] = { - JS_PS_END}; - -static const JSFunctionSpec ReadableByteStreamController_methods[] = { - JS_FS_END}; - -static void ReadableByteStreamControllerFinalize(JS::GCContext* gcx, - JSObject* obj) { - ReadableByteStreamController& controller = - obj->as(); - - if (controller.getFixedSlot(ReadableStreamController::Slot_Flags) - .isUndefined()) { - return; - } - - if (!controller.hasExternalSource()) { - return; - } - - controller.externalSource()->finalize(); -} - -static const JSClassOps ReadableByteStreamControllerClassOps = { - nullptr, // addProperty - nullptr, // delProperty - nullptr, // enumerate - nullptr, // newEnumerate - nullptr, // resolve - nullptr, // mayResolve - ReadableByteStreamControllerFinalize, // finalize - nullptr, // call - nullptr, // construct - nullptr, // trace -}; - -JS_STREAMS_CLASS_SPEC(ReadableByteStreamController, 0, SlotCount, - ClassSpec::DontDefineConstructor, - JSCLASS_BACKGROUND_FINALIZE, - &ReadableByteStreamControllerClassOps); - -// Streams spec, 3.11.5.1. [[CancelSteps]] () -// Unified with 3.9.5.1 above. - -[[nodiscard]] static bool ReadableByteStreamControllerHandleQueueDrain( - JSContext* cx, Handle unwrappedController); - -/** - * Streams spec, 3.11.5.2. [[PullSteps]] ( forAuthorCode ) - */ -[[nodiscard]] static PromiseObject* ReadableByteStreamControllerPullSteps( - JSContext* cx, Handle unwrappedController) { - // Step 1: Let stream be this.[[controlledReadableByteStream]]. - Rooted unwrappedStream(cx, unwrappedController->stream()); - - // Step 2: Assert: ! ReadableStreamHasDefaultReader(stream) is true. -#ifdef DEBUG - bool result; - if (!ReadableStreamHasDefaultReader(cx, unwrappedStream, &result)) { - return nullptr; - } - MOZ_ASSERT(result); -#endif - - RootedValue val(cx); - // Step 3: If this.[[queueTotalSize]] > 0, - double queueTotalSize = unwrappedController->queueTotalSize(); - if (queueTotalSize > 0) { - // Step 3.a: Assert: ! ReadableStreamGetNumReadRequests(_stream_) is 0. - MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0); - - RootedObject view(cx); - - MOZ_RELEASE_ASSERT(unwrappedStream->mode() == - JS::ReadableStreamMode::ExternalSource); -#if 0 // disable user-defined byte streams - if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource) -#endif // user-defined byte streams - { - JS::ReadableStreamUnderlyingSource* source = - unwrappedController->externalSource(); - - view = JS_NewUint8Array(cx, queueTotalSize); - if (!view) { - return nullptr; - } - - size_t bytesWritten; - { - AutoRealm ar(cx, unwrappedStream); - source->writeIntoReadRequestBuffer(cx, unwrappedStream, view, - queueTotalSize, &bytesWritten); - } - - queueTotalSize = queueTotalSize - bytesWritten; - } - -#if 0 // disable user-defined byte streams - else { - // Step 3.b: Let entry be the first element of this.[[queue]]. - // Step 3.c: Remove entry from this.[[queue]], shifting all other - // elements downward (so that the second becomes the - // first, and so on). - Rooted unwrappedQueue(cx, unwrappedController->queue()); - Rooted unwrappedEntry(cx, - UnwrapAndDowncastObject( - cx, &unwrappedQueue->popFirstAs(cx))); - if (!unwrappedEntry) { - return nullptr; - } - - queueTotalSize = queueTotalSize - unwrappedEntry->byteLength(); - - // Step 3.f: Let view be ! Construct(%Uint8Array%, - // « entry.[[buffer]], - // entry.[[byteOffset]], - // entry.[[byteLength]] »). - // (reordered) - RootedObject buffer(cx, unwrappedEntry->buffer()); - if (!cx->compartment()->wrap(cx, &buffer)) { - return nullptr; - } - - uint32_t byteOffset = unwrappedEntry->byteOffset(); - view = JS_NewUint8ArrayWithBuffer(cx, buffer, byteOffset, unwrappedEntry->byteLength()); - if (!view) { - return nullptr; - } - } -#endif // user-defined byte streams - - // Step 3.d: Set this.[[queueTotalSize]] to - // this.[[queueTotalSize]] − entry.[[byteLength]]. - // (reordered) - unwrappedController->setQueueTotalSize(queueTotalSize); - - // Step 3.e: Perform ! ReadableByteStreamControllerHandleQueueDrain(this). - // (reordered) - if (!ReadableByteStreamControllerHandleQueueDrain(cx, - unwrappedController)) { - return nullptr; - } - - // Step 3.g: Return a promise resolved with - // ! ReadableStreamCreateReadResult(view, false, forAuthorCode). - val.setObject(*view); - ReadableStreamReader* unwrappedReader = - UnwrapReaderFromStream(cx, unwrappedStream); - if (!unwrappedReader) { - return nullptr; - } - Rooted readResult( - cx, ReadableStreamCreateReadResult(cx, val, false, - unwrappedReader->forAuthorCode())); - if (!readResult) { - return nullptr; - } - val.setObject(*readResult); - - return PromiseObject::unforgeableResolveWithNonPromise(cx, val); - } - - // Step 4: Let autoAllocateChunkSize be this.[[autoAllocateChunkSize]]. - val = unwrappedController->autoAllocateChunkSize(); - - // Step 5: If autoAllocateChunkSize is not undefined, - if (!val.isUndefined()) { - double autoAllocateChunkSize = val.toNumber(); - - // Step 5.a: Let buffer be - // Construct(%ArrayBuffer%, « autoAllocateChunkSize »). - JSObject* bufferObj = JS::NewArrayBuffer(cx, autoAllocateChunkSize); - - // Step 5.b: If buffer is an abrupt completion, - // return a promise rejected with buffer.[[Value]]. - if (!bufferObj) { - return PromiseRejectedWithPendingError(cx); - } - - RootedArrayBufferObject buffer(cx, &bufferObj->as()); - - // Step 5.c: Let pullIntoDescriptor be - // Record {[[buffer]]: buffer.[[Value]], - // [[byteOffset]]: 0, - // [[byteLength]]: autoAllocateChunkSize, - // [[bytesFilled]]: 0, - // [[elementSize]]: 1, - // [[ctor]]: %Uint8Array%, - // [[readerType]]: `"default"`}. - RootedObject pullIntoDescriptor( - cx, PullIntoDescriptor::create(cx, buffer, 0, autoAllocateChunkSize, 0, - 1, nullptr, ReaderType::Default)); - if (!pullIntoDescriptor) { - return PromiseRejectedWithPendingError(cx); - } - - // Step 5.d: Append pullIntoDescriptor as the last element of - // this.[[pendingPullIntos]]. - if (!AppendToListInFixedSlot( - cx, unwrappedController, - ReadableByteStreamController::Slot_PendingPullIntos, - pullIntoDescriptor)) { - return nullptr; - } - } - - // Step 6: Let promise be ! ReadableStreamAddReadRequest(stream, - // forAuthorCode). - Rooted promise( - cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream)); - if (!promise) { - return nullptr; - } - - // Step 7: Perform ! ReadableByteStreamControllerCallPullIfNeeded(this). - if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) { - return nullptr; - } - - // Step 8: Return promise. - return promise; -} - -/** - * Unified implementation of ReadableStream controllers' [[PullSteps]] internal - * methods. - * Streams spec, 3.9.5.2. [[PullSteps]] ( forAuthorCode ) - * and - * Streams spec, 3.11.5.2. [[PullSteps]] ( forAuthorCode ) - */ -[[nodiscard]] PromiseObject* js::ReadableStreamControllerPullSteps( - JSContext* cx, Handle unwrappedController) { - if (unwrappedController->is()) { - Rooted unwrappedDefaultController( - cx, &unwrappedController->as()); - return ReadableStreamDefaultControllerPullSteps(cx, - unwrappedDefaultController); - } - - Rooted unwrappedByteController( - cx, &unwrappedController->as()); - return ReadableByteStreamControllerPullSteps(cx, unwrappedByteController); -} - -/*** 3.13. Readable stream BYOB controller abstract operations **************/ - -// Streams spec, 3.13.1. IsReadableStreamBYOBRequest ( x ) -// Implemented via is() - -// Streams spec, 3.13.2. IsReadableByteStreamController ( x ) -// Implemented via is() - -// Streams spec, 3.13.3. -// ReadableByteStreamControllerCallPullIfNeeded ( controller ) -// Unified with 3.9.2 above. - -[[nodiscard]] static bool ReadableByteStreamControllerInvalidateBYOBRequest( - JSContext* cx, Handle unwrappedController); - -/** - * Streams spec, 3.13.5. - * ReadableByteStreamControllerClearPendingPullIntos ( controller ) - */ -[[nodiscard]] bool js::ReadableByteStreamControllerClearPendingPullIntos( - JSContext* cx, Handle unwrappedController) { - // Step 1: Perform - // ! ReadableByteStreamControllerInvalidateBYOBRequest(controller). - if (!ReadableByteStreamControllerInvalidateBYOBRequest(cx, - unwrappedController)) { - return false; - } - - // Step 2: Set controller.[[pendingPullIntos]] to a new empty List. - return StoreNewListInFixedSlot( - cx, unwrappedController, - ReadableByteStreamController::Slot_PendingPullIntos); -} - -/** - * Streams spec, 3.13.6. ReadableByteStreamControllerClose ( controller ) - */ -[[nodiscard]] bool js::ReadableByteStreamControllerClose( - JSContext* cx, Handle unwrappedController) { - // Step 1: Let stream be controller.[[controlledReadableByteStream]]. - Rooted unwrappedStream(cx, unwrappedController->stream()); - - // Step 2: Assert: controller.[[closeRequested]] is false. - MOZ_ASSERT(!unwrappedController->closeRequested()); - - // Step 3: Assert: stream.[[state]] is "readable". - MOZ_ASSERT(unwrappedStream->readable()); - - // Step 4: If controller.[[queueTotalSize]] > 0, - if (unwrappedController->queueTotalSize() > 0) { - // Step a: Set controller.[[closeRequested]] to true. - unwrappedController->setCloseRequested(); - - // Step b: Return. - return true; - } - - // Step 5: If controller.[[pendingPullIntos]] is not empty, - Rooted unwrappedPendingPullIntos( - cx, unwrappedController->pendingPullIntos()); - if (unwrappedPendingPullIntos->length() != 0) { - // Step a: Let firstPendingPullInto be the first element of - // controller.[[pendingPullIntos]]. - Rooted unwrappedFirstPendingPullInto( - cx, UnwrapAndDowncastObject( - cx, &unwrappedPendingPullIntos->get(0).toObject())); - if (!unwrappedFirstPendingPullInto) { - return false; - } - - // Step b: If firstPendingPullInto.[[bytesFilled]] > 0, - if (unwrappedFirstPendingPullInto->bytesFilled() > 0) { - // Step i: Let e be a new TypeError exception. - JS_ReportErrorNumberASCII( - cx, GetErrorMessage, nullptr, - JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL); - RootedValue e(cx); - Rooted stack(cx); - if (!cx->isExceptionPending() || - !GetAndClearExceptionAndStack(cx, &e, &stack)) { - // Uncatchable error. Die immediately without erroring the - // stream. - return false; - } - - // Step ii: Perform ! ReadableByteStreamControllerError(controller, e). - if (!ReadableStreamControllerError(cx, unwrappedController, e)) { - return false; - } - - // Step iii: Throw e. - cx->setPendingException(e, stack); - return false; - } - } - - // Step 6: Perform ! ReadableByteStreamControllerClearAlgorithms(controller). - ReadableStreamControllerClearAlgorithms(unwrappedController); - - // Step 7: Perform ! ReadableStreamClose(stream). - return ReadableStreamCloseInternal(cx, unwrappedStream); -} - -// Streams spec, 3.13.11. ReadableByteStreamControllerError ( controller, e ) -// Unified with 3.10.7 above. - -// Streams spec 3.13.14. -// ReadableByteStreamControllerGetDesiredSize ( controller ) -// Unified with 3.10.8 above. - -/** - * Streams spec, 3.13.15. - * ReadableByteStreamControllerHandleQueueDrain ( controller ) - */ -[[nodiscard]] static bool ReadableByteStreamControllerHandleQueueDrain( - JSContext* cx, Handle unwrappedController) { - MOZ_ASSERT(unwrappedController->is()); - - // Step 1: Assert: controller.[[controlledReadableStream]].[[state]] - // is "readable". - Rooted unwrappedStream(cx, unwrappedController->stream()); - MOZ_ASSERT(unwrappedStream->readable()); - - // Step 2: If controller.[[queueTotalSize]] is 0 and - // controller.[[closeRequested]] is true, - if (unwrappedController->queueTotalSize() == 0 && - unwrappedController->closeRequested()) { - // Step a: Perform - // ! ReadableByteStreamControllerClearAlgorithms(controller). - ReadableStreamControllerClearAlgorithms(unwrappedController); - - // Step b: Perform - // ! ReadableStreamClose(controller.[[controlledReadableStream]]). - return ReadableStreamCloseInternal(cx, unwrappedStream); - } - - // Step 3: Otherwise, - // Step a: Perform ! ReadableByteStreamControllerCallPullIfNeeded(controller). - return ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController); -} - -enum BYOBRequestSlots { - BYOBRequestSlot_Controller, - BYOBRequestSlot_View, - BYOBRequestSlotCount -}; - -/** - * Streams spec 3.13.16. - * ReadableByteStreamControllerInvalidateBYOBRequest ( controller ) - */ -[[nodiscard]] static bool ReadableByteStreamControllerInvalidateBYOBRequest( - JSContext* cx, Handle unwrappedController) { - // Step 1: If controller.[[byobRequest]] is undefined, return. - RootedValue unwrappedBYOBRequestVal(cx, unwrappedController->byobRequest()); - if (unwrappedBYOBRequestVal.isUndefined()) { - return true; - } - - Rooted unwrappedBYOBRequest( - cx, UnwrapAndDowncastValue(cx, unwrappedBYOBRequestVal)); - if (!unwrappedBYOBRequest) { - return false; - } - - // Step 2: Set controller.[[byobRequest]] - // .[[associatedReadableByteStreamController]] - // to undefined. - unwrappedBYOBRequest->setFixedSlot(BYOBRequestSlot_Controller, - UndefinedValue()); - - // Step 3: Set controller.[[byobRequest]].[[view]] to undefined. - unwrappedBYOBRequest->setFixedSlot(BYOBRequestSlot_View, UndefinedValue()); - - // Step 4: Set controller.[[byobRequest]] to undefined. - unwrappedController->clearBYOBRequest(); - - return true; -} - -// Streams spec, 3.13.25. -// ReadableByteStreamControllerShouldCallPull ( controller ) -// Unified with 3.10.3 above. diff --git a/js/src/builtin/Stream.h b/js/src/builtin/Stream.h deleted file mode 100644 index 7ecb95862376..000000000000 --- a/js/src/builtin/Stream.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -#ifndef builtin_Stream_h -#define builtin_Stream_h - -#include "jstypes.h" // JS_PUBLIC_API -#include "js/RootingAPI.h" // JS::Handle - -struct JS_PUBLIC_API JSContext; - -namespace js { - -class PromiseObject; -class ReadableByteStreamController; -class ReadableStreamController; - -[[nodiscard]] extern bool ReadableByteStreamControllerClearPendingPullIntos( - JSContext* cx, - JS::Handle unwrappedController); - -[[nodiscard]] extern bool ReadableByteStreamControllerClose( - JSContext* cx, - JS::Handle unwrappedController); - -[[nodiscard]] extern PromiseObject* ReadableStreamControllerPullSteps( - JSContext* cx, JS::Handle unwrappedController); - -} // namespace js - -#endif /* builtin_Stream_h */ diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 9154ff2e6088..0ea5988d45f2 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -3879,12 +3879,6 @@ static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) { return result; } -static bool StreamsAreEnabled(JSContext* cx, unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - args.rval().setBoolean(cx->realm()->creationOptions().getStreamsEnabled()); - return true; -} - static unsigned finalizeCount = 0; static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) { @@ -8326,10 +8320,6 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0, "rejectPromise(promise, reason)", " Reject a Promise by calling the JSAPI function JS::RejectPromise."), -JS_FN_HELP("streamsAreEnabled", StreamsAreEnabled, 0, 0, -"streamsAreEnabled()", -" Returns a boolean indicating whether WHATWG Streams are enabled for the current realm."), - JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0, "makeFinalizeObserver()", " Get a special object whose finalization increases the counter returned\n" diff --git a/js/src/builtin/streams/ClassSpecMacro.h b/js/src/builtin/streams/ClassSpecMacro.h deleted file mode 100644 index 17d1fda9307c..000000000000 --- a/js/src/builtin/streams/ClassSpecMacro.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* A JS_STREAMS_CLASS_SPEC macro for defining streams classes. */ - -#ifndef builtin_streams_ClassSpecMacro_h -#define builtin_streams_ClassSpecMacro_h - -#include "gc/AllocKind.h" // js::gc::AllocKind -#include "js/Class.h" // js::ClassSpec, JSClass, JSCLASS_HAS_{CACHED_PROTO,RESERVED_SLOTS}, JS_NULL_CLASS_OPS -#include "js/ProtoKey.h" // JSProto_* -#include "vm/GlobalObject.h" // js::GenericCreate{Constructor,Prototype} - -#define JS_STREAMS_CLASS_SPEC(cls, nCtorArgs, nSlots, specFlags, classFlags, \ - classOps) \ - const js::ClassSpec cls::classSpec_ = { \ - js::GenericCreateConstructor, \ - js::GenericCreatePrototype, \ - nullptr, \ - nullptr, \ - cls##_methods, \ - cls##_properties, \ - nullptr, \ - specFlags}; \ - \ - const JSClass cls::class_ = {#cls, \ - JSCLASS_HAS_RESERVED_SLOTS(nSlots) | \ - JSCLASS_HAS_CACHED_PROTO(JSProto_##cls) | \ - classFlags, \ - classOps, &cls::classSpec_}; \ - \ - const JSClass cls::protoClass_ = {#cls ".prototype", \ - JSCLASS_HAS_CACHED_PROTO(JSProto_##cls), \ - JS_NULL_CLASS_OPS, &cls::classSpec_}; - -#endif // builtin_streams_ClassSpecMacro_h diff --git a/js/src/builtin/streams/MiscellaneousOperations-inl.h b/js/src/builtin/streams/MiscellaneousOperations-inl.h deleted file mode 100644 index 3334471fb7d3..000000000000 --- a/js/src/builtin/streams/MiscellaneousOperations-inl.h +++ /dev/null @@ -1,115 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Miscellaneous operations. */ - -#ifndef builtin_streams_MiscellaneousOperations_inl_h -#define builtin_streams_MiscellaneousOperations_inl_h - -#include "builtin/streams/MiscellaneousOperations.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT - -#include "js/CallAndConstruct.h" // JS::IsCallable -#include "js/Promise.h" // JS::{Resolve,Reject}Promise -#include "js/RootingAPI.h" // JS::Rooted, JS::{,Mutable}Handle -#include "js/Value.h" // JS::UndefinedHandleValue, JS::Value -#include "vm/Compartment.h" // JS::Compartment -#include "vm/Interpreter.h" // js::Call -#include "vm/JSContext.h" // JSContext -#include "vm/JSObject.h" // JSObject -#include "vm/PromiseObject.h" // js::PromiseObject - -#include "vm/Compartment-inl.h" // JS::Compartment::wrap -#include "vm/JSContext-inl.h" // JSContext::check -#include "vm/JSObject-inl.h" // js::IsCallable - -namespace js { - -/** - * Streams spec, 6.3.5. PromiseCall ( F, V, args ) - * There must be 0-2 |args| arguments, all convertible to JS::Handle. - */ -template -[[nodiscard]] inline JSObject* PromiseCall(JSContext* cx, - JS::Handle F, - JS::Handle V, - Args&&... args) { - cx->check(F); - cx->check(V); - cx->check(args...); - - // Step 1: Assert: ! IsCallable(F) is true. - MOZ_ASSERT(IsCallable(F)); - - // Step 2: Assert: V is not undefined. - MOZ_ASSERT(!V.isUndefined()); - - // Step 3: Assert: args is a List (implicit). - // Step 4: Let returnValue be Call(F, V, args). - JS::Rooted rval(cx); - if (!Call(cx, F, V, args..., &rval)) { - // Step 5: If returnValue is an abrupt completion, return a promise rejected - // with returnValue.[[Value]]. - return PromiseRejectedWithPendingError(cx); - } - - // Step 6: Otherwise, return a promise resolved with returnValue.[[Value]]. - return PromiseObject::unforgeableResolve(cx, rval); -} - -/** - * Resolve the unwrapped promise |unwrappedPromise| with |value|. - */ -[[nodiscard]] inline bool ResolveUnwrappedPromiseWithValue( - JSContext* cx, JSObject* unwrappedPromise, JS::Handle value) { - cx->check(value); - - JS::Rooted promise(cx, unwrappedPromise); - if (!cx->compartment()->wrap(cx, &promise)) { - return false; - } - - return JS::ResolvePromise(cx, promise, value); -} - -/** - * Resolve the unwrapped promise |unwrappedPromise| with |undefined|. - */ -[[nodiscard]] inline bool ResolveUnwrappedPromiseWithUndefined( - JSContext* cx, JSObject* unwrappedPromise) { - return ResolveUnwrappedPromiseWithValue(cx, unwrappedPromise, - JS::UndefinedHandleValue); -} - -/** - * Reject the unwrapped promise |unwrappedPromise| with |error|, overwriting - * |*unwrappedPromise| with its wrapped form. - */ -[[nodiscard]] inline bool RejectUnwrappedPromiseWithError( - JSContext* cx, JS::MutableHandle unwrappedPromise, - JS::Handle error) { - cx->check(error); - - if (!cx->compartment()->wrap(cx, unwrappedPromise)) { - return false; - } - - return JS::RejectPromise(cx, unwrappedPromise, error); -} - -/** - * Reject the unwrapped promise |unwrappedPromise| with |error|. - */ -[[nodiscard]] inline bool RejectUnwrappedPromiseWithError( - JSContext* cx, JSObject* unwrappedPromise, JS::Handle error) { - JS::Rooted promise(cx, unwrappedPromise); - return RejectUnwrappedPromiseWithError(cx, &promise, error); -} - -} // namespace js - -#endif // builtin_streams_MiscellaneousOperations_inl_h diff --git a/js/src/builtin/streams/MiscellaneousOperations.cpp b/js/src/builtin/streams/MiscellaneousOperations.cpp deleted file mode 100644 index 268960624f33..000000000000 --- a/js/src/builtin/streams/MiscellaneousOperations.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Miscellaneous operations. */ - -#include "builtin/streams/MiscellaneousOperations.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT -#include "mozilla/FloatingPoint.h" // mozilla::IsNaN - -#include "js/CallAndConstruct.h" // JS::IsCallable -#include "js/Conversions.h" // JS::ToNumber -#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted -#include "vm/Interpreter.h" // js::{Call,GetAndClearException} -#include "vm/JSContext.h" // JSContext -#include "vm/ObjectOperations.h" // js::GetProperty -#include "vm/PromiseObject.h" // js::PromiseObject -#include "vm/StringType.h" // js::PropertyName - -#include "vm/JSContext-inl.h" // JSContext::check -#include "vm/JSObject-inl.h" // js::IsCallable - -using JS::Handle; -using JS::MutableHandle; -using JS::ToNumber; -using JS::Value; - -[[nodiscard]] js::PromiseObject* js::PromiseRejectedWithPendingError( - JSContext* cx) { - Rooted exn(cx); - if (!cx->isExceptionPending() || !GetAndClearException(cx, &exn)) { - // Uncatchable error. This happens when a slow script is killed or a - // worker is terminated. Propagate the uncatchable error. This will - // typically kill off the calling asynchronous process: the caller - // can't hook its continuation to the new rejected promise. - return nullptr; - } - return PromiseObject::unforgeableReject(cx, exn); -} - -/*** 6.3. Miscellaneous operations ******************************************/ - -/** - * Streams spec, 6.3.1. - * CreateAlgorithmFromUnderlyingMethod ( underlyingObject, methodName, - * algoArgCount, extraArgs ) - * - * This function only partly implements the standard algorithm. We do not - * actually create a new JSFunction completely encapsulating the new algorithm. - * Instead, this just gets the specified method and checks for errors. It's the - * caller's responsibility to make sure that later, when the algorithm is - * "performed", the appropriate steps are carried out. - */ -[[nodiscard]] bool js::CreateAlgorithmFromUnderlyingMethod( - JSContext* cx, Handle underlyingObject, - const char* methodNameForErrorMessage, Handle methodName, - MutableHandle method) { - cx->check(underlyingObject); - cx->check(methodName); - cx->check(method); - - // Step 1: Assert: underlyingObject is not undefined. - MOZ_ASSERT(!underlyingObject.isUndefined()); - - // Step 2: Assert: ! IsPropertyKey(methodName) is true (implicit). - // Step 3: Assert: algoArgCount is 0 or 1 (omitted). - // Step 4: Assert: extraArgs is a List (omitted). - - // Step 5: Let method be ? GetV(underlyingObject, methodName). - if (!GetProperty(cx, underlyingObject, methodName, method)) { - return false; - } - - // Step 6: If method is not undefined, - if (!method.isUndefined()) { - // Step a: If ! IsCallable(method) is false, throw a TypeError - // exception. - if (!IsCallable(method)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_NOT_FUNCTION, methodNameForErrorMessage); - return false; - } - - // Step b: If algoArgCount is 0, return an algorithm that performs the - // following steps: - // Step i: Return ! PromiseCall(method, underlyingObject, - // extraArgs). - // Step c: Otherwise, return an algorithm that performs the following - // steps, taking an arg argument: - // Step i: Let fullArgs be a List consisting of arg followed by the - // elements of extraArgs in order. - // Step ii: Return ! PromiseCall(method, underlyingObject, - // fullArgs). - // (These steps are deferred to the code that performs the algorithm. - // See Perform{Write,Close}Algorithm, ReadableStreamControllerCancelSteps, - // and ReadableStreamControllerCallPullIfNeeded.) - return true; - } - - // Step 7: Return an algorithm which returns a promise resolved with - // undefined (implicit). - return true; -} - -/** - * Streams spec, 6.3.2. InvokeOrNoop ( O, P, args ) - * As it happens, all callers pass exactly one argument. - */ -[[nodiscard]] bool js::InvokeOrNoop(JSContext* cx, Handle O, - Handle P, Handle arg, - MutableHandle rval) { - cx->check(O, P, arg); - - // Step 1: Assert: O is not undefined. - MOZ_ASSERT(!O.isUndefined()); - - // Step 2: Assert: ! IsPropertyKey(P) is true (implicit). - // Step 3: Assert: args is a List (implicit). - // Step 4: Let method be ? GetV(O, P). - Rooted method(cx); - if (!GetProperty(cx, O, P, &method)) { - return false; - } - - // Step 5: If method is undefined, return. - if (method.isUndefined()) { - return true; - } - - // Step 6: Return ? Call(method, O, args). - return Call(cx, method, O, arg, rval); -} - -/** - * Streams spec, 6.3.7. ValidateAndNormalizeHighWaterMark ( highWaterMark ) - */ -[[nodiscard]] bool js::ValidateAndNormalizeHighWaterMark( - JSContext* cx, Handle highWaterMarkVal, double* highWaterMark) { - // Step 1: Set highWaterMark to ? ToNumber(highWaterMark). - if (!ToNumber(cx, highWaterMarkVal, highWaterMark)) { - return false; - } - - // Step 2: If highWaterMark is NaN or highWaterMark < 0, throw a RangeError - // exception. - if (mozilla::IsNaN(*highWaterMark) || *highWaterMark < 0) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_STREAM_INVALID_HIGHWATERMARK); - return false; - } - - // Step 3: Return highWaterMark. - return true; -} - -/** - * Streams spec, 6.3.8. MakeSizeAlgorithmFromSizeFunction ( size ) - * - * The standard makes a big deal of turning JavaScript functions (grubby, - * touched by users, covered with germs) into algorithms (pristine, - * respectable, purposeful). We don't bother. Here we only check for errors and - * leave `size` unchanged. Then, in ReadableStreamDefaultControllerEnqueue and - * WritableStreamDefaultControllerGetChunkSize where this value is used, we - * check for undefined and behave as if we had "made" an "algorithm" for it. - */ -[[nodiscard]] bool js::MakeSizeAlgorithmFromSizeFunction(JSContext* cx, - Handle size) { - cx->check(size); - - // Step 1: If size is undefined, return an algorithm that returns 1. - if (size.isUndefined()) { - // Deferred. Size algorithm users must check for undefined. - return true; - } - - // Step 2: If ! IsCallable(size) is false, throw a TypeError exception. - if (!IsCallable(size)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, - "ReadableStream argument options.size"); - return false; - } - - // Step 3: Return an algorithm that performs the following steps, taking a - // chunk argument: - // a. Return ? Call(size, undefined, « chunk »). - // Deferred. Size algorithm users must know how to call the size function. - return true; -} diff --git a/js/src/builtin/streams/MiscellaneousOperations.h b/js/src/builtin/streams/MiscellaneousOperations.h deleted file mode 100644 index f52815c99702..000000000000 --- a/js/src/builtin/streams/MiscellaneousOperations.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Miscellaneous operations. */ - -#ifndef builtin_streams_MiscellaneousOperations_h -#define builtin_streams_MiscellaneousOperations_h - -#include "jstypes.h" // JS_PUBLIC_API -#include "js/CallArgs.h" // JS::CallArgs -#include "js/RootingAPI.h" // JS::{,Mutable}Handle -#include "js/Value.h" // JS::Value -#include "vm/JSObject.h" // JSObject -#include "vm/PromiseObject.h" // js::PromiseObject - -struct JS_PUBLIC_API JSContext; - -namespace js { - -class PropertyName; - -[[nodiscard]] extern PromiseObject* PromiseRejectedWithPendingError( - JSContext* cx); - -[[nodiscard]] inline bool ReturnPromiseRejectedWithPendingError( - JSContext* cx, const JS::CallArgs& args) { - PromiseObject* promise = PromiseRejectedWithPendingError(cx); - if (!promise) { - return false; - } - - args.rval().setObject(*promise); - return true; -} - -/** - * Streams spec, 6.3.1. - * CreateAlgorithmFromUnderlyingMethod ( underlyingObject, methodName, - * algoArgCount, extraArgs ) - * - * This function only partly implements the standard algorithm. We do not - * actually create a new JSFunction completely encapsulating the new algorithm. - * Instead, this just gets the specified method and checks for errors. It's the - * caller's responsibility to make sure that later, when the algorithm is - * "performed", the appropriate steps are carried out. - */ -[[nodiscard]] extern bool CreateAlgorithmFromUnderlyingMethod( - JSContext* cx, JS::Handle underlyingObject, - const char* methodNameForErrorMessage, JS::Handle methodName, - JS::MutableHandle method); - -/** - * Streams spec, 6.3.2. InvokeOrNoop ( O, P, args ) - * As it happens, all callers pass exactly one argument. - */ -[[nodiscard]] extern bool InvokeOrNoop(JSContext* cx, JS::Handle O, - JS::Handle P, - JS::Handle arg, - JS::MutableHandle rval); - -/** - * Streams spec, 6.3.7. ValidateAndNormalizeHighWaterMark ( highWaterMark ) - */ -[[nodiscard]] extern bool ValidateAndNormalizeHighWaterMark( - JSContext* cx, JS::Handle highWaterMarkVal, - double* highWaterMark); - -/** - * Streams spec, 6.3.8. MakeSizeAlgorithmFromSizeFunction ( size ) - */ -[[nodiscard]] extern bool MakeSizeAlgorithmFromSizeFunction( - JSContext* cx, JS::Handle size); - -template -inline bool IsMaybeWrapped(const JS::Handle v) { - return v.isObject() && v.toObject().canUnwrapAs(); -} - -} // namespace js - -#endif // builtin_streams_MiscellaneousOperations_h diff --git a/js/src/builtin/streams/PullIntoDescriptor.cpp b/js/src/builtin/streams/PullIntoDescriptor.cpp deleted file mode 100644 index 1b6c99a45b61..000000000000 --- a/js/src/builtin/streams/PullIntoDescriptor.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Pull descriptor objects for tracking byte stream pull-into requests. */ - -#include "builtin/streams/PullIntoDescriptor.h" - -#include // uint32_t - -#include "js/Class.h" // JSClass, JSCLASS_HAS_RESERVED_SLOTS -#include "js/RootingAPI.h" // JS::Handle, JS::Rooted - -#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance - -using js::PullIntoDescriptor; - -using JS::Handle; -using JS::Int32Value; -using JS::ObjectOrNullValue; -using JS::ObjectValue; -using JS::Rooted; - -/* static */ PullIntoDescriptor* PullIntoDescriptor::create( - JSContext* cx, Handle buffer, uint32_t byteOffset, - uint32_t byteLength, uint32_t bytesFilled, uint32_t elementSize, - Handle ctor, ReaderType readerType) { - Rooted descriptor( - cx, NewBuiltinClassInstance(cx)); - if (!descriptor) { - return nullptr; - } - - descriptor->setFixedSlot(Slot_buffer, ObjectValue(*buffer)); - descriptor->setFixedSlot(Slot_Ctor, ObjectOrNullValue(ctor)); - descriptor->setFixedSlot(Slot_ByteOffset, Int32Value(byteOffset)); - descriptor->setFixedSlot(Slot_ByteLength, Int32Value(byteLength)); - descriptor->setFixedSlot(Slot_BytesFilled, Int32Value(bytesFilled)); - descriptor->setFixedSlot(Slot_ElementSize, Int32Value(elementSize)); - descriptor->setFixedSlot(Slot_ReaderType, - Int32Value(static_cast(readerType))); - return descriptor; -} - -const JSClass PullIntoDescriptor::class_ = { - "PullIntoDescriptor", JSCLASS_HAS_RESERVED_SLOTS(SlotCount)}; diff --git a/js/src/builtin/streams/PullIntoDescriptor.h b/js/src/builtin/streams/PullIntoDescriptor.h deleted file mode 100644 index 41378dd92e08..000000000000 --- a/js/src/builtin/streams/PullIntoDescriptor.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Pull descriptor objects for tracking byte stream pull-into requests. */ - -#ifndef builtin_streams_PullIntoDescriptor_h -#define builtin_streams_PullIntoDescriptor_h - -#include // int32_t, uint32_t - -#include "js/Class.h" // JSClass -#include "vm/ArrayBufferObject.h" // js::ArrayBufferObject; -#include "vm/NativeObject.h" // js::NativeObject - -namespace js { - -enum class ReaderType : int32_t { Default = 0, BYOB = 1 }; - -class PullIntoDescriptor : public NativeObject { - private: - enum Slots { - Slot_buffer, - Slot_ByteOffset, - Slot_ByteLength, - Slot_BytesFilled, - Slot_ElementSize, - Slot_Ctor, - Slot_ReaderType, - SlotCount - }; - - public: - static const JSClass class_; - - ArrayBufferObject* buffer() { - return &getFixedSlot(Slot_buffer).toObject().as(); - } - void setBuffer(ArrayBufferObject* buffer) { - setFixedSlot(Slot_buffer, ObjectValue(*buffer)); - } - JSObject* ctor() { return getFixedSlot(Slot_Ctor).toObjectOrNull(); } - uint32_t byteOffset() const { - return getFixedSlot(Slot_ByteOffset).toInt32(); - } - uint32_t byteLength() const { - return getFixedSlot(Slot_ByteLength).toInt32(); - } - uint32_t bytesFilled() const { - return getFixedSlot(Slot_BytesFilled).toInt32(); - } - void setBytesFilled(int32_t bytes) { - setFixedSlot(Slot_BytesFilled, Int32Value(bytes)); - } - uint32_t elementSize() const { - return getFixedSlot(Slot_ElementSize).toInt32(); - } - ReaderType readerType() const { - int32_t n = getFixedSlot(Slot_ReaderType).toInt32(); - MOZ_ASSERT(n == int32_t(ReaderType::Default) || - n == int32_t(ReaderType::BYOB)); - return ReaderType(n); - } - - static PullIntoDescriptor* create(JSContext* cx, - JS::Handle buffer, - uint32_t byteOffset, uint32_t byteLength, - uint32_t bytesFilled, uint32_t elementSize, - JS::Handle ctor, - ReaderType readerType); -}; - -} // namespace js - -#endif // builtin_streams_PullIntoDescriptor_h diff --git a/js/src/builtin/streams/QueueWithSizes-inl.h b/js/src/builtin/streams/QueueWithSizes-inl.h deleted file mode 100644 index fa73cdc7cacc..000000000000 --- a/js/src/builtin/streams/QueueWithSizes-inl.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Queue-with-sizes operations. */ - -#ifndef builtin_streams_QueueWithSizes_inl_h -#define builtin_streams_QueueWithSizes_inl_h - -#include "builtin/streams/QueueWithSizes.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT - -#include "js/RootingAPI.h" // JS::Handle -#include "js/Value.h" // JS::Value -#include "vm/List.h" // js::ListObject - -#include "vm/List-inl.h" // js::ListObject::* - -struct JS_PUBLIC_API JSContext; - -namespace js { - -namespace detail { - -// The *internal* representation of a queue-with-sizes is a List of even length -// where elements (2 * n, 2 * n + 1) represent the nth (value, size) element in -// the queue. - -inline JS::Value QueueFirstValue(ListObject* unwrappedQueue) { - MOZ_ASSERT(!unwrappedQueue->isEmpty(), - "can't examine first value in an empty queue-with-sizes"); - MOZ_ASSERT((unwrappedQueue->length() % 2) == 0, - "queue-with-sizes must consist of (value, size) element pairs and " - "so must have even length"); - return unwrappedQueue->get(0); -} - -inline double QueueFirstSize(ListObject* unwrappedQueue) { - MOZ_ASSERT(!unwrappedQueue->isEmpty(), - "can't examine first value in an empty queue-with-sizes"); - MOZ_ASSERT((unwrappedQueue->length() % 2) == 0, - "queue-with-sizes must consist of (value, size) element pairs and " - "so must have even length"); - return unwrappedQueue->get(1).toDouble(); -} - -inline void QueueRemoveFirstValueAndSize(ListObject* unwrappedQueue, - JSContext* cx) { - MOZ_ASSERT(!unwrappedQueue->isEmpty(), - "can't remove first value from an empty queue-with-sizes"); - MOZ_ASSERT((unwrappedQueue->length() % 2) == 0, - "queue-with-sizes must consist of (value, size) element pairs and " - "so must have even length"); - unwrappedQueue->popFirstPair(cx); -} - -[[nodiscard]] inline bool QueueAppendValueAndSize( - JSContext* cx, JS::Handle unwrappedQueue, - JS::Handle value, double size) { - return unwrappedQueue->appendValueAndSize(cx, value, size); -} - -} // namespace detail - -/** - * Streams spec, 6.2.3. PeekQueueValue ( container ) nothrow - */ -inline JS::Value PeekQueueValue(ListObject* queue) { - return detail::QueueFirstValue(queue); -} - -} // namespace js - -#endif // builtin_streams_QueueWithSizes_inl_h diff --git a/js/src/builtin/streams/QueueWithSizes.cpp b/js/src/builtin/streams/QueueWithSizes.cpp deleted file mode 100644 index db99d3a8ada5..000000000000 --- a/js/src/builtin/streams/QueueWithSizes.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Queue-with-sizes operations. */ - -#include "builtin/streams/QueueWithSizes-inl.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT -#include "mozilla/FloatingPoint.h" // mozilla::Is{Infinite,NaN} - -#include "builtin/streams/StreamController.h" // js::StreamController -#include "js/Class.h" // JSClass, JSCLASS_HAS_RESERVED_SLOTS -#include "js/Conversions.h" // JS::ToNumber -#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/RootingAPI.h" // JS::Rooted -#include "js/Value.h" // JS::Value, JS::{Number,Object}Value -#include "vm/Compartment.h" // JSCompartment -#include "vm/JSContext.h" // JSContext -#include "vm/List.h" // js::ListObject -#include "vm/NativeObject.h" // js::NativeObject - -#include "vm/Compartment-inl.h" // JSCompartment::wrap -#include "vm/JSContext-inl.h" // JSContext::check -#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance -#include "vm/List-inl.h" // js::ListObject::*, js::StoreNewListInFixedSlot -#include "vm/Realm-inl.h" // js::AutoRealm - -using JS::Handle; -using JS::MutableHandle; -using JS::NumberValue; -using JS::ObjectValue; -using JS::Rooted; -using JS::ToNumber; -using JS::Value; - -/*** 6.2. Queue-with-sizes operations ***************************************/ - -/** - * Streams spec, 6.2.1. DequeueValue ( container ) nothrow - */ -[[nodiscard]] bool js::DequeueValue( - JSContext* cx, Handle unwrappedContainer, - MutableHandle chunk) { - // Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal - // slots (implicit). - // Step 2: Assert: queue is not empty. - Rooted unwrappedQueue(cx, unwrappedContainer->queue()); - - // Step 3. Let pair be the first element of queue. - double chunkSize = detail::QueueFirstSize(unwrappedQueue); - chunk.set(detail::QueueFirstValue(unwrappedQueue)); - - // Step 4. Remove pair from queue, shifting all other elements downward - // (so that the second becomes the first, and so on). - detail::QueueRemoveFirstValueAndSize(unwrappedQueue, cx); - - // Step 5: Set container.[[queueTotalSize]] to - // container.[[queueTotalSize]] − pair.[[size]]. - // Step 6: If container.[[queueTotalSize]] < 0, set - // container.[[queueTotalSize]] to 0. - // (This can occur due to rounding errors.) - double totalSize = unwrappedContainer->queueTotalSize(); - totalSize -= chunkSize; - if (totalSize < 0) { - totalSize = 0; - } - unwrappedContainer->setQueueTotalSize(totalSize); - - // Step 7: Return pair.[[value]]. - return cx->compartment()->wrap(cx, chunk); -} - -void js::DequeueValue(StreamController* unwrappedContainer, JSContext* cx) { - // Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal - // slots (implicit). - // Step 2: Assert: queue is not empty. - ListObject* unwrappedQueue = unwrappedContainer->queue(); - - // Step 3. Let pair be the first element of queue. - // (The value is being discarded, so all we must extract is the size.) - double chunkSize = detail::QueueFirstSize(unwrappedQueue); - - // Step 4. Remove pair from queue, shifting all other elements downward - // (so that the second becomes the first, and so on). - detail::QueueRemoveFirstValueAndSize(unwrappedQueue, cx); - - // Step 5: Set container.[[queueTotalSize]] to - // container.[[queueTotalSize]] − pair.[[size]]. - // Step 6: If container.[[queueTotalSize]] < 0, set - // container.[[queueTotalSize]] to 0. - // (This can occur due to rounding errors.) - double totalSize = unwrappedContainer->queueTotalSize(); - totalSize -= chunkSize; - if (totalSize < 0) { - totalSize = 0; - } - unwrappedContainer->setQueueTotalSize(totalSize); - - // Step 7: Return pair.[[value]]. (omitted because not used) -} - -/** - * Streams spec, 6.2.2. EnqueueValueWithSize ( container, value, size ) throws - */ -[[nodiscard]] bool js::EnqueueValueWithSize( - JSContext* cx, Handle unwrappedContainer, - Handle value, Handle sizeVal) { - cx->check(value, sizeVal); - - // Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal - // slots (implicit). - // Step 2: Let size be ? ToNumber(size). - double size; - if (!ToNumber(cx, sizeVal, &size)) { - return false; - } - - // Step 3: If ! IsFiniteNonNegativeNumber(size) is false, throw a RangeError - // exception. - if (size < 0 || mozilla::IsNaN(size) || mozilla::IsInfinite(size)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, "size"); - return false; - } - - // Step 4: Append Record {[[value]]: value, [[size]]: size} as the last - // element of container.[[queue]]. - { - AutoRealm ar(cx, unwrappedContainer); - Rooted unwrappedQueue(cx, unwrappedContainer->queue()); - Rooted wrappedVal(cx, value); - if (!cx->compartment()->wrap(cx, &wrappedVal)) { - return false; - } - - if (!detail::QueueAppendValueAndSize(cx, unwrappedQueue, wrappedVal, - size)) { - return false; - } - } - - // Step 5: Set container.[[queueTotalSize]] to - // container.[[queueTotalSize]] + size. - unwrappedContainer->setQueueTotalSize(unwrappedContainer->queueTotalSize() + - size); - - return true; -} - -/** - * Streams spec, 6.2.4. ResetQueue ( container ) nothrow - */ -[[nodiscard]] bool js::ResetQueue( - JSContext* cx, Handle unwrappedContainer) { - // Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal - // slots (implicit). - // Step 2: Set container.[[queue]] to a new empty List. - if (!StoreNewListInFixedSlot(cx, unwrappedContainer, - StreamController::Slot_Queue)) { - return false; - } - - // Step 3: Set container.[[queueTotalSize]] to 0. - unwrappedContainer->setQueueTotalSize(0); - - return true; -} diff --git a/js/src/builtin/streams/QueueWithSizes.h b/js/src/builtin/streams/QueueWithSizes.h deleted file mode 100644 index da4f490c76d6..000000000000 --- a/js/src/builtin/streams/QueueWithSizes.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Queue-with-sizes operations. */ - -#ifndef builtin_streams_QueueWithSizes_h -#define builtin_streams_QueueWithSizes_h - -#include "mozilla/Assertions.h" // MOZ_ASSERT - -#include "jstypes.h" // JS_PUBLIC_API -#include "js/RootingAPI.h" // JS::{,Mutable}Handle -#include "js/Value.h" // JS::Value -#include "vm/List.h" // js::ListObject - -struct JS_PUBLIC_API JSContext; - -namespace js { - -class StreamController; - -/** - * Streams spec, 6.2.1. DequeueValue ( container ) nothrow - */ -[[nodiscard]] extern bool DequeueValue( - JSContext* cx, JS::Handle unwrappedContainer, - JS::MutableHandle chunk); - -/** - * Streams spec, 6.2.1. DequeueValue ( container ) nothrow - * when the dequeued value is ignored. - */ -extern void DequeueValue(StreamController* unwrappedContainer, JSContext* cx); - -/** - * Streams spec, 6.2.2. EnqueueValueWithSize ( container, value, size ) throws - */ -[[nodiscard]] extern bool EnqueueValueWithSize( - JSContext* cx, JS::Handle unwrappedContainer, - JS::Handle value, JS::Handle sizeVal); - -/** - * Streams spec, 6.2.4. ResetQueue ( container ) nothrow - */ -[[nodiscard]] extern bool ResetQueue( - JSContext* cx, JS::Handle unwrappedContainer); - -inline bool QueueIsEmpty(ListObject* unwrappedQueue) { - if (unwrappedQueue->isEmpty()) { - return true; - } - - MOZ_ASSERT((unwrappedQueue->length() % 2) == 0, - "queue-with-sizes must consist of (value, size) element pairs and " - "so must have even length"); - return false; -} - -} // namespace js - -#endif // builtin_streams_QueueWithSizes_h diff --git a/js/src/builtin/streams/QueueingStrategies.cpp b/js/src/builtin/streams/QueueingStrategies.cpp deleted file mode 100644 index 2b03d2fec7bc..000000000000 --- a/js/src/builtin/streams/QueueingStrategies.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Queuing strategies. */ - -#include "builtin/streams/QueueingStrategies.h" - -#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC -#include "js/CallArgs.h" // JS::CallArgs{,FromVp} -#include "js/Class.h" // JS::ObjectOpResult, JS_NULL_CLASS_OPS -#include "js/Conversions.h" // JS::ToNumber -#include "js/PropertySpec.h" // JS{Property,Function}Spec, JS_FN, JS_FS_END, JS_PS_END -#include "js/ProtoKey.h" // JSProto_{ByteLength,Count}QueuingStrategy -#include "js/RootingAPI.h" // JS::{Handle,Rooted} -#include "vm/Interpreter.h" // js::GetProperty -#include "vm/JSObject.h" // js::GetPrototypeFromBuiltinConstructor -#include "vm/ObjectOperations.h" // js::{Define,Get}Property -#include "vm/Runtime.h" // JSAtomState -#include "vm/StringType.h" // js::NameToId, PropertyName - -#include "vm/Compartment-inl.h" // js::UnwrapAndTypeCheckThis -#include "vm/JSObject-inl.h" // js::NewObjectWithClassProto -#include "vm/NativeObject-inl.h" // js::ThrowIfNotConstructing - -using js::ByteLengthQueuingStrategy; -using js::CountQueuingStrategy; -using js::PropertyName; -using js::UnwrapAndTypeCheckThis; - -using JS::CallArgs; -using JS::CallArgsFromVp; -using JS::Handle; -using JS::ObjectOpResult; -using JS::Rooted; -using JS::ToNumber; -using JS::ToObject; -using JS::Value; - -/*** 6.1. Queuing strategies ************************************************/ - -// Streams spec, 6.1.2.2. new ByteLengthQueuingStrategy({ highWaterMark }) -bool js::ByteLengthQueuingStrategy::constructor(JSContext* cx, unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - if (!ThrowIfNotConstructing(cx, args, "ByteLengthQueuingStrategy")) { - return false; - } - - // Implicit in the spec: Create the new strategy object. - Rooted proto(cx); - if (!GetPrototypeFromBuiltinConstructor( - cx, args, JSProto_ByteLengthQueuingStrategy, &proto)) { - return false; - } - Rooted strategy( - cx, NewObjectWithClassProto(cx, proto)); - if (!strategy) { - return false; - } - - // Implicit in the spec: Argument destructuring. - RootedObject argObj(cx, ToObject(cx, args.get(0))); - if (!argObj) { - return false; - } - - // https://heycam.github.io/webidl/#es-dictionary - // 3.2.17. Dictionary types - // Step 4.1.2: Let esMemberValue be an ECMAScript value, - // depending on Type(esDict): ? Get(esDict, key) - RootedValue highWaterMarkV(cx); - if (!GetProperty(cx, argObj, argObj, cx->names().highWaterMark, - &highWaterMarkV)) { - return false; - } - // Step 4.1.5: Otherwise, if esMemberValue is undefined and - // member is required, then throw a TypeError. - if (highWaterMarkV.isUndefined()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_STREAM_MISSING_HIGHWATERMARK); - return false; - } - // Step 4.1.3: If esMemberValue is not undefined, then: - // Let idlMemberValue be the result of converting esMemberValue to - // an IDL value whose type is the type member is declared to be of. - double highWaterMark; - if (!ToNumber(cx, highWaterMarkV, &highWaterMark)) { - return false; - } - - // Step 1: Set this.[[highWaterMark]] to init["highWaterMark"]. - strategy->setHighWaterMark(highWaterMark); - - args.rval().setObject(*strategy); - return true; -} - -static bool ByteLengthQueuingStrategy_highWaterMark(JSContext* cx, - unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - Rooted unwrappedStrategy( - cx, UnwrapAndTypeCheckThis( - cx, args, "get highWaterMark")); - if (!unwrappedStrategy) { - return false; - } - - // Step 1: Return this.[[highWaterMark]]. - args.rval().setDouble(unwrappedStrategy->highWaterMark()); - return true; -} - -// Streams spec 6.1.2.3.1. size ( chunk ) -static bool ByteLengthQueuingStrategy_size(JSContext* cx, unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1: Return ? GetV(chunk, "byteLength"). - return GetProperty(cx, args.get(0), cx->names().byteLength, args.rval()); -} - -static const JSPropertySpec ByteLengthQueuingStrategy_properties[] = { - JS_PSG("highWaterMark", ByteLengthQueuingStrategy_highWaterMark, - JSPROP_ENUMERATE), - JS_STRING_SYM_PS(toStringTag, "ByteLengthQueuingStrategy", JSPROP_READONLY), - JS_PS_END}; - -static const JSFunctionSpec ByteLengthQueuingStrategy_methods[] = { - JS_FN("size", ByteLengthQueuingStrategy_size, 1, 0), JS_FS_END}; - -JS_STREAMS_CLASS_SPEC(ByteLengthQueuingStrategy, 1, SlotCount, 0, 0, - JS_NULL_CLASS_OPS); - -// Streams spec, 6.1.3.2. new CountQueuingStrategy({ highWaterMark }) -bool js::CountQueuingStrategy::constructor(JSContext* cx, unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - if (!ThrowIfNotConstructing(cx, args, "CountQueuingStrategy")) { - return false; - } - - // Implicit in the spec: Create the new strategy object. - RootedObject proto(cx); - if (!GetPrototypeFromBuiltinConstructor( - cx, args, JSProto_CountQueuingStrategy, &proto)) { - return false; - } - Rooted strategy( - cx, NewObjectWithClassProto(cx, proto)); - if (!strategy) { - return false; - } - - // Implicit in the spec: Argument destructuring. - RootedObject argObj(cx, ToObject(cx, args.get(0))); - if (!argObj) { - return false; - } - // https://heycam.github.io/webidl/#es-dictionary - // 3.2.17. Dictionary types - // Step 4.1.2: Let esMemberValue be an ECMAScript value, - // depending on Type(esDict): ? Get(esDict, key) - RootedValue highWaterMarkV(cx); - if (!GetProperty(cx, argObj, argObj, cx->names().highWaterMark, - &highWaterMarkV)) { - return false; - } - // Step 4.1.5: Otherwise, if esMemberValue is undefined and - // member is required, then throw a TypeError. - if (highWaterMarkV.isUndefined()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_STREAM_MISSING_HIGHWATERMARK); - return false; - } - // Step 4.1.3: If esMemberValue is not undefined, then: - // Let idlMemberValue be the result of converting esMemberValue to - // an IDL value whose type is the type member is declared to be of. - double highWaterMark; - if (!ToNumber(cx, highWaterMarkV, &highWaterMark)) { - return false; - } - - // Step 1: Set this.[[highWaterMark]] to init["highWaterMark"]. - strategy->setHighWaterMark(highWaterMark); - - args.rval().setObject(*strategy); - return true; -} - -static bool CountQueuingStrategy_highWaterMark(JSContext* cx, unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - Rooted unwrappedStrategy( - cx, UnwrapAndTypeCheckThis(cx, args, - "get highWaterMark")); - if (!unwrappedStrategy) { - return false; - } - - // Step 1: Return this.[[highWaterMark]]. - args.rval().setDouble(unwrappedStrategy->highWaterMark()); - return true; -} - -// Streams spec 6.1.3.3.1. size ( chunk ) -static bool CountQueuingStrategy_size(JSContext* cx, unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1: Return 1. - args.rval().setInt32(1); - return true; -} - -static const JSPropertySpec CountQueuingStrategy_properties[] = { - JS_PSG("highWaterMark", CountQueuingStrategy_highWaterMark, - JSPROP_ENUMERATE), - JS_STRING_SYM_PS(toStringTag, "CountQueuingStrategy", JSPROP_READONLY), - JS_PS_END}; - -static const JSFunctionSpec CountQueuingStrategy_methods[] = { - JS_FN("size", CountQueuingStrategy_size, 0, 0), JS_FS_END}; - -JS_STREAMS_CLASS_SPEC(CountQueuingStrategy, 1, SlotCount, 0, 0, - JS_NULL_CLASS_OPS); diff --git a/js/src/builtin/streams/QueueingStrategies.h b/js/src/builtin/streams/QueueingStrategies.h deleted file mode 100644 index 6da83e770b3f..000000000000 --- a/js/src/builtin/streams/QueueingStrategies.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Queuing strategies. */ - -#ifndef builtin_stream_QueueingStrategies_h -#define builtin_stream_QueueingStrategies_h - -#include "js/Class.h" // JSClass, js::ClassSpec -#include "js/Value.h" // JS::Value -#include "vm/JSContext.h" // JSContext -#include "vm/NativeObject.h" // js::NativeObject - -namespace js { - -class ByteLengthQueuingStrategy : public NativeObject { - public: - enum Slots { Slot_HighWaterMark, SlotCount }; - - double highWaterMark() const { - return getFixedSlot(Slot_HighWaterMark).toDouble(); - } - void setHighWaterMark(double value) { - setFixedSlot(Slot_HighWaterMark, JS::DoubleValue(value)); - } - - static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); - static const ClassSpec classSpec_; - static const JSClass class_; - static const ClassSpec protoClassSpec_; - static const JSClass protoClass_; -}; - -class CountQueuingStrategy : public NativeObject { - public: - enum Slots { Slot_HighWaterMark, SlotCount }; - - double highWaterMark() const { - return getFixedSlot(Slot_HighWaterMark).toDouble(); - } - void setHighWaterMark(double value) { - setFixedSlot(Slot_HighWaterMark, JS::DoubleValue(value)); - } - - static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); - static const ClassSpec classSpec_; - static const JSClass class_; - static const ClassSpec protoClassSpec_; - static const JSClass protoClass_; -}; - -} // namespace js - -#endif // builtin_stream_QueueingStrategies_h diff --git a/js/src/builtin/streams/ReadableStream.cpp b/js/src/builtin/streams/ReadableStream.cpp deleted file mode 100644 index a8445aa65eba..000000000000 --- a/js/src/builtin/streams/ReadableStream.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Class ReadableStream. */ - -#include "builtin/streams/ReadableStream.h" - -#include "mozilla/Maybe.h" // mozilla::Maybe, mozilla::Some - -#include "jspubtd.h" // JSProto_ReadableStream - -#include "builtin/Array.h" // js::NewDenseFullyAllocatedArray -#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC -#include "builtin/streams/MiscellaneousOperations.h" // js::MakeSizeAlgorithmFromSizeFunction, js::ValidateAndNormalizeHighWaterMark, js::ReturnPromiseRejectedWithPendingError -#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController -#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::SetUpReadableStreamDefaultControllerFromUnderlyingSource -#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStreamCancel -#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStream{PipeTo,Tee} -#include "builtin/streams/ReadableStreamReader.h" // js::CreateReadableStream{BYOB,Default}Reader, js::ForAuthorCodeBool -#include "js/CallArgs.h" // JS::CallArgs{,FromVp} -#include "js/Class.h" // JSCLASS_SLOT0_IS_NSISUPPORTS, JS_NULL_CLASS_OPS -#include "js/Conversions.h" // JS::ToBoolean -#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/PropertySpec.h" // JS{Function,Property}Spec, JS_FN, JS_PSG, JS_{FS,PS}_END -#include "js/RootingAPI.h" // JS::Handle, JS::Rooted, js::CanGC -#include "js/Stream.h" // JS::ReadableStream{Mode,UnderlyingSource} -#include "js/Value.h" // JS::Value -#include "vm/Interpreter.h" // js::GetProperty -#include "vm/JSContext.h" // JSContext -#include "vm/JSObject.h" // js::GetPrototypeFromBuiltinConstructor -#include "vm/ObjectOperations.h" // js::GetProperty -#include "vm/PlainObject.h" // js::PlainObject -#include "vm/Runtime.h" // JSAtomState, JSRuntime -#include "vm/StringType.h" // js::EqualStrings, js::ToString - -#include "vm/Compartment-inl.h" // js::UnwrapAndTypeCheck{Argument,This,Value} -#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance -#include "vm/NativeObject-inl.h" // js::ThrowIfNotConstructing - -using mozilla::Maybe; -using mozilla::Some; - -using js::CanGC; -using js::ClassSpec; -using js::CreateReadableStreamDefaultReader; -using js::EqualStrings; -using js::ForAuthorCodeBool; -using js::GetErrorMessage; -using js::NativeObject; -using js::NewBuiltinClassInstance; -using js::NewDenseFullyAllocatedArray; -using js::PlainObject; -using js::ReadableStream; -using js::ReadableStreamTee; -using js::ReturnPromiseRejectedWithPendingError; -using js::ToString; -using js::UnwrapAndTypeCheckArgument; -using js::UnwrapAndTypeCheckThis; -using js::UnwrapAndTypeCheckValue; - -using JS::CallArgs; -using JS::CallArgsFromVp; -using JS::Handle; -using JS::ObjectValue; -using JS::Rooted; -using JS::Value; - -/*** 3.2. Class ReadableStream **********************************************/ - -JS::ReadableStreamMode ReadableStream::mode() const { - ReadableStreamController* controller = this->controller(); - if (controller->is()) { - return JS::ReadableStreamMode::Default; - } - return controller->as().hasExternalSource() - ? JS::ReadableStreamMode::ExternalSource - : JS::ReadableStreamMode::Byte; -} - -ReadableStream* ReadableStream::createExternalSourceStream( - JSContext* cx, JS::ReadableStreamUnderlyingSource* source, - void* nsISupportsObject_alreadyAddreffed /* = nullptr */, - Handle proto /* = nullptr */) { - Rooted stream( - cx, create(cx, nsISupportsObject_alreadyAddreffed, proto)); - if (!stream) { - return nullptr; - } - - if (!SetUpExternalReadableByteStreamController(cx, stream, source)) { - return nullptr; - } - - return stream; -} - -/** - * Streams spec, 3.2.3. new ReadableStream(underlyingSource = {}, strategy = {}) - */ -bool ReadableStream::constructor(JSContext* cx, unsigned argc, JS::Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - if (!ThrowIfNotConstructing(cx, args, "ReadableStream")) { - return false; - } - - // Implicit in the spec: argument default values. - Rooted underlyingSource(cx, args.get(0)); - if (underlyingSource.isUndefined()) { - JSObject* emptyObj = NewPlainObject(cx); - if (!emptyObj) { - return false; - } - underlyingSource = ObjectValue(*emptyObj); - } - - Rooted strategy(cx, args.get(1)); - if (strategy.isUndefined()) { - JSObject* emptyObj = NewPlainObject(cx); - if (!emptyObj) { - return false; - } - strategy = ObjectValue(*emptyObj); - } - - // Implicit in the spec: Set this to - // OrdinaryCreateFromConstructor(NewTarget, ...). - // Step 1: Perform ! InitializeReadableStream(this). - Rooted proto(cx); - if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ReadableStream, - &proto)) { - return false; - } - Rooted stream(cx, - ReadableStream::create(cx, nullptr, proto)); - if (!stream) { - return false; - } - - // Step 2: Let size be ? GetV(strategy, "size"). - Rooted size(cx); - if (!GetProperty(cx, strategy, cx->names().size, &size)) { - return false; - } - - // Step 3: Let highWaterMark be ? GetV(strategy, "highWaterMark"). - Rooted highWaterMarkVal(cx); - if (!GetProperty(cx, strategy, cx->names().highWaterMark, - &highWaterMarkVal)) { - return false; - } - - // Step 4: Let type be ? GetV(underlyingSource, "type"). - Rooted type(cx); - if (!GetProperty(cx, underlyingSource, cx->names().type, &type)) { - return false; - } - - // Step 5: Let typeString be ? ToString(type). - Rooted typeString(cx, ToString(cx, type)); - if (!typeString) { - return false; - } - - // Step 6: If typeString is "bytes", - bool equal; - if (!EqualStrings(cx, typeString, cx->names().bytes, &equal)) { - return false; - } - if (equal) { - // The rest of step 6 is unimplemented, since we don't support - // user-defined byte streams yet. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_BYTES_TYPE_NOT_IMPLEMENTED); - return false; - } - - // Step 7: Otherwise, if type is undefined, - if (type.isUndefined()) { - // Step 7.a: Let sizeAlgorithm be ? MakeSizeAlgorithmFromSizeFunction(size). - if (!MakeSizeAlgorithmFromSizeFunction(cx, size)) { - return false; - } - - // Step 7.b: If highWaterMark is undefined, let highWaterMark be 1. - double highWaterMark; - if (highWaterMarkVal.isUndefined()) { - highWaterMark = 1; - } else { - // Step 7.c: Set highWaterMark to ? - // ValidateAndNormalizeHighWaterMark(highWaterMark). - if (!ValidateAndNormalizeHighWaterMark(cx, highWaterMarkVal, - &highWaterMark)) { - return false; - } - } - - // Step 7.d: Perform - // ? SetUpReadableStreamDefaultControllerFromUnderlyingSource( - // this, underlyingSource, highWaterMark, sizeAlgorithm). - if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource( - cx, stream, underlyingSource, highWaterMark, size)) { - return false; - } - - args.rval().setObject(*stream); - return true; - } - - // Step 8: Otherwise, throw a RangeError exception. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_UNDERLYINGSOURCE_TYPE_WRONG); - return false; -} - -/** - * Streams spec, 3.2.5.1. get locked - */ -[[nodiscard]] static bool ReadableStream_locked(JSContext* cx, unsigned argc, - JS::Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1: If ! IsReadableStream(this) is false, throw a TypeError exception. - Rooted unwrappedStream( - cx, UnwrapAndTypeCheckThis(cx, args, "get locked")); - if (!unwrappedStream) { - return false; - } - - // Step 2: Return ! IsReadableStreamLocked(this). - args.rval().setBoolean(unwrappedStream->locked()); - return true; -} - -/** - * Streams spec, 3.2.5.2. cancel ( reason ) - */ -[[nodiscard]] static bool ReadableStream_cancel(JSContext* cx, unsigned argc, - JS::Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1: If ! IsReadableStream(this) is false, return a promise rejected - // with a TypeError exception. - Rooted unwrappedStream( - cx, UnwrapAndTypeCheckThis(cx, args, "cancel")); - if (!unwrappedStream) { - return ReturnPromiseRejectedWithPendingError(cx, args); - } - - // Step 2: If ! IsReadableStreamLocked(this) is true, return a promise - // rejected with a TypeError exception. - if (unwrappedStream->locked()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_LOCKED_METHOD, "cancel"); - return ReturnPromiseRejectedWithPendingError(cx, args); - } - - // Step 3: Return ! ReadableStreamCancel(this, reason). - Rooted cancelPromise( - cx, js::ReadableStreamCancel(cx, unwrappedStream, args.get(0))); - if (!cancelPromise) { - return false; - } - args.rval().setObject(*cancelPromise); - return true; -} - -// Streams spec, 3.2.5.3. -// getIterator({ preventCancel } = {}) -// -// Not implemented. - -/** - * https://streams.spec.whatwg.org/#rs-get-reader - * ReadableStreamReader getReader(optional ReadableStreamGetReaderOptions - * options = {}); - */ -[[nodiscard]] static bool ReadableStream_getReader(JSContext* cx, unsigned argc, - JS::Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Implicit |this| check. - Rooted unwrappedStream( - cx, UnwrapAndTypeCheckThis(cx, args, "getReader")); - if (!unwrappedStream) { - return false; - } - - // Implicit in the spec: Dictionary destructuring. - // https://heycam.github.io/webidl/#es-dictionary - // 3.2.17. Dictionary types - - Rooted optionsVal(cx, args.get(0)); - // Step 1. - if (!optionsVal.isNullOrUndefined() && !optionsVal.isObject()) { - ReportValueError(cx, JSMSG_CANT_CONVERT_TO, JSDVG_IGNORE_STACK, optionsVal, - nullptr, "dictionary"); - return false; - } - - Maybe mode; - // Step 4: ... - // - // - Optimized for one dictionary member. - // - Treat non-object options as non-existing "mode" member. - if (optionsVal.isObject()) { - Rooted modeVal(cx); - if (!GetProperty(cx, optionsVal, cx->names().mode, &modeVal)) { - return false; - } - - // Step 4.1.3: If esMemberValue is not undefined, then: ... - if (!modeVal.isUndefined()) { - // https://heycam.github.io/webidl/#es-enumeration - // 3.2.18. Enumeration types - - // Step 1: Let S be the result of calling ToString(V). - Rooted modeStr(cx, ToString(cx, modeVal)); - if (!modeStr) { - return false; - } - - // Step 2: If S is not one of E's enumeration values, - // then throw a TypeError. - // - // Note: We only have one valid value "byob". - bool equal; - if (!EqualStrings(cx, modeStr, cx->names().byob, &equal)) { - return false; - } - if (!equal) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_INVALID_READER_MODE); - return false; - } - - mode = Some(JS::ReadableStreamReaderMode::Byob); - } - } - - // Step 1: If options["mode"] does not exist, - // return ? AcquireReadableStreamDefaultReader(this). - Rooted reader(cx); - if (mode.isNothing()) { - reader = CreateReadableStreamDefaultReader(cx, unwrappedStream, - ForAuthorCodeBool::Yes); - } else { - // Step 2: Assert: options["mode"] is "byob". - MOZ_ASSERT(mode.value() == JS::ReadableStreamReaderMode::Byob); - - // Step 3: Return ? AcquireReadableStreamBYOBReader(this). - reader = CreateReadableStreamBYOBReader(cx, unwrappedStream, - ForAuthorCodeBool::Yes); - } - - if (!reader) { - return false; - } - - args.rval().setObject(*reader); - return true; -} - -/** - * Streams spec, 3.2.5.7. tee() - */ -static bool ReadableStream_tee(JSContext* cx, unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1: If ! IsReadableStream(this) is false, throw a TypeError exception. - Rooted unwrappedStream( - cx, UnwrapAndTypeCheckThis(cx, args, "tee")); - if (!unwrappedStream) { - return false; - } - - // Step 2: Let branches be ? ReadableStreamTee(this, false). - Rooted branch1(cx); - Rooted branch2(cx); - if (!ReadableStreamTee(cx, unwrappedStream, false, &branch1, &branch2)) { - return false; - } - - // Step 3: Return ! CreateArrayFromList(branches). - Rooted branches(cx, NewDenseFullyAllocatedArray(cx, 2)); - if (!branches) { - return false; - } - branches->setDenseInitializedLength(2); - branches->initDenseElement(0, ObjectValue(*branch1)); - branches->initDenseElement(1, ObjectValue(*branch2)); - - args.rval().setObject(*branches); - return true; -} - -// Streams spec, 3.2.5.8. -// [@@asyncIterator]({ preventCancel } = {}) -// -// Not implemented. - -static const JSFunctionSpec ReadableStream_methods[] = { - JS_FN("cancel", ReadableStream_cancel, 0, JSPROP_ENUMERATE), - JS_FN("getReader", ReadableStream_getReader, 0, JSPROP_ENUMERATE), - // pipeTo is only conditionally supported right now, so it must be manually - // added below if desired. - JS_FN("tee", ReadableStream_tee, 0, JSPROP_ENUMERATE), JS_FS_END}; - -static const JSPropertySpec ReadableStream_properties[] = { - JS_PSG("locked", ReadableStream_locked, JSPROP_ENUMERATE), - JS_STRING_SYM_PS(toStringTag, "ReadableStream", JSPROP_READONLY), - JS_PS_END}; - -const ClassSpec ReadableStream::classSpec_ = { - js::GenericCreateConstructor, - js::GenericCreatePrototype, - nullptr, - nullptr, - ReadableStream_methods, - ReadableStream_properties, - nullptr, - 0}; - -const JSClass ReadableStream::class_ = { - "ReadableStream", - JSCLASS_HAS_RESERVED_SLOTS(ReadableStream::SlotCount) | - JSCLASS_HAS_CACHED_PROTO(JSProto_ReadableStream) | - JSCLASS_SLOT0_IS_NSISUPPORTS, - JS_NULL_CLASS_OPS, &ReadableStream::classSpec_}; - -const JSClass ReadableStream::protoClass_ = { - "ReadableStream.prototype", - JSCLASS_HAS_CACHED_PROTO(JSProto_ReadableStream), JS_NULL_CLASS_OPS, - &ReadableStream::classSpec_}; diff --git a/js/src/builtin/streams/ReadableStream.h b/js/src/builtin/streams/ReadableStream.h deleted file mode 100644 index f59cfbc99d53..000000000000 --- a/js/src/builtin/streams/ReadableStream.h +++ /dev/null @@ -1,144 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Class ReadableStream. */ - -#ifndef builtin_streams_ReadableStream_h -#define builtin_streams_ReadableStream_h - -#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} - -#include // uint32_t - -#include "jstypes.h" // JS_PUBLIC_API -#include "js/Class.h" // JSClass, js::ClassSpec -#include "js/RootingAPI.h" // JS::Handle -#include "js/Stream.h" // JS::ReadableStream{Mode,UnderlyingSource} -#include "js/Value.h" // JS::Int32Value, JS::ObjectValue, JS::UndefinedValue -#include "vm/NativeObject.h" // js::NativeObject - -class JS_PUBLIC_API JSObject; - -namespace js { - -class ReadableStreamController; - -class ReadableStream : public NativeObject { - public: - /** - * Memory layout of Stream instances. - * - * See https://streams.spec.whatwg.org/#rs-internal-slots for details on - * the stored state. [[state]] and [[disturbed]] are stored in - * StreamSlot_State as ReadableStream::State enum values. - * - * Of the stored values, Reader and StoredError might be cross-compartment - * wrappers. This can happen if the Reader was created by applying a - * different compartment's ReadableStream.prototype.getReader method. - * - * A stream's associated controller is always created from under the - * stream's constructor and thus cannot be in a different compartment. - */ - enum Slots { - /** - * Optional pointer to make the stream participate in Gecko's cycle - * collection. See also JSCLASS_SLOT0_IS_NSISUPPORTS. - */ - Slot_ISupports, - - Slot_Controller, - Slot_Reader, - Slot_State, - Slot_StoredError, - SlotCount - }; - - private: - enum StateBits { - Readable = 0, - Closed = 1, - Errored = 2, - StateMask = 0x000000ff, - Disturbed = 0x00000100 - }; - - uint32_t stateBits() const { return getFixedSlot(Slot_State).toInt32(); } - void initStateBits(uint32_t stateBits) { - MOZ_ASSERT((stateBits & ~Disturbed) <= Errored); - setFixedSlot(Slot_State, JS::Int32Value(stateBits)); - } - void setStateBits(uint32_t stateBits) { -#ifdef DEBUG - bool wasDisturbed = disturbed(); - bool wasClosedOrErrored = closed() || errored(); -#endif - initStateBits(stateBits); - MOZ_ASSERT_IF(wasDisturbed, disturbed()); - MOZ_ASSERT_IF(wasClosedOrErrored, !readable()); - } - - StateBits state() const { return StateBits(stateBits() & StateMask); } - void setState(StateBits state) { - MOZ_ASSERT(state <= Errored); - uint32_t current = stateBits() & ~StateMask; - setStateBits(current | state); - } - - public: - bool readable() const { return state() == Readable; } - bool closed() const { return state() == Closed; } - void setClosed() { setState(Closed); } - bool errored() const { return state() == Errored; } - void setErrored() { setState(Errored); } - bool disturbed() const { return stateBits() & Disturbed; } - void setDisturbed() { setStateBits(stateBits() | Disturbed); } - - bool hasController() const { - return !getFixedSlot(Slot_Controller).isUndefined(); - } - inline ReadableStreamController* controller() const; - inline void setController(ReadableStreamController* controller); - void clearController() { - setFixedSlot(Slot_Controller, JS::UndefinedValue()); - } - - bool hasReader() const { return !getFixedSlot(Slot_Reader).isUndefined(); } - void setReader(JSObject* reader) { - setFixedSlot(Slot_Reader, JS::ObjectValue(*reader)); - } - void clearReader() { setFixedSlot(Slot_Reader, JS::UndefinedValue()); } - - JS::Value storedError() const { return getFixedSlot(Slot_StoredError); } - void setStoredError(JS::Handle value) { - setFixedSlot(Slot_StoredError, value); - } - - JS::ReadableStreamMode mode() const; - - bool locked() const; - - [[nodiscard]] static ReadableStream* create( - JSContext* cx, void* nsISupportsObject_alreadyAddreffed = nullptr, - JS::Handle proto = nullptr); - static ReadableStream* createExternalSourceStream( - JSContext* cx, JS::ReadableStreamUnderlyingSource* source, - void* nsISupportsObject_alreadyAddreffed = nullptr, - JS::Handle proto = nullptr); - - static bool constructor(JSContext* cx, unsigned argc, Value* vp); - static const ClassSpec classSpec_; - static const JSClass class_; - static const ClassSpec protoClassSpec_; - static const JSClass protoClass_; -}; - -[[nodiscard]] extern bool SetUpExternalReadableByteStreamController( - JSContext* cx, JS::Handle stream, - JS::ReadableStreamUnderlyingSource* source); - -} // namespace js - -#endif // builtin_streams_ReadableStream_h diff --git a/js/src/builtin/streams/ReadableStreamBYOBReader.cpp b/js/src/builtin/streams/ReadableStreamBYOBReader.cpp deleted file mode 100644 index eb0210dc9061..000000000000 --- a/js/src/builtin/streams/ReadableStreamBYOBReader.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* - * Class ReadableStreamBYOBReader. - * - * Byte streams and BYOB readers are unimplemented, so this is skeletal -- yet - * helpful to ensure certain trivial tests of the functionality in wpt, that - * don't actually test fully-constructed byte streams/BYOB readers, pass. 🙄 - */ - -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "builtin/streams/ReadableStreamReader.h" // js::CreateReadableStreamBYOBReader, js::ForAuthorCodeBool -#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* - -using JS::Handle; - -/*** 3.7. Class ReadableStreamBYOBReader *********************************/ - -/** - * Stream spec, 3.7.3. new ReadableStreamBYOBReader ( stream ) - * Steps 2-5. - */ -[[nodiscard]] JSObject* js::CreateReadableStreamBYOBReader( - JSContext* cx, Handle unwrappedStream, - ForAuthorCodeBool forAuthorCode, Handle proto /* = nullptr */) { - // Step 2: If ! IsReadableByteStreamController( - // stream.[[readableStreamController]]) is false, throw a - // TypeError exception. - // We don't implement byte stream controllers yet, so always throw here. Note - // that JSMSG_READABLESTREAM_BYTES_TYPE_NOT_IMPLEMENTED can't be used here - // because it's a RangeError (and sadly wpt actually tests this and we have a - // spurious failure if we don't make this a TypeError). - JS_ReportErrorNumberASCII( - cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_BYOB_READER_FOR_NON_BYTE_STREAM); - - // Step 3: If ! IsReadableStreamLocked(stream) is true, throw a TypeError - // exception. - // Step 4: Perform ! ReadableStreamReaderGenericInitialize(this, stream). - // Step 5: Set this.[[readIntoRequests]] to a new empty List. - // Steps 3-5 are presently unreachable. - return nullptr; -} diff --git a/js/src/builtin/streams/ReadableStreamController.h b/js/src/builtin/streams/ReadableStreamController.h deleted file mode 100644 index df347356d622..000000000000 --- a/js/src/builtin/streams/ReadableStreamController.h +++ /dev/null @@ -1,266 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* ReadableStream controller classes and functions. */ - -#ifndef builtin_streams_ReadableStreamController_h -#define builtin_streams_ReadableStreamController_h - -#include "mozilla/Assertions.h" // MOZ_ASSERT - -#include // uint32_t - -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "builtin/streams/StreamController.h" // js::StreamController -#include "js/Class.h" // JSClass, js::ClassSpec -#include "js/RootingAPI.h" // JS::Handle -#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource -#include "js/Value.h" // JS::Value, JS::{Number,Object,Private,Undefined}Value, JS::UndefinedHandleValue -#include "vm/List.h" // js::ListObject -#include "vm/NativeObject.h" // js::NativeObject - -namespace js { - -class PromiseObject; - -class ReadableStreamController : public StreamController { - public: - /** - * Memory layout for ReadableStream controllers, starting after the slots - * reserved for queue container usage. - * - * Storage of the internal slots listed in the standard is fairly - * straightforward except for [[pullAlgorithm]] and [[cancelAlgorithm]]. - * These algorithms are not stored as JSFunction objects. Rather, there are - * three cases: - * - * - Streams created with `new ReadableStream`: The methods are stored - * in Slot_PullMethod and Slot_CancelMethod. The underlying source - * object (`this` for these methods) is in Slot_UnderlyingSource. - * - * - External source streams. Slot_UnderlyingSource is a PrivateValue - * pointing to the JS::ReadableStreamUnderlyingSource object. The - * algorithms are implemented using the .pull() and .cancel() methods - * of that object. Slot_Pull/CancelMethod are undefined. - * - * - Tee streams. Slot_UnderlyingSource is a TeeState object. The - * pull/cancel algorithms are implemented as separate functions in - * Stream.cpp. Slot_Pull/CancelMethod are undefined. - * - * UnderlyingSource, PullMethod, and CancelMethod can be wrappers to objects - * in other compartments. - * - * StrategyHWM and Flags are both primitive (numeric) values. - */ - enum Slots { - Slot_Stream = StreamController::SlotCount, - Slot_UnderlyingSource, - Slot_PullMethod, - Slot_CancelMethod, - Slot_StrategyHWM, - Slot_Flags, - SlotCount - }; - - enum ControllerFlags { - Flag_Started = 1 << 0, - Flag_Pulling = 1 << 1, - Flag_PullAgain = 1 << 2, - Flag_CloseRequested = 1 << 3, - Flag_TeeBranch1 = 1 << 4, - Flag_TeeBranch2 = 1 << 5, - Flag_ExternalSource = 1 << 6, - Flag_SourceLocked = 1 << 7, - }; - - ReadableStream* stream() const { - return &getFixedSlot(Slot_Stream).toObject().as(); - } - void setStream(ReadableStream* stream) { - setFixedSlot(Slot_Stream, JS::ObjectValue(*stream)); - } - JS::Value underlyingSource() const { - return getFixedSlot(Slot_UnderlyingSource); - } - void setUnderlyingSource(const JS::Value& underlyingSource) { - setFixedSlot(Slot_UnderlyingSource, underlyingSource); - } - JS::Value pullMethod() const { return getFixedSlot(Slot_PullMethod); } - void setPullMethod(const JS::Value& pullMethod) { - setFixedSlot(Slot_PullMethod, pullMethod); - } - JS::Value cancelMethod() const { return getFixedSlot(Slot_CancelMethod); } - void setCancelMethod(const JS::Value& cancelMethod) { - setFixedSlot(Slot_CancelMethod, cancelMethod); - } - JS::ReadableStreamUnderlyingSource* externalSource() const { - static_assert(alignof(JS::ReadableStreamUnderlyingSource) >= 2, - "External underling sources are stored as PrivateValues, " - "so they must have even addresses"); - MOZ_ASSERT(hasExternalSource()); - return static_cast( - underlyingSource().toPrivate()); - } - void setExternalSource(JS::ReadableStreamUnderlyingSource* underlyingSource) { - setUnderlyingSource(JS::PrivateValue(underlyingSource)); - addFlags(Flag_ExternalSource); - } - static void clearUnderlyingSource( - JS::Handle controller, - bool finalizeSource = true) { - if (controller->hasExternalSource()) { - if (finalizeSource) { - controller->externalSource()->finalize(); - } - controller->setFlags(controller->flags() & ~Flag_ExternalSource); - } - controller->setUnderlyingSource(JS::UndefinedHandleValue); - } - double strategyHWM() const { - return getFixedSlot(Slot_StrategyHWM).toNumber(); - } - void setStrategyHWM(double highWaterMark) { - setFixedSlot(Slot_StrategyHWM, NumberValue(highWaterMark)); - } - uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); } - void setFlags(uint32_t flags) { setFixedSlot(Slot_Flags, Int32Value(flags)); } - void addFlags(uint32_t flags) { setFlags(this->flags() | flags); } - void removeFlags(uint32_t flags) { setFlags(this->flags() & ~flags); } - bool started() const { return flags() & Flag_Started; } - void setStarted() { addFlags(Flag_Started); } - bool pulling() const { return flags() & Flag_Pulling; } - void setPulling() { addFlags(Flag_Pulling); } - void clearPullFlags() { removeFlags(Flag_Pulling | Flag_PullAgain); } - bool pullAgain() const { return flags() & Flag_PullAgain; } - void setPullAgain() { addFlags(Flag_PullAgain); } - bool closeRequested() const { return flags() & Flag_CloseRequested; } - void setCloseRequested() { addFlags(Flag_CloseRequested); } - bool isTeeBranch1() const { return flags() & Flag_TeeBranch1; } - void setTeeBranch1() { - MOZ_ASSERT(!isTeeBranch2()); - addFlags(Flag_TeeBranch1); - } - bool isTeeBranch2() const { return flags() & Flag_TeeBranch2; } - void setTeeBranch2() { - MOZ_ASSERT(!isTeeBranch1()); - addFlags(Flag_TeeBranch2); - } - bool hasExternalSource() const { return flags() & Flag_ExternalSource; } - bool sourceLocked() const { return flags() & Flag_SourceLocked; } - void setSourceLocked() { addFlags(Flag_SourceLocked); } - void clearSourceLocked() { removeFlags(Flag_SourceLocked); } -}; - -class ReadableStreamDefaultController : public ReadableStreamController { - private: - /** - * Memory layout for ReadableStreamDefaultControllers, starting after the - * slots shared among all types of controllers. - * - * StrategySize is treated as an opaque value when stored. The only use site - * ensures that it's wrapped into the current cx compartment. - */ - enum Slots { - Slot_StrategySize = ReadableStreamController::SlotCount, - SlotCount - }; - - public: - JS::Value strategySize() const { return getFixedSlot(Slot_StrategySize); } - void setStrategySize(const JS::Value& size) { - setFixedSlot(Slot_StrategySize, size); - } - - static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); - static const ClassSpec classSpec_; - static const JSClass class_; - static const ClassSpec protoClassSpec_; - static const JSClass protoClass_; -}; - -class ReadableByteStreamController : public ReadableStreamController { - public: - /** - * Memory layout for ReadableByteStreamControllers, starting after the - * slots shared among all types of controllers. - * - * PendingPullIntos is guaranteed to be in the same compartment as the - * controller, but might contain wrappers for objects from other - * compartments. - * - * AutoAllocateSize is a primitive (numeric) value. - */ - enum Slots { - Slot_BYOBRequest = ReadableStreamController::SlotCount, - Slot_PendingPullIntos, - Slot_AutoAllocateSize, - SlotCount - }; - - JS::Value byobRequest() const { return getFixedSlot(Slot_BYOBRequest); } - void clearBYOBRequest() { - setFixedSlot(Slot_BYOBRequest, JS::UndefinedValue()); - } - ListObject* pendingPullIntos() const { - return &getFixedSlot(Slot_PendingPullIntos).toObject().as(); - } - JS::Value autoAllocateChunkSize() const { - return getFixedSlot(Slot_AutoAllocateSize); - } - void setAutoAllocateChunkSize(const JS::Value& size) { - setFixedSlot(Slot_AutoAllocateSize, size); - } - - static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); - static const ClassSpec classSpec_; - static const JSClass class_; - static const ClassSpec protoClassSpec_; - static const JSClass protoClass_; -}; - -[[nodiscard]] extern bool CheckReadableStreamControllerCanCloseOrEnqueue( - JSContext* cx, JS::Handle unwrappedController, - const char* action); - -[[nodiscard]] extern JSObject* ReadableStreamControllerCancelSteps( - JSContext* cx, JS::Handle unwrappedController, - JS::Handle reason); - -extern PromiseObject* ReadableStreamDefaultControllerPullSteps( - JSContext* cx, - JS::Handle unwrappedController); - -extern bool ReadableStreamControllerStartHandler(JSContext* cx, unsigned argc, - JS::Value* vp); - -extern bool ReadableStreamControllerStartFailedHandler(JSContext* cx, - unsigned argc, - JS::Value* vp); - -} // namespace js - -template <> -inline bool JSObject::is() const { - return is() || - is(); -} - -namespace js { - -inline ReadableStreamController* ReadableStream::controller() const { - return &getFixedSlot(Slot_Controller) - .toObject() - .as(); -} - -inline void ReadableStream::setController( - ReadableStreamController* controller) { - setFixedSlot(Slot_Controller, JS::ObjectValue(*controller)); -} - -} // namespace js - -#endif // builtin_streams_ReadableStreamController_h diff --git a/js/src/builtin/streams/ReadableStreamDefaultController.cpp b/js/src/builtin/streams/ReadableStreamDefaultController.cpp deleted file mode 100644 index 7589373e2d68..000000000000 --- a/js/src/builtin/streams/ReadableStreamDefaultController.cpp +++ /dev/null @@ -1,513 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Class ReadableStreamDefaultController. */ - -#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} - -#include "jsfriendapi.h" // js::AssertSameCompartment - -#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC -#include "builtin/streams/MiscellaneousOperations.h" // js::IsMaybeWrapped -#include "builtin/streams/PullIntoDescriptor.h" // js::PullIntoDescriptor -#include "builtin/streams/QueueWithSizes.h" // js::{DequeueValue,ResetQueue} -#include "builtin/streams/ReadableStream.h" // js::ReadableStream, js::SetUpExternalReadableByteStreamController -#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController, js::CheckReadableStreamControllerCanCloseOrEnqueue, js::ReadableStreamControllerCancelSteps, js::ReadableStreamDefaultControllerPullSteps, js::ReadableStreamControllerStart{,Failed}Handler -#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamController{CallPullIfNeeded,ClearAlgorithms,Error,GetDesiredSizeUnchecked}, js::ReadableStreamDefaultController{Close,Enqueue} -#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{AddReadOrReadIntoRequest,CloseInternal,CreateReadResult} -#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee_Cancel -#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader -#include "builtin/streams/StreamController.h" // js::StreamController -#include "builtin/streams/TeeState.h" // js::TeeState -#include "js/ArrayBuffer.h" // JS::NewArrayBuffer -#include "js/Class.h" // js::ClassSpec -#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/PropertySpec.h" -#include "vm/Interpreter.h" -#include "vm/JSContext.h" -#include "vm/PlainObject.h" // js::PlainObject -#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined -#include "vm/SelfHosting.h" - -#include "builtin/HandlerFunction-inl.h" // js::TargetFromHandler -#include "builtin/streams/MiscellaneousOperations-inl.h" // js::PromiseCall -#include "builtin/streams/ReadableStreamReader-inl.h" // js::UnwrapReaderFromStream -#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapAnd{DowncastObject,TypeCheckThis} -#include "vm/JSContext-inl.h" // JSContext::check -#include "vm/Realm-inl.h" // js::AutoRealm - -using js::ClassSpec; -using js::PromiseObject; -using js::ReadableStream; -using js::ReadableStreamController; -using js::ReadableStreamControllerCallPullIfNeeded; -using js::ReadableStreamControllerClearAlgorithms; -using js::ReadableStreamControllerError; -using js::ReadableStreamControllerGetDesiredSizeUnchecked; -using js::ReadableStreamDefaultController; -using js::ReadableStreamDefaultControllerClose; -using js::ReadableStreamDefaultControllerEnqueue; -using js::TargetFromHandler; -using js::UnwrapAndTypeCheckThis; - -using JS::CallArgs; -using JS::CallArgsFromVp; -using JS::Handle; -using JS::ObjectValue; -using JS::Rooted; -using JS::Value; - -/*** 3.9. Class ReadableStreamDefaultController *****************************/ - -/** - * Streams spec, 3.10.11. SetUpReadableStreamDefaultController, step 11 - * and - * Streams spec, 3.13.26. SetUpReadableByteStreamController, step 16: - * Upon fulfillment of startPromise, [...] - */ -bool js::ReadableStreamControllerStartHandler(JSContext* cx, unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - Rooted controller( - cx, TargetFromHandler(args)); - - // Step a: Set controller.[[started]] to true. - controller->setStarted(); - - // Step b: Assert: controller.[[pulling]] is false. - MOZ_ASSERT(!controller->pulling()); - - // Step c: Assert: controller.[[pullAgain]] is false. - MOZ_ASSERT(!controller->pullAgain()); - - // Step d: Perform - // ! ReadableStreamDefaultControllerCallPullIfNeeded(controller) - // (or ReadableByteStreamControllerCallPullIfNeeded(controller)). - if (!ReadableStreamControllerCallPullIfNeeded(cx, controller)) { - return false; - } - args.rval().setUndefined(); - return true; -} - -/** - * Streams spec, 3.10.11. SetUpReadableStreamDefaultController, step 12 - * and - * Streams spec, 3.13.26. SetUpReadableByteStreamController, step 17: - * Upon rejection of startPromise with reason r, [...] - */ -bool js::ReadableStreamControllerStartFailedHandler(JSContext* cx, - unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - Rooted controller( - cx, TargetFromHandler(args)); - - // Step a: Perform - // ! ReadableStreamDefaultControllerError(controller, r) - // (or ReadableByteStreamControllerError(controller, r)). - if (!ReadableStreamControllerError(cx, controller, args.get(0))) { - return false; - } - - args.rval().setUndefined(); - return true; -} - -/** - * Streams spec, 3.9.3. - * new ReadableStreamDefaultController( stream, underlyingSource, size, - * highWaterMark ) - */ -bool ReadableStreamDefaultController::constructor(JSContext* cx, unsigned argc, - Value* vp) { - // Step 1: Throw a TypeError. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_BOGUS_CONSTRUCTOR, - "ReadableStreamDefaultController"); - return false; -} - -/** - * Streams spec, 3.9.4.1. get desiredSize - */ -static bool ReadableStreamDefaultController_desiredSize(JSContext* cx, - unsigned argc, - Value* vp) { - // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a - // TypeError exception. - CallArgs args = CallArgsFromVp(argc, vp); - Rooted unwrappedController( - cx, UnwrapAndTypeCheckThis( - cx, args, "get desiredSize")); - if (!unwrappedController) { - return false; - } - - // 3.10.8. ReadableStreamDefaultControllerGetDesiredSize, steps 1-4. - // 3.10.8. Step 1: Let stream be controller.[[controlledReadableStream]]. - ReadableStream* unwrappedStream = unwrappedController->stream(); - - // 3.10.8. Step 2: Let state be stream.[[state]]. - // 3.10.8. Step 3: If state is "errored", return null. - if (unwrappedStream->errored()) { - args.rval().setNull(); - return true; - } - - // 3.10.8. Step 4: If state is "closed", return 0. - if (unwrappedStream->closed()) { - args.rval().setInt32(0); - return true; - } - - // Step 2: Return ! ReadableStreamDefaultControllerGetDesiredSize(this). - args.rval().setNumber( - ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController)); - return true; -} - -/** - * Unified implementation of step 2 of 3.9.4.2 and 3.9.4.3, - * and steps 2-3 of 3.11.4.3. - */ -[[nodiscard]] bool js::CheckReadableStreamControllerCanCloseOrEnqueue( - JSContext* cx, Handle unwrappedController, - const char* action) { - // 3.9.4.2. close(), step 2, and - // 3.9.4.3. enqueue(chunk), step 2: - // If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(this) is false, - // throw a TypeError exception. - // RSDCCanCloseOrEnqueue returns false in two cases: (1) - // controller.[[closeRequested]] is true; (2) the stream is not readable, - // i.e. already closed or errored. This amounts to exactly the same thing as - // 3.11.4.3 steps 2-3 below, and we want different error messages for the two - // cases anyway. - - // 3.11.4.3. Step 2: If this.[[closeRequested]] is true, throw a TypeError - // exception. - if (unwrappedController->closeRequested()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_CLOSED, action); - return false; - } - - // 3.11.4.3. Step 3: If this.[[controlledReadableByteStream]].[[state]] is - // not "readable", throw a TypeError exception. - ReadableStream* unwrappedStream = unwrappedController->stream(); - if (!unwrappedStream->readable()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, - action); - return false; - } - - return true; -} - -/** - * Streams spec, 3.9.4.2 close() - */ -static bool ReadableStreamDefaultController_close(JSContext* cx, unsigned argc, - Value* vp) { - // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a - // TypeError exception. - CallArgs args = CallArgsFromVp(argc, vp); - Rooted unwrappedController( - cx, UnwrapAndTypeCheckThis(cx, args, - "close")); - if (!unwrappedController) { - return false; - } - - // Step 2: If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(this) is - // false, throw a TypeError exception. - if (!CheckReadableStreamControllerCanCloseOrEnqueue(cx, unwrappedController, - "close")) { - return false; - } - - // Step 3: Perform ! ReadableStreamDefaultControllerClose(this). - if (!ReadableStreamDefaultControllerClose(cx, unwrappedController)) { - return false; - } - - args.rval().setUndefined(); - return true; -} - -/** - * Streams spec, 3.9.4.3. enqueue ( chunk ) - */ -static bool ReadableStreamDefaultController_enqueue(JSContext* cx, - unsigned argc, Value* vp) { - // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a - // TypeError exception. - CallArgs args = CallArgsFromVp(argc, vp); - Rooted unwrappedController( - cx, UnwrapAndTypeCheckThis(cx, args, - "enqueue")); - if (!unwrappedController) { - return false; - } - - // Step 2: If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(this) is - // false, throw a TypeError exception. - if (!CheckReadableStreamControllerCanCloseOrEnqueue(cx, unwrappedController, - "enqueue")) { - return false; - } - - // Step 3: Return ! ReadableStreamDefaultControllerEnqueue(this, chunk). - if (!ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, - args.get(0))) { - return false; - } - args.rval().setUndefined(); - return true; -} - -/** - * Streams spec, 3.9.4.4. error ( e ) - */ -static bool ReadableStreamDefaultController_error(JSContext* cx, unsigned argc, - Value* vp) { - // Step 1: If ! IsReadableStreamDefaultController(this) is false, throw a - // TypeError exception. - CallArgs args = CallArgsFromVp(argc, vp); - Rooted unwrappedController( - cx, UnwrapAndTypeCheckThis(cx, args, - "enqueue")); - if (!unwrappedController) { - return false; - } - - // Step 2: Perform ! ReadableStreamDefaultControllerError(this, e). - if (!ReadableStreamControllerError(cx, unwrappedController, args.get(0))) { - return false; - } - - args.rval().setUndefined(); - return true; -} - -static const JSPropertySpec ReadableStreamDefaultController_properties[] = { - JS_PSG("desiredSize", ReadableStreamDefaultController_desiredSize, 0), - JS_PS_END}; - -static const JSFunctionSpec ReadableStreamDefaultController_methods[] = { - JS_FN("close", ReadableStreamDefaultController_close, 0, 0), - JS_FN("enqueue", ReadableStreamDefaultController_enqueue, 1, 0), - JS_FN("error", ReadableStreamDefaultController_error, 1, 0), JS_FS_END}; - -JS_STREAMS_CLASS_SPEC(ReadableStreamDefaultController, 0, SlotCount, - ClassSpec::DontDefineConstructor, 0, JS_NULL_CLASS_OPS); - -/** - * Unified implementation of ReadableStream controllers' [[CancelSteps]] - * internal methods. - * Streams spec, 3.9.5.1. [[CancelSteps]] ( reason ) - * and - * Streams spec, 3.11.5.1. [[CancelSteps]] ( reason ) - */ -[[nodiscard]] JSObject* js::ReadableStreamControllerCancelSteps( - JSContext* cx, Handle unwrappedController, - Handle reason) { - AssertSameCompartment(cx, reason); - - // Step 1 of 3.11.5.1: If this.[[pendingPullIntos]] is not empty, - if (!unwrappedController->is()) { - Rooted unwrappedPendingPullIntos( - cx, unwrappedController->as() - .pendingPullIntos()); - - if (unwrappedPendingPullIntos->length() != 0) { - // Step a: Let firstDescriptor be the first element of - // this.[[pendingPullIntos]]. - PullIntoDescriptor* unwrappedDescriptor = - UnwrapAndDowncastObject( - cx, &unwrappedPendingPullIntos->get(0).toObject()); - if (!unwrappedDescriptor) { - return nullptr; - } - - // Step b: Set firstDescriptor.[[bytesFilled]] to 0. - unwrappedDescriptor->setBytesFilled(0); - } - } - - Rooted unwrappedUnderlyingSource( - cx, unwrappedController->underlyingSource()); - - // Step 1 of 3.9.5.1, step 2 of 3.11.5.1: Perform ! ResetQueue(this). - if (!ResetQueue(cx, unwrappedController)) { - return nullptr; - } - - // Step 2 of 3.9.5.1, step 3 of 3.11.5.1: Let result be the result of - // performing this.[[cancelAlgorithm]], passing reason. - // - // Our representation of cancel algorithms is a bit awkward, for - // performance, so we must figure out which algorithm is being invoked. - Rooted result(cx); - if (IsMaybeWrapped(unwrappedUnderlyingSource)) { - // The cancel algorithm given in ReadableStreamTee step 13 or 14. - MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is(), - "tee streams and controllers are always same-compartment with " - "the TeeState object"); - Rooted unwrappedTeeState( - cx, &unwrappedUnderlyingSource.toObject().as()); - Rooted unwrappedDefaultController( - cx, &unwrappedController->as()); - result = ReadableStreamTee_Cancel(cx, unwrappedTeeState, - unwrappedDefaultController, reason); - } else if (unwrappedController->hasExternalSource()) { - // An embedding-provided cancel algorithm. - Rooted rval(cx); - { - AutoRealm ar(cx, unwrappedController); - JS::ReadableStreamUnderlyingSource* source = - unwrappedController->externalSource(); - Rooted stream(cx, unwrappedController->stream()); - Rooted wrappedReason(cx, reason); - if (!cx->compartment()->wrap(cx, &wrappedReason)) { - return nullptr; - } - - cx->check(stream, wrappedReason); - rval = source->cancel(cx, stream, wrappedReason); - } - - // Make sure the ReadableStreamControllerClearAlgorithms call below is - // reached, even on error. - if (!cx->compartment()->wrap(cx, &rval)) { - result = nullptr; - } else { - result = PromiseObject::unforgeableResolve(cx, rval); - } - } else { - // The algorithm created in - // SetUpReadableByteStreamControllerFromUnderlyingSource step 5. - Rooted unwrappedCancelMethod(cx, - unwrappedController->cancelMethod()); - if (unwrappedCancelMethod.isUndefined()) { - // CreateAlgorithmFromUnderlyingMethod step 7. - result = PromiseResolvedWithUndefined(cx); - } else { - // CreateAlgorithmFromUnderlyingMethod steps 6.c.i-ii. - { - AutoRealm ar(cx, unwrappedController); - - // |unwrappedCancelMethod| and |unwrappedUnderlyingSource| come directly - // from |unwrappedController| slots so must be same-compartment with it. - cx->check(unwrappedCancelMethod); - cx->check(unwrappedUnderlyingSource); - - Rooted wrappedReason(cx, reason); - if (!cx->compartment()->wrap(cx, &wrappedReason)) { - return nullptr; - } - - // If PromiseCall fails, don't bail out until after the - // ReadableStreamControllerClearAlgorithms call below. - result = PromiseCall(cx, unwrappedCancelMethod, - unwrappedUnderlyingSource, wrappedReason); - } - if (!cx->compartment()->wrap(cx, &result)) { - result = nullptr; - } - } - } - - // Step 3 (or 4): Perform - // ! ReadableStreamDefaultControllerClearAlgorithms(this) - // (or ReadableByteStreamControllerClearAlgorithms(this)). - ReadableStreamControllerClearAlgorithms(unwrappedController); - - // Step 4 (or 5): Return result. - return result; -} - -/** - * Streams spec, 3.9.5.2. - * ReadableStreamDefaultController [[PullSteps]]( forAuthorCode ) - */ -PromiseObject* js::ReadableStreamDefaultControllerPullSteps( - JSContext* cx, - Handle unwrappedController) { - // Step 1: Let stream be this.[[controlledReadableStream]]. - Rooted unwrappedStream(cx, unwrappedController->stream()); - - // Step 2: If this.[[queue]] is not empty, - Rooted unwrappedQueue(cx); - Rooted val( - cx, unwrappedController->getFixedSlot(StreamController::Slot_Queue)); - if (val.isObject()) { - unwrappedQueue = &val.toObject().as(); - } - - if (unwrappedQueue && unwrappedQueue->length() != 0) { - // Step a: Let chunk be ! DequeueValue(this). - Rooted chunk(cx); - if (!DequeueValue(cx, unwrappedController, &chunk)) { - return nullptr; - } - - // Step b: If this.[[closeRequested]] is true and this.[[queue]] is empty, - if (unwrappedController->closeRequested() && - unwrappedQueue->length() == 0) { - // Step i: Perform ! ReadableStreamDefaultControllerClearAlgorithms(this). - ReadableStreamControllerClearAlgorithms(unwrappedController); - - // Step ii: Perform ! ReadableStreamClose(stream). - if (!ReadableStreamCloseInternal(cx, unwrappedStream)) { - return nullptr; - } - } - - // Step c: Otherwise, perform - // ! ReadableStreamDefaultControllerCallPullIfNeeded(this). - else { - if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) { - return nullptr; - } - } - - // Step d: Return a promise resolved with - // ! ReadableStreamCreateReadResult(chunk, false, forAuthorCode). - cx->check(chunk); - ReadableStreamReader* unwrappedReader = - UnwrapReaderFromStream(cx, unwrappedStream); - if (!unwrappedReader) { - return nullptr; - } - - PlainObject* readResultObj = ReadableStreamCreateReadResult( - cx, chunk, false, unwrappedReader->forAuthorCode()); - if (!readResultObj) { - return nullptr; - } - - Rooted readResult(cx, ObjectValue(*readResultObj)); - return PromiseObject::unforgeableResolveWithNonPromise(cx, readResult); - } - - // Step 3: Let pendingPromise be - // ! ReadableStreamAddReadRequest(stream, forAuthorCode). - Rooted pendingPromise( - cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream)); - if (!pendingPromise) { - return nullptr; - } - - // Step 4: Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(this). - if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) { - return nullptr; - } - - // Step 5: Return pendingPromise. - return pendingPromise; -} diff --git a/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.cpp b/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.cpp deleted file mode 100644 index cf7debed02f9..000000000000 --- a/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.cpp +++ /dev/null @@ -1,682 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Readable stream default controller abstract operations. */ - -#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} - -#include "jsfriendapi.h" // js::AssertSameCompartment - -#include "builtin/Stream.h" // js::ReadableByteStreamControllerClearPendingPullIntos -#include "builtin/streams/MiscellaneousOperations.h" // js::CreateAlgorithmFromUnderlyingMethod, js::InvokeOrNoop, js::IsMaybeWrapped -#include "builtin/streams/QueueWithSizes.h" // js::EnqueueValueWithSize, js::ResetQueue -#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller, js::ReadableByteStreamController, js::ReadableStreamControllerStart{,Failed}Handler -#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{CloseInternal,ErrorInternal,FulfillReadOrReadIntoRequest,GetNumReadRequests} -#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee_Pull, js::SetUpReadableStreamDefaultController -#include "builtin/streams/TeeState.h" // js::TeeState -#include "js/CallAndConstruct.h" // JS::IsCallable -#include "js/CallArgs.h" // JS::CallArgs{,FromVp} -#include "js/Promise.h" // JS::AddPromiseReactions -#include "js/RootingAPI.h" // JS::Handle, JS::Rooted -#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource -#include "js/Value.h" // JS::{,Int32,Object}Value, JS::UndefinedHandleValue -#include "vm/Compartment.h" // JS::Compartment -#include "vm/Interpreter.h" // js::Call, js::GetAndClearExceptionAndStack -#include "vm/JSContext.h" // JSContext -#include "vm/JSObject.h" // JSObject -#include "vm/List.h" // js::ListObject -#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined -#include "vm/Runtime.h" // JSAtomState -#include "vm/SavedFrame.h" // js::SavedFrame - -#include "builtin/HandlerFunction-inl.h" // js::NewHandler -#include "builtin/streams/MiscellaneousOperations-inl.h" // js::PromiseCall -#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapCalleeSlot -#include "vm/JSContext-inl.h" // JSContext::check -#include "vm/JSObject-inl.h" // js::IsCallable, js::NewBuiltinClassInstance -#include "vm/Realm-inl.h" // js::AutoRealm - -using js::ReadableByteStreamController; -using js::ReadableStream; -using js::ReadableStreamController; -using js::ReadableStreamControllerCallPullIfNeeded; -using js::ReadableStreamControllerError; -using js::ReadableStreamGetNumReadRequests; -using js::UnwrapCalleeSlot; - -using JS::CallArgs; -using JS::CallArgsFromVp; -using JS::Handle; -using JS::Rooted; -using JS::UndefinedHandleValue; -using JS::Value; - -/*** 3.10. Readable stream default controller abstract operations ***********/ - -// Streams spec, 3.10.1. IsReadableStreamDefaultController ( x ) -// Implemented via is() - -/** - * Streams spec, 3.10.2 and 3.13.3. step 7: - * Upon fulfillment of pullPromise, [...] - */ -static bool ControllerPullHandler(JSContext* cx, unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - Rooted unwrappedController( - cx, UnwrapCalleeSlot(cx, args, 0)); - if (!unwrappedController) { - return false; - } - - bool pullAgain = unwrappedController->pullAgain(); - - // Step a: Set controller.[[pulling]] to false. - // Step b.i: Set controller.[[pullAgain]] to false. - unwrappedController->clearPullFlags(); - - // Step b: If controller.[[pullAgain]] is true, - if (pullAgain) { - // Step ii: Perform - // ! ReadableStreamDefaultControllerCallPullIfNeeded(controller) - // (or ReadableByteStreamControllerCallPullIfNeeded(controller)). - if (!ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController)) { - return false; - } - } - - args.rval().setUndefined(); - return true; -} - -/** - * Streams spec, 3.10.2 and 3.13.3. step 8: - * Upon rejection of pullPromise with reason e, - */ -static bool ControllerPullFailedHandler(JSContext* cx, unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - Handle e = args.get(0); - - Rooted controller( - cx, UnwrapCalleeSlot(cx, args, 0)); - if (!controller) { - return false; - } - - // Step a: Perform ! ReadableStreamDefaultControllerError(controller, e). - // (ReadableByteStreamControllerError in 3.12.3.) - if (!ReadableStreamControllerError(cx, controller, e)) { - return false; - } - - args.rval().setUndefined(); - return true; -} - -static bool ReadableStreamControllerShouldCallPull( - ReadableStreamController* unwrappedController); - -/** - * Streams spec, 3.10.2 - * ReadableStreamDefaultControllerCallPullIfNeeded ( controller ) - * Streams spec, 3.13.3. - * ReadableByteStreamControllerCallPullIfNeeded ( controller ) - */ -[[nodiscard]] bool js::ReadableStreamControllerCallPullIfNeeded( - JSContext* cx, Handle unwrappedController) { - // Step 1: Let shouldPull be - // ! ReadableStreamDefaultControllerShouldCallPull(controller). - // (ReadableByteStreamDefaultControllerShouldCallPull in 3.13.3.) - bool shouldPull = ReadableStreamControllerShouldCallPull(unwrappedController); - - // Step 2: If shouldPull is false, return. - if (!shouldPull) { - return true; - } - - // Step 3: If controller.[[pulling]] is true, - if (unwrappedController->pulling()) { - // Step a: Set controller.[[pullAgain]] to true. - unwrappedController->setPullAgain(); - - // Step b: Return. - return true; - } - - // Step 4: Assert: controller.[[pullAgain]] is false. - MOZ_ASSERT(!unwrappedController->pullAgain()); - - // Step 5: Set controller.[[pulling]] to true. - unwrappedController->setPulling(); - - // We use this variable in step 7. For ease of error-handling, we wrap it - // early. - Rooted wrappedController(cx, unwrappedController); - if (!cx->compartment()->wrap(cx, &wrappedController)) { - return false; - } - - // Step 6: Let pullPromise be the result of performing - // controller.[[pullAlgorithm]]. - // Our representation of pull algorithms is a bit awkward, for performance, - // so we must figure out which algorithm is being invoked. - Rooted pullPromise(cx); - Rooted unwrappedUnderlyingSource( - cx, unwrappedController->underlyingSource()); - - if (IsMaybeWrapped(unwrappedUnderlyingSource)) { - // The pull algorithm given in ReadableStreamTee step 12. - MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is(), - "tee streams and controllers are always same-compartment with " - "the TeeState object"); - Rooted unwrappedTeeState( - cx, &unwrappedUnderlyingSource.toObject().as()); - pullPromise = ReadableStreamTee_Pull(cx, unwrappedTeeState); - } else if (unwrappedController->hasExternalSource()) { - // An embedding-provided pull algorithm. - { - AutoRealm ar(cx, unwrappedController); - JS::ReadableStreamUnderlyingSource* source = - unwrappedController->externalSource(); - Rooted stream(cx, unwrappedController->stream()); - double desiredSize = - ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController); - source->requestData(cx, stream, desiredSize); - } - pullPromise = PromiseResolvedWithUndefined(cx); - } else { - // The pull algorithm created in - // SetUpReadableStreamDefaultControllerFromUnderlyingSource step 4. - Rooted unwrappedPullMethod(cx, unwrappedController->pullMethod()); - if (unwrappedPullMethod.isUndefined()) { - // CreateAlgorithmFromUnderlyingMethod step 7. - pullPromise = PromiseResolvedWithUndefined(cx); - } else { - // CreateAlgorithmFromUnderlyingMethod step 6.b.i. - { - AutoRealm ar(cx, unwrappedController); - - // |unwrappedPullMethod| and |unwrappedUnderlyingSource| come directly - // from |unwrappedController| slots so must be same-compartment with it. - cx->check(unwrappedPullMethod); - cx->check(unwrappedUnderlyingSource); - - Rooted controller(cx, ObjectValue(*unwrappedController)); - cx->check(controller); - - pullPromise = PromiseCall(cx, unwrappedPullMethod, - unwrappedUnderlyingSource, controller); - if (!pullPromise) { - return false; - } - } - if (!cx->compartment()->wrap(cx, &pullPromise)) { - return false; - } - } - } - if (!pullPromise) { - return false; - } - - // Step 7: Upon fulfillment of pullPromise, [...] - // Step 8. Upon rejection of pullPromise with reason e, [...] - Rooted onPullFulfilled( - cx, NewHandler(cx, ControllerPullHandler, wrappedController)); - if (!onPullFulfilled) { - return false; - } - Rooted onPullRejected( - cx, NewHandler(cx, ControllerPullFailedHandler, wrappedController)); - if (!onPullRejected) { - return false; - } - return JS::AddPromiseReactions(cx, pullPromise, onPullFulfilled, - onPullRejected); -} - -/** - * Streams spec, 3.10.3. - * ReadableStreamDefaultControllerShouldCallPull ( controller ) - * Streams spec, 3.13.25. - * ReadableByteStreamControllerShouldCallPull ( controller ) - */ -static bool ReadableStreamControllerShouldCallPull( - ReadableStreamController* unwrappedController) { - // Step 1: Let stream be controller.[[controlledReadableStream]] - // (or [[controlledReadableByteStream]]). - ReadableStream* unwrappedStream = unwrappedController->stream(); - - // 3.10.3. Step 2: - // If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) - // is false, return false. - // This turns out to be the same as 3.13.25 steps 2-3. - - // 3.13.25 Step 2: If stream.[[state]] is not "readable", return false. - if (!unwrappedStream->readable()) { - return false; - } - - // 3.13.25 Step 3: If controller.[[closeRequested]] is true, return false. - if (unwrappedController->closeRequested()) { - return false; - } - - // Step 3 (or 4): - // If controller.[[started]] is false, return false. - if (!unwrappedController->started()) { - return false; - } - - // 3.10.3. - // Step 4: If ! IsReadableStreamLocked(stream) is true and - // ! ReadableStreamGetNumReadRequests(stream) > 0, return true. - // - // 3.13.25. - // Step 5: If ! ReadableStreamHasDefaultReader(stream) is true and - // ! ReadableStreamGetNumReadRequests(stream) > 0, return true. - // Step 6: If ! ReadableStreamHasBYOBReader(stream) is true and - // ! ReadableStreamGetNumReadIntoRequests(stream) > 0, return true. - // - // All of these amount to the same thing in this implementation: - if (unwrappedStream->locked() && - ReadableStreamGetNumReadRequests(unwrappedStream) > 0) { - return true; - } - - // Step 5 (or 7): - // Let desiredSize be - // ! ReadableStreamDefaultControllerGetDesiredSize(controller). - // (ReadableByteStreamControllerGetDesiredSize in 3.13.25.) - double desiredSize = - ReadableStreamControllerGetDesiredSizeUnchecked(unwrappedController); - - // Step 6 (or 8): Assert: desiredSize is not null (implicit). - // Step 7 (or 9): If desiredSize > 0, return true. - // Step 8 (or 10): Return false. - return desiredSize > 0; -} - -/** - * Streams spec, 3.10.4. - * ReadableStreamDefaultControllerClearAlgorithms ( controller ) - * and 3.13.4. - * ReadableByteStreamControllerClearAlgorithms ( controller ) - */ -void js::ReadableStreamControllerClearAlgorithms( - Handle controller) { - // Step 1: Set controller.[[pullAlgorithm]] to undefined. - // Step 2: Set controller.[[cancelAlgorithm]] to undefined. - // (In this implementation, the UnderlyingSource slot is part of the - // representation of these algorithms.) - controller->setPullMethod(UndefinedHandleValue); - controller->setCancelMethod(UndefinedHandleValue); - ReadableStreamController::clearUnderlyingSource(controller); - - // Step 3 (of 3.10.4 only) : Set controller.[[strategySizeAlgorithm]] to - // undefined. - if (controller->is()) { - controller->as().setStrategySize( - UndefinedHandleValue); - } -} - -/** - * Streams spec, 3.10.5. ReadableStreamDefaultControllerClose ( controller ) - */ -[[nodiscard]] bool js::ReadableStreamDefaultControllerClose( - JSContext* cx, - Handle unwrappedController) { - // Step 1: Let stream be controller.[[controlledReadableStream]]. - Rooted unwrappedStream(cx, unwrappedController->stream()); - - // Step 2: Assert: - // ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) - // is true. - MOZ_ASSERT(!unwrappedController->closeRequested()); - MOZ_ASSERT(unwrappedStream->readable()); - - // Step 3: Set controller.[[closeRequested]] to true. - unwrappedController->setCloseRequested(); - - // Step 4: If controller.[[queue]] is empty, - Rooted unwrappedQueue(cx, unwrappedController->queue()); - if (unwrappedQueue->length() == 0) { - // Step a: Perform - // ! ReadableStreamDefaultControllerClearAlgorithms(controller). - ReadableStreamControllerClearAlgorithms(unwrappedController); - - // Step b: Perform ! ReadableStreamClose(stream). - return ReadableStreamCloseInternal(cx, unwrappedStream); - } - - return true; -} - -/** - * Streams spec, 3.10.6. - * ReadableStreamDefaultControllerEnqueue ( controller, chunk ) - */ -[[nodiscard]] bool js::ReadableStreamDefaultControllerEnqueue( - JSContext* cx, Handle unwrappedController, - Handle chunk) { - AssertSameCompartment(cx, chunk); - - // Step 1: Let stream be controller.[[controlledReadableStream]]. - Rooted unwrappedStream(cx, unwrappedController->stream()); - - // Step 2: Assert: - // ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is - // true. - MOZ_ASSERT(!unwrappedController->closeRequested()); - MOZ_ASSERT(unwrappedStream->readable()); - - // Step 3: If ! IsReadableStreamLocked(stream) is true and - // ! ReadableStreamGetNumReadRequests(stream) > 0, perform - // ! ReadableStreamFulfillReadRequest(stream, chunk, false). - if (unwrappedStream->locked() && - ReadableStreamGetNumReadRequests(unwrappedStream) > 0) { - if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk, - false)) { - return false; - } - } else { - // Step 4: Otherwise, - // Step a: Let result be the result of performing - // controller.[[strategySizeAlgorithm]], passing in chunk, and - // interpreting the result as an ECMAScript completion value. - // Step c: (on success) Let chunkSize be result.[[Value]]. - Rooted chunkSize(cx, Int32Value(1)); - bool success = true; - Rooted strategySize(cx, unwrappedController->strategySize()); - if (!strategySize.isUndefined()) { - if (!cx->compartment()->wrap(cx, &strategySize)) { - return false; - } - success = Call(cx, strategySize, UndefinedHandleValue, chunk, &chunkSize); - } - - // Step d: Let enqueueResult be - // EnqueueValueWithSize(controller, chunk, chunkSize). - if (success) { - success = EnqueueValueWithSize(cx, unwrappedController, chunk, chunkSize); - } - - // Step b: If result is an abrupt completion, - // and - // Step e: If enqueueResult is an abrupt completion, - if (!success) { - Rooted exn(cx); - Rooted stack(cx); - if (!cx->isExceptionPending() || - !GetAndClearExceptionAndStack(cx, &exn, &stack)) { - // Uncatchable error. Die immediately without erroring the - // stream. - return false; - } - - // Step b.i: Perform ! ReadableStreamDefaultControllerError( - // controller, result.[[Value]]). - // Step e.i: Perform ! ReadableStreamDefaultControllerError( - // controller, enqueueResult.[[Value]]). - if (!ReadableStreamControllerError(cx, unwrappedController, exn)) { - return false; - } - - // Step b.ii: Return result. - // Step e.ii: Return enqueueResult. - // (I.e., propagate the exception.) - cx->setPendingException(exn, stack); - return false; - } - } - - // Step 5: Perform - // ! ReadableStreamDefaultControllerCallPullIfNeeded(controller). - return ReadableStreamControllerCallPullIfNeeded(cx, unwrappedController); -} - -/** - * Streams spec, 3.10.7. ReadableStreamDefaultControllerError ( controller, e ) - * Streams spec, 3.13.11. ReadableByteStreamControllerError ( controller, e ) - */ -[[nodiscard]] bool js::ReadableStreamControllerError( - JSContext* cx, Handle unwrappedController, - Handle e) { - MOZ_ASSERT(!cx->isExceptionPending()); - AssertSameCompartment(cx, e); - - // Step 1: Let stream be controller.[[controlledReadableStream]] - // (or controller.[[controlledReadableByteStream]]). - Rooted unwrappedStream(cx, unwrappedController->stream()); - - // Step 2: If stream.[[state]] is not "readable", return. - if (!unwrappedStream->readable()) { - return true; - } - - // Step 3 of 3.13.10: - // Perform ! ReadableByteStreamControllerClearPendingPullIntos(controller). - if (unwrappedController->is()) { - Rooted unwrappedByteStreamController( - cx, &unwrappedController->as()); - if (!ReadableByteStreamControllerClearPendingPullIntos( - cx, unwrappedByteStreamController)) { - return false; - } - } - - // Step 3 (or 4): Perform ! ResetQueue(controller). - if (!ResetQueue(cx, unwrappedController)) { - return false; - } - - // Step 4 (or 5): - // Perform ! ReadableStreamDefaultControllerClearAlgorithms(controller) - // (or ReadableByteStreamControllerClearAlgorithms(controller)). - ReadableStreamControllerClearAlgorithms(unwrappedController); - - // Step 5 (or 6): Perform ! ReadableStreamError(stream, e). - return ReadableStreamErrorInternal(cx, unwrappedStream, e); -} - -/** - * Streams spec, 3.10.8. - * ReadableStreamDefaultControllerGetDesiredSize ( controller ) - * Streams spec 3.13.14. - * ReadableByteStreamControllerGetDesiredSize ( controller ) - */ -[[nodiscard]] double js::ReadableStreamControllerGetDesiredSizeUnchecked( - ReadableStreamController* controller) { - // Steps 1-4 done at callsites, so only assert that they have been done. -#if DEBUG - ReadableStream* stream = controller->stream(); - MOZ_ASSERT(!(stream->errored() || stream->closed())); -#endif // DEBUG - - // Step 5: Return controller.[[strategyHWM]] − controller.[[queueTotalSize]]. - return controller->strategyHWM() - controller->queueTotalSize(); -} - -/** - * Streams spec, 3.10.11. - * SetUpReadableStreamDefaultController(stream, controller, - * startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, - * sizeAlgorithm ) - * - * The standard algorithm takes a `controller` argument which must be a new, - * blank object. This implementation creates a new controller instead. - * - * In the spec, three algorithms (startAlgorithm, pullAlgorithm, - * cancelAlgorithm) are passed as arguments to this routine. This - * implementation passes these "algorithms" as data, using four arguments: - * sourceAlgorithms, underlyingSource, pullMethod, and cancelMethod. The - * sourceAlgorithms argument tells how to interpret the other three: - * - * - SourceAlgorithms::Script - We're creating a stream from a JS source. - * The caller is `new ReadableStream(underlyingSource)` or - * `JS::NewReadableDefaultStreamObject`. `underlyingSource` is the - * source; `pullMethod` and `cancelMethod` are its .pull and - * .cancel methods, which the caller has already extracted and - * type-checked: each one must be either a callable JS object or undefined. - * - * Script streams use the start/pull/cancel algorithms defined in - * 3.10.12. SetUpReadableStreamDefaultControllerFromUnderlyingSource, which - * call JS methods of the underlyingSource. - * - * - SourceAlgorithms::Tee - We're creating a tee stream. `underlyingSource` - * is a TeeState object. `pullMethod` and `cancelMethod` are undefined. - * - * Tee streams use the start/pull/cancel algorithms given in - * 3.4.10. ReadableStreamTee. - * - * Note: All arguments must be same-compartment with cx. ReadableStream - * controllers are always created in the same compartment as the stream. - */ -[[nodiscard]] bool js::SetUpReadableStreamDefaultController( - JSContext* cx, Handle stream, - SourceAlgorithms sourceAlgorithms, Handle underlyingSource, - Handle pullMethod, Handle cancelMethod, double highWaterMark, - Handle size) { - cx->check(stream, underlyingSource, size); - MOZ_ASSERT(pullMethod.isUndefined() || IsCallable(pullMethod)); - MOZ_ASSERT(cancelMethod.isUndefined() || IsCallable(cancelMethod)); - MOZ_ASSERT_IF(sourceAlgorithms != SourceAlgorithms::Script, - pullMethod.isUndefined()); - MOZ_ASSERT_IF(sourceAlgorithms != SourceAlgorithms::Script, - cancelMethod.isUndefined()); - MOZ_ASSERT(highWaterMark >= 0); - MOZ_ASSERT(size.isUndefined() || IsCallable(size)); - - // Done elsewhere in the standard: Create the new controller. - Rooted controller( - cx, NewBuiltinClassInstance(cx)); - if (!controller) { - return false; - } - - // Step 1: Assert: stream.[[readableStreamController]] is undefined. - MOZ_ASSERT(!stream->hasController()); - - // Step 2: Set controller.[[controlledReadableStream]] to stream. - controller->setStream(stream); - - // Step 3: Set controller.[[queue]] and controller.[[queueTotalSize]] to - // undefined (implicit), then perform ! ResetQueue(controller). - if (!ResetQueue(cx, controller)) { - return false; - } - - // Step 4: Set controller.[[started]], controller.[[closeRequested]], - // controller.[[pullAgain]], and controller.[[pulling]] to false. - controller->setFlags(0); - - // Step 5: Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm - // and controller.[[strategyHWM]] to highWaterMark. - controller->setStrategySize(size); - controller->setStrategyHWM(highWaterMark); - - // Step 6: Set controller.[[pullAlgorithm]] to pullAlgorithm. - // (In this implementation, the pullAlgorithm is determined by the - // underlyingSource in combination with the pullMethod field.) - controller->setUnderlyingSource(underlyingSource); - controller->setPullMethod(pullMethod); - - // Step 7: Set controller.[[cancelAlgorithm]] to cancelAlgorithm. - controller->setCancelMethod(cancelMethod); - - // Step 8: Set stream.[[readableStreamController]] to controller. - stream->setController(controller); - - // Step 9: Let startResult be the result of performing startAlgorithm. - Rooted startResult(cx); - if (sourceAlgorithms == SourceAlgorithms::Script) { - Rooted controllerVal(cx, ObjectValue(*controller)); - if (!InvokeOrNoop(cx, underlyingSource, cx->names().start, controllerVal, - &startResult)) { - return false; - } - } - - // Step 10: Let startPromise be a promise resolved with startResult. - Rooted startPromise( - cx, PromiseObject::unforgeableResolve(cx, startResult)); - if (!startPromise) { - return false; - } - - // Step 11: Upon fulfillment of startPromise, [...] - // Step 12: Upon rejection of startPromise with reason r, [...] - Rooted onStartFulfilled( - cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller)); - if (!onStartFulfilled) { - return false; - } - Rooted onStartRejected( - cx, - NewHandler(cx, ReadableStreamControllerStartFailedHandler, controller)); - if (!onStartRejected) { - return false; - } - if (!JS::AddPromiseReactions(cx, startPromise, onStartFulfilled, - onStartRejected)) { - return false; - } - - return true; -} - -/** - * Streams spec, 3.10.12. - * SetUpReadableStreamDefaultControllerFromUnderlyingSource( stream, - * underlyingSource, highWaterMark, sizeAlgorithm ) - */ -[[nodiscard]] bool js::SetUpReadableStreamDefaultControllerFromUnderlyingSource( - JSContext* cx, Handle stream, - Handle underlyingSource, double highWaterMark, - Handle sizeAlgorithm) { - // Step 1: Assert: underlyingSource is not undefined. - MOZ_ASSERT(!underlyingSource.isUndefined()); - - // Step 2: Let controller be ObjectCreate(the original value of - // ReadableStreamDefaultController's prototype property). - // (Deferred to SetUpReadableStreamDefaultController.) - - // Step 3: Let startAlgorithm be the following steps: - // a. Return ? InvokeOrNoop(underlyingSource, "start", - // « controller »). - SourceAlgorithms sourceAlgorithms = SourceAlgorithms::Script; - - // Step 4: Let pullAlgorithm be - // ? CreateAlgorithmFromUnderlyingMethod(underlyingSource, "pull", - // 0, « controller »). - Rooted pullMethod(cx); - if (!CreateAlgorithmFromUnderlyingMethod(cx, underlyingSource, - "ReadableStream source.pull method", - cx->names().pull, &pullMethod)) { - return false; - } - - // Step 5. Let cancelAlgorithm be - // ? CreateAlgorithmFromUnderlyingMethod(underlyingSource, - // "cancel", 1, « »). - Rooted cancelMethod(cx); - if (!CreateAlgorithmFromUnderlyingMethod( - cx, underlyingSource, "ReadableStream source.cancel method", - cx->names().cancel, &cancelMethod)) { - return false; - } - - // Step 6. Perform ? SetUpReadableStreamDefaultController(stream, - // controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, - // highWaterMark, sizeAlgorithm). - return SetUpReadableStreamDefaultController( - cx, stream, sourceAlgorithms, underlyingSource, pullMethod, cancelMethod, - highWaterMark, sizeAlgorithm); -} diff --git a/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.h b/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.h deleted file mode 100644 index 905f56bad7ad..000000000000 --- a/js/src/builtin/streams/ReadableStreamDefaultControllerOperations.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Readable stream default controller abstract operations. */ - -#ifndef builtin_streams_ReadableStreamDefaultControllerOperations_h -#define builtin_streams_ReadableStreamDefaultControllerOperations_h - -#include "jstypes.h" // JS_PUBLIC_API -#include "js/RootingAPI.h" // JS::Handle -#include "js/Value.h" // JS::Value - -struct JS_PUBLIC_API JSContext; - -namespace js { - -class ReadableStream; -class ReadableStreamController; -class ReadableStreamDefaultController; - -[[nodiscard]] extern bool ReadableStreamDefaultControllerEnqueue( - JSContext* cx, - JS::Handle unwrappedController, - JS::Handle chunk); - -[[nodiscard]] extern bool ReadableStreamControllerError( - JSContext* cx, JS::Handle unwrappedController, - JS::Handle e); - -[[nodiscard]] extern bool ReadableStreamDefaultControllerClose( - JSContext* cx, - JS::Handle unwrappedController); - -[[nodiscard]] extern double ReadableStreamControllerGetDesiredSizeUnchecked( - ReadableStreamController* controller); - -[[nodiscard]] extern bool ReadableStreamControllerCallPullIfNeeded( - JSContext* cx, JS::Handle unwrappedController); - -extern void ReadableStreamControllerClearAlgorithms( - JS::Handle controller); - -/** - * Characterizes the family of algorithms, (startAlgorithm, pullAlgorithm, - * cancelAlgorithm), associated with a readable stream. - * - * See the comment on SetUpReadableStreamDefaultController(). - */ -enum class SourceAlgorithms { - Script, - Tee, -}; - -[[nodiscard]] extern bool SetUpReadableStreamDefaultController( - JSContext* cx, JS::Handle stream, - SourceAlgorithms sourceAlgorithms, JS::Handle underlyingSource, - JS::Handle pullMethod, JS::Handle cancelMethod, - double highWaterMark, JS::Handle size); - -[[nodiscard]] extern bool -SetUpReadableStreamDefaultControllerFromUnderlyingSource( - JSContext* cx, JS::Handle stream, - JS::Handle underlyingSource, double highWaterMark, - JS::Handle sizeAlgorithm); - -} // namespace js - -#endif // builtin_streams_ReadableStreamDefaultControllerOperations_h diff --git a/js/src/builtin/streams/ReadableStreamDefaultReader.cpp b/js/src/builtin/streams/ReadableStreamDefaultReader.cpp deleted file mode 100644 index c45281b9c46a..000000000000 --- a/js/src/builtin/streams/ReadableStreamDefaultReader.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Class ReadableStreamDefaultReader. */ - -#include "builtin/streams/ClassSpecMacro.h" // JS_STREAMS_CLASS_SPEC -#include "builtin/streams/MiscellaneousOperations.h" // js::ReturnPromiseRejectedWithPendingError -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "builtin/streams/ReadableStreamReader.h" // js::ForAuthorCodeBool, js::ReadableStream{,Default}Reader -#include "js/CallArgs.h" // JS::CallArgs{,FromVp} -#include "js/Class.h" // JSClass, JS_NULL_CLASS_OPS -#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/RootingAPI.h" // JS::Handle, JS::Rooted -#include "vm/PromiseObject.h" // js::PromiseObject - -#include "vm/Compartment-inl.h" // js::UnwrapAndTypeCheckThis -#include "vm/JSObject-inl.h" // js::NewObjectWithClassProto -#include "vm/NativeObject-inl.h" // js::ThrowIfNotConstructing - -using JS::CallArgs; -using JS::CallArgsFromVp; -using JS::Handle; -using JS::Rooted; -using JS::Value; - -using js::ForAuthorCodeBool; -using js::GetErrorMessage; -using js::ListObject; -using js::NewObjectWithClassProto; -using js::PromiseObject; -using js::ReadableStream; -using js::ReadableStreamDefaultReader; -using js::ReadableStreamReader; -using js::UnwrapAndTypeCheckThis; - -/*** 3.6. Class ReadableStreamDefaultReader *********************************/ - -/** - * Stream spec, 3.6.3. new ReadableStreamDefaultReader ( stream ) - * Steps 2-4. - */ -[[nodiscard]] ReadableStreamDefaultReader* -js::CreateReadableStreamDefaultReader(JSContext* cx, - Handle unwrappedStream, - ForAuthorCodeBool forAuthorCode, - Handle proto /* = nullptr */) { - Rooted reader( - cx, NewObjectWithClassProto(cx, proto)); - if (!reader) { - return nullptr; - } - - // Step 2: If ! IsReadableStreamLocked(stream) is true, throw a TypeError - // exception. - if (unwrappedStream->locked()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_LOCKED); - return nullptr; - } - - // Step 3: Perform ! ReadableStreamReaderGenericInitialize(this, stream). - // Step 4: Set this.[[readRequests]] to a new empty List. - if (!ReadableStreamReaderGenericInitialize(cx, reader, unwrappedStream, - forAuthorCode)) { - return nullptr; - } - - return reader; -} - -/** - * Stream spec, 3.6.3. new ReadableStreamDefaultReader ( stream ) - */ -bool ReadableStreamDefaultReader::constructor(JSContext* cx, unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - if (!ThrowIfNotConstructing(cx, args, "ReadableStreamDefaultReader")) { - return false; - } - - // Implicit in the spec: Find the prototype object to use. - Rooted proto(cx); - if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) { - return false; - } - - // Step 1: If ! IsReadableStream(stream) is false, throw a TypeError - // exception. - Rooted unwrappedStream( - cx, UnwrapAndTypeCheckArgument( - cx, args, "ReadableStreamDefaultReader constructor", 0)); - if (!unwrappedStream) { - return false; - } - - Rooted reader( - cx, CreateReadableStreamDefaultReader(cx, unwrappedStream, - ForAuthorCodeBool::Yes, proto)); - if (!reader) { - return false; - } - - args.rval().setObject(*reader); - return true; -} - -/** - * Streams spec, 3.6.4.1 get closed - */ -[[nodiscard]] static bool ReadableStreamDefaultReader_closed(JSContext* cx, - unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1: If ! IsReadableStreamDefaultReader(this) is false, return a promise - // rejected with a TypeError exception. - Rooted unwrappedReader( - cx, UnwrapAndTypeCheckThis(cx, args, - "get closed")); - if (!unwrappedReader) { - return ReturnPromiseRejectedWithPendingError(cx, args); - } - - // Step 2: Return this.[[closedPromise]]. - Rooted closedPromise(cx, unwrappedReader->closedPromise()); - if (!cx->compartment()->wrap(cx, &closedPromise)) { - return false; - } - - args.rval().setObject(*closedPromise); - return true; -} - -/** - * Streams spec, 3.6.4.2. cancel ( reason ) - */ -[[nodiscard]] static bool ReadableStreamDefaultReader_cancel(JSContext* cx, - unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1: If ! IsReadableStreamDefaultReader(this) is false, return a promise - // rejected with a TypeError exception. - Rooted unwrappedReader( - cx, - UnwrapAndTypeCheckThis(cx, args, "cancel")); - if (!unwrappedReader) { - return ReturnPromiseRejectedWithPendingError(cx, args); - } - - // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise - // rejected with a TypeError exception. - if (!unwrappedReader->hasStream()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMREADER_NOT_OWNED, "cancel"); - return ReturnPromiseRejectedWithPendingError(cx, args); - } - - // Step 3: Return ! ReadableStreamReaderGenericCancel(this, reason). - JSObject* cancelPromise = - ReadableStreamReaderGenericCancel(cx, unwrappedReader, args.get(0)); - if (!cancelPromise) { - return false; - } - args.rval().setObject(*cancelPromise); - return true; -} - -/** - * Streams spec, 3.6.4.3 read ( ) - */ -[[nodiscard]] static bool ReadableStreamDefaultReader_read(JSContext* cx, - unsigned argc, - Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - // Step 1: If ! IsReadableStreamDefaultReader(this) is false, return a promise - // rejected with a TypeError exception. - Rooted unwrappedReader( - cx, - UnwrapAndTypeCheckThis(cx, args, "read")); - if (!unwrappedReader) { - return ReturnPromiseRejectedWithPendingError(cx, args); - } - - // Step 2: If this.[[ownerReadableStream]] is undefined, return a promise - // rejected with a TypeError exception. - if (!unwrappedReader->hasStream()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMREADER_NOT_OWNED, "read"); - return ReturnPromiseRejectedWithPendingError(cx, args); - } - - // Step 3: Return ! ReadableStreamDefaultReaderRead(this, true). - PromiseObject* readPromise = - js::ReadableStreamDefaultReaderRead(cx, unwrappedReader); - if (!readPromise) { - return false; - } - args.rval().setObject(*readPromise); - return true; -} - -/** - * Streams spec, 3.6.4.4. releaseLock ( ) - */ -static bool ReadableStreamDefaultReader_releaseLock(JSContext* cx, - unsigned argc, Value* vp) { - // Step 1: If ! IsReadableStreamDefaultReader(this) is false, - // throw a TypeError exception. - CallArgs args = CallArgsFromVp(argc, vp); - Rooted reader( - cx, UnwrapAndTypeCheckThis(cx, args, - "releaseLock")); - if (!reader) { - return false; - } - - // Step 2: If this.[[ownerReadableStream]] is undefined, return. - if (!reader->hasStream()) { - args.rval().setUndefined(); - return true; - } - - // Step 3: If this.[[readRequests]] is not empty, throw a TypeError exception. - Value val = reader->getFixedSlot(ReadableStreamReader::Slot_Requests); - if (!val.isUndefined()) { - ListObject* readRequests = &val.toObject().as(); - if (readRequests->length() != 0) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMREADER_NOT_EMPTY, - "releaseLock"); - return false; - } - } - - // Step 4: Perform ! ReadableStreamReaderGenericRelease(this). - if (!js::ReadableStreamReaderGenericRelease(cx, reader)) { - return false; - } - - args.rval().setUndefined(); - return true; -} - -static const JSFunctionSpec ReadableStreamDefaultReader_methods[] = { - JS_FN("cancel", ReadableStreamDefaultReader_cancel, 1, 0), - JS_FN("read", ReadableStreamDefaultReader_read, 0, 0), - JS_FN("releaseLock", ReadableStreamDefaultReader_releaseLock, 0, 0), - JS_FS_END}; - -static const JSPropertySpec ReadableStreamDefaultReader_properties[] = { - JS_PSG("closed", ReadableStreamDefaultReader_closed, 0), JS_PS_END}; - -const JSClass ReadableStreamReader::class_ = {"ReadableStreamReader"}; - -JS_STREAMS_CLASS_SPEC(ReadableStreamDefaultReader, 1, SlotCount, - js::ClassSpec::DontDefineConstructor, 0, - JS_NULL_CLASS_OPS); diff --git a/js/src/builtin/streams/ReadableStreamInternals.cpp b/js/src/builtin/streams/ReadableStreamInternals.cpp deleted file mode 100644 index 6b94da84160b..000000000000 --- a/js/src/builtin/streams/ReadableStreamInternals.cpp +++ /dev/null @@ -1,473 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* The interface between readable streams and controllers. */ - -#include "builtin/streams/ReadableStreamInternals.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} - -#include // uint32_t - -#include "jsfriendapi.h" // js::AssertSameCompartment - -#include "builtin/streams/ReadableStreamController.h" // js::ReadableStreamController{,CancelSteps} -#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::ForAuthorCodeBool -#include "gc/AllocKind.h" // js::gc::AllocKind -#include "js/CallArgs.h" // JS::CallArgs{,FromVp} -#include "js/GCAPI.h" // JS::AutoSuppressGCAnalysis -#include "js/Promise.h" // JS::CallOriginalPromiseThen, JS::ResolvePromise -#include "js/Result.h" // JS_TRY_VAR_OR_RETURN_NULL -#include "js/RootingAPI.h" // JS::Handle, JS::Rooted -#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource, JS::ReadableStreamMode -#include "js/Value.h" // JS::Value, JS::{Boolean,Object}Value, JS::UndefinedHandleValue -#include "vm/JSContext.h" // JSContext -#include "vm/JSFunction.h" // JSFunction, js::NewNativeFunction -#include "vm/JSObject.h" // js::GenericObject -#include "vm/NativeObject.h" // js::NativeObject, js::PlainObject -#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined -#include "vm/Realm.h" // JS::Realm -#include "vm/StringType.h" // js::PropertyName - -#include "builtin/Promise-inl.h" // js::SetSettledPromiseIsHandled -#include "builtin/streams/MiscellaneousOperations-inl.h" // js::{Reject,Resolve}UnwrappedPromiseWithUndefined -#include "builtin/streams/ReadableStreamReader-inl.h" // js::js::UnwrapReaderFromStream{,NoThrow} -#include "vm/Compartment-inl.h" // JS::Compartment::wrap -#include "vm/JSContext-inl.h" // JSContext::check -#include "vm/List-inl.h" // js::ListObject, js::AppendToListInFixedSlot, js::StoreNewListInFixedSlot -#include "vm/PlainObject-inl.h" // js::PlainObject::createWithTemplate -#include "vm/Realm-inl.h" // JS::Realm - -using JS::BooleanValue; -using JS::CallArgs; -using JS::CallArgsFromVp; -using JS::Handle; -using JS::ObjectValue; -using JS::ResolvePromise; -using JS::Rooted; -using JS::UndefinedHandleValue; -using JS::Value; - -using js::PlainObject; -using js::ReadableStream; - -/*** 3.5. The interface between readable streams and controllers ************/ - -/** - * Streams spec, 3.5.1. - * ReadableStreamAddReadIntoRequest ( stream, forAuthorCode ) - * Streams spec, 3.5.2. - * ReadableStreamAddReadRequest ( stream, forAuthorCode ) - * - * Our implementation does not pass around forAuthorCode parameters in the same - * places as the standard, but the effect is the same. See the comment on - * `ReadableStreamReader::forAuthorCode()`. - */ -[[nodiscard]] js::PromiseObject* js::ReadableStreamAddReadOrReadIntoRequest( - JSContext* cx, Handle unwrappedStream) { - // Step 1: Assert: ! IsReadableStream{BYOB,Default}Reader(stream.[[reader]]) - // is true. - // (Only default readers exist so far.) - Rooted unwrappedReader( - cx, UnwrapReaderFromStream(cx, unwrappedStream)); - if (!unwrappedReader) { - return nullptr; - } - MOZ_ASSERT(unwrappedReader->is()); - - // Step 2 of 3.5.1: Assert: stream.[[state]] is "readable" or "closed". - // Step 2 of 3.5.2: Assert: stream.[[state]] is "readable". - MOZ_ASSERT(unwrappedStream->readable() || unwrappedStream->closed()); - MOZ_ASSERT_IF(unwrappedReader->is(), - unwrappedStream->readable()); - - // Step 3: Let promise be a new promise. - Rooted promise(cx, PromiseObject::createSkippingExecutor(cx)); - if (!promise) { - return nullptr; - } - - // Step 4: Let read{Into}Request be - // Record {[[promise]]: promise, [[forAuthorCode]]: forAuthorCode}. - // Step 5: Append read{Into}Request as the last element of - // stream.[[reader]].[[read{Into}Requests]]. - // Since we don't need the [[forAuthorCode]] field (see the comment on - // `ReadableStreamReader::forAuthorCode()`), we elide the Record and store - // only the promise. - if (!AppendToListInFixedSlot(cx, unwrappedReader, - ReadableStreamReader::Slot_Requests, promise)) { - return nullptr; - } - - // Step 6: Return promise. - return promise; -} - -/** - * Used for transforming the result of promise fulfillment/rejection. - */ -static bool ReturnUndefined(JSContext* cx, unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - args.rval().setUndefined(); - return true; -} - -/** - * Streams spec, 3.5.3. ReadableStreamCancel ( stream, reason ) - */ -[[nodiscard]] JSObject* js::ReadableStreamCancel( - JSContext* cx, Handle unwrappedStream, - Handle reason) { - AssertSameCompartment(cx, reason); - - // Step 1: Set stream.[[disturbed]] to true. - unwrappedStream->setDisturbed(); - - // Step 2: If stream.[[state]] is "closed", return a promise resolved with - // undefined. - if (unwrappedStream->closed()) { - return PromiseResolvedWithUndefined(cx); - } - - // Step 3: If stream.[[state]] is "errored", return a promise rejected with - // stream.[[storedError]]. - if (unwrappedStream->errored()) { - Rooted storedError(cx, unwrappedStream->storedError()); - if (!cx->compartment()->wrap(cx, &storedError)) { - return nullptr; - } - return PromiseObject::unforgeableReject(cx, storedError); - } - - // Step 4: Perform ! ReadableStreamClose(stream). - if (!ReadableStreamCloseInternal(cx, unwrappedStream)) { - return nullptr; - } - - // Step 5: Let sourceCancelPromise be - // ! stream.[[readableStreamController]].[[CancelSteps]](reason). - Rooted unwrappedController( - cx, unwrappedStream->controller()); - Rooted sourceCancelPromise( - cx, ReadableStreamControllerCancelSteps(cx, unwrappedController, reason)); - if (!sourceCancelPromise) { - return nullptr; - } - - // Step 6: Return the result of reacting to sourceCancelPromise with a - // fulfillment step that returns undefined. - Handle funName = cx->names().empty; - Rooted returnUndefined( - cx, NewNativeFunction(cx, ReturnUndefined, 0, funName, - gc::AllocKind::FUNCTION, GenericObject)); - if (!returnUndefined) { - return nullptr; - } - return JS::CallOriginalPromiseThen(cx, sourceCancelPromise, returnUndefined, - nullptr); -} - -/** - * Streams spec, 3.5.4. ReadableStreamClose ( stream ) - */ -[[nodiscard]] bool js::ReadableStreamCloseInternal( - JSContext* cx, Handle unwrappedStream) { - // Step 1: Assert: stream.[[state]] is "readable". - MOZ_ASSERT(unwrappedStream->readable()); - - // Step 2: Set stream.[[state]] to "closed". - unwrappedStream->setClosed(); - - // Step 4: If reader is undefined, return (reordered). - if (!unwrappedStream->hasReader()) { - return true; - } - - // Step 3: Let reader be stream.[[reader]]. - Rooted unwrappedReader( - cx, UnwrapReaderFromStream(cx, unwrappedStream)); - if (!unwrappedReader) { - return false; - } - - // Step 5: If ! IsReadableStreamDefaultReader(reader) is true, - if (unwrappedReader->is()) { - ForAuthorCodeBool forAuthorCode = unwrappedReader->forAuthorCode(); - - // Step a: Repeat for each readRequest that is an element of - // reader.[[readRequests]], - Rooted unwrappedReadRequests(cx, unwrappedReader->requests()); - uint32_t len = unwrappedReadRequests->length(); - Rooted readRequest(cx); - Rooted resultObj(cx); - Rooted resultVal(cx); - for (uint32_t i = 0; i < len; i++) { - // Step i: Resolve readRequest.[[promise]] with - // ! ReadableStreamCreateReadResult(undefined, true, - // readRequest.[[forAuthorCode]]). - readRequest = &unwrappedReadRequests->getAs(i); - if (!cx->compartment()->wrap(cx, &readRequest)) { - return false; - } - - resultObj = js::ReadableStreamCreateReadResult(cx, UndefinedHandleValue, - true, forAuthorCode); - if (!resultObj) { - return false; - } - resultVal = ObjectValue(*resultObj); - if (!ResolvePromise(cx, readRequest, resultVal)) { - return false; - } - } - - // Step b: Set reader.[[readRequests]] to an empty List. - unwrappedReader->clearRequests(); - } - - // Step 6: Resolve reader.[[closedPromise]] with undefined. - if (!ResolveUnwrappedPromiseWithUndefined(cx, - unwrappedReader->closedPromise())) { - return false; - } - - if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource) { - // Make sure we're in the stream's compartment. - AutoRealm ar(cx, unwrappedStream); - JS::ReadableStreamUnderlyingSource* source = - unwrappedStream->controller()->externalSource(); - source->onClosed(cx, unwrappedStream); - } - - return true; -} - -/** - * Streams spec, 3.5.5. ReadableStreamCreateReadResult ( value, done, - * forAuthorCode ) - */ -[[nodiscard]] PlainObject* js::ReadableStreamCreateReadResult( - JSContext* cx, Handle value, bool done, - ForAuthorCodeBool forAuthorCode) { - // Step 1: Let prototype be null. - // Step 2: If forAuthorCode is true, set prototype to %ObjectPrototype%. - Rooted templateObject( - cx, - forAuthorCode == ForAuthorCodeBool::Yes - ? GlobalObject::getOrCreateIterResultTemplateObject(cx) - : GlobalObject::getOrCreateIterResultWithoutPrototypeTemplateObject( - cx)); - if (!templateObject) { - return nullptr; - } - - // Step 3: Assert: Type(done) is Boolean (implicit). - - // Step 4: Let obj be ObjectCreate(prototype). - PlainObject* obj = PlainObject::createWithTemplate(cx, templateObject); - if (!obj) { - return nullptr; - } - - // Step 5: Perform CreateDataProperty(obj, "value", value). - obj->setSlot(GlobalObject::IterResultObjectValueSlot, value); - - // Step 6: Perform CreateDataProperty(obj, "done", done). - obj->setSlot(GlobalObject::IterResultObjectDoneSlot, BooleanValue(done)); - - // Step 7: Return obj. - return obj; -} - -/** - * Streams spec, 3.5.6. ReadableStreamError ( stream, e ) - */ -[[nodiscard]] bool js::ReadableStreamErrorInternal( - JSContext* cx, Handle unwrappedStream, Handle e) { - // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). - - // Step 2: Assert: stream.[[state]] is "readable". - MOZ_ASSERT(unwrappedStream->readable()); - - // Step 3: Set stream.[[state]] to "errored". - unwrappedStream->setErrored(); - - // Step 4: Set stream.[[storedError]] to e. - { - AutoRealm ar(cx, unwrappedStream); - Rooted wrappedError(cx, e); - if (!cx->compartment()->wrap(cx, &wrappedError)) { - return false; - } - unwrappedStream->setStoredError(wrappedError); - } - - // Step 6: If reader is undefined, return (reordered). - if (!unwrappedStream->hasReader()) { - return true; - } - - // Step 5: Let reader be stream.[[reader]]. - Rooted unwrappedReader( - cx, UnwrapReaderFromStream(cx, unwrappedStream)); - if (!unwrappedReader) { - return false; - } - - // Steps 7-8: (Identical in our implementation.) - // Step 7.a/8.b: Repeat for each read{Into}Request that is an element of - // reader.[[read{Into}Requests]], - { - Rooted unwrappedReadRequests(cx, unwrappedReader->requests()); - Rooted readRequest(cx); - uint32_t len = unwrappedReadRequests->length(); - for (uint32_t i = 0; i < len; i++) { - // Step i: Reject read{Into}Request.[[promise]] with e. - // Responses have to be created in the compartment from which the error - // was triggered, which might not be the same as the one the request was - // created in, so we have to wrap requests here. - readRequest = &unwrappedReadRequests->get(i).toObject(); - if (!RejectUnwrappedPromiseWithError(cx, &readRequest, e)) { - return false; - } - } - } - - // Step 7.b/8.c: Set reader.[[read{Into}Requests]] to a new empty List. - if (!StoreNewListInFixedSlot(cx, unwrappedReader, - ReadableStreamReader::Slot_Requests)) { - return false; - } - - // Step 9: Reject reader.[[closedPromise]] with e. - if (!RejectUnwrappedPromiseWithError(cx, unwrappedReader->closedPromise(), - e)) { - return false; - } - - // Step 10: Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. - // - // `closedPromise` can return a CCW, but that case is filtered out by step 6, - // given the only place that can set [[closedPromise]] to a CCW is - // 3.8.5 ReadableStreamReaderGenericRelease step 4, and - // 3.8.5 ReadableStreamReaderGenericRelease step 6 sets - // stream.[[reader]] to undefined. - Rooted closedPromise(cx, unwrappedReader->closedPromise()); - js::SetSettledPromiseIsHandled(cx, closedPromise.as()); - - if (unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource) { - // Make sure we're in the stream's compartment. - AutoRealm ar(cx, unwrappedStream); - JS::ReadableStreamUnderlyingSource* source = - unwrappedStream->controller()->externalSource(); - - // Ensure that the embedding doesn't have to deal with - // mixed-compartment arguments to the callback. - Rooted error(cx, e); - if (!cx->compartment()->wrap(cx, &error)) { - return false; - } - source->onErrored(cx, unwrappedStream, error); - } - - return true; -} - -/** - * Streams spec, 3.5.7. - * ReadableStreamFulfillReadIntoRequest( stream, chunk, done ) - * Streams spec, 3.5.8. - * ReadableStreamFulfillReadRequest ( stream, chunk, done ) - * These two spec functions are identical in our implementation. - */ -[[nodiscard]] bool js::ReadableStreamFulfillReadOrReadIntoRequest( - JSContext* cx, Handle unwrappedStream, Handle chunk, - bool done) { - cx->check(chunk); - - // Step 1: Let reader be stream.[[reader]]. - Rooted unwrappedReader( - cx, UnwrapReaderFromStream(cx, unwrappedStream)); - if (!unwrappedReader) { - return false; - } - - // Step 2: Let read{Into}Request be the first element of - // reader.[[read{Into}Requests]]. - // Step 3: Remove read{Into}Request from reader.[[read{Into}Requests]], - // shifting all other elements downward (so that the second becomes - // the first, and so on). - Rooted unwrappedReadIntoRequests(cx, - unwrappedReader->requests()); - Rooted readIntoRequest( - cx, &unwrappedReadIntoRequests->popFirstAs(cx)); - MOZ_ASSERT(readIntoRequest); - if (!cx->compartment()->wrap(cx, &readIntoRequest)) { - return false; - } - - // Step 4: Resolve read{Into}Request.[[promise]] with - // ! ReadableStreamCreateReadResult(chunk, done, - // readIntoRequest.[[forAuthorCode]]). - PlainObject* iterResult = ReadableStreamCreateReadResult( - cx, chunk, done, unwrappedReader->forAuthorCode()); - if (!iterResult) { - return false; - } - - Rooted val(cx, ObjectValue(*iterResult)); - return ResolvePromise(cx, readIntoRequest, val); -} - -/** - * Streams spec, 3.5.9. ReadableStreamGetNumReadIntoRequests ( stream ) - * Streams spec, 3.5.10. ReadableStreamGetNumReadRequests ( stream ) - * (Identical implementation.) - */ -uint32_t js::ReadableStreamGetNumReadRequests(ReadableStream* stream) { - // Step 1: Return the number of elements in - // stream.[[reader]].[[read{Into}Requests]]. - if (!stream->hasReader()) { - return 0; - } - - JS::AutoSuppressGCAnalysis nogc; - ReadableStreamReader* reader = UnwrapReaderFromStreamNoThrow(stream); - - // Reader is a dead wrapper, treat it as non-existent. - if (!reader) { - return 0; - } - - return reader->requests()->length(); -} - -// Streams spec, 3.5.11. ReadableStreamHasBYOBReader ( stream ) -// -// Not implemented. - -/** - * Streams spec 3.5.12. ReadableStreamHasDefaultReader ( stream ) - */ -[[nodiscard]] bool js::ReadableStreamHasDefaultReader( - JSContext* cx, Handle unwrappedStream, bool* result) { - // Step 1: Let reader be stream.[[reader]]. - // Step 2: If reader is undefined, return false. - if (!unwrappedStream->hasReader()) { - *result = false; - return true; - } - Rooted unwrappedReader( - cx, UnwrapReaderFromStream(cx, unwrappedStream)); - if (!unwrappedReader) { - return false; - } - - // Step 3: If ! ReadableStreamDefaultReader(reader) is false, return false. - // Step 4: Return true. - *result = unwrappedReader->is(); - return true; -} diff --git a/js/src/builtin/streams/ReadableStreamInternals.h b/js/src/builtin/streams/ReadableStreamInternals.h deleted file mode 100644 index a2b22f107a36..000000000000 --- a/js/src/builtin/streams/ReadableStreamInternals.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* The interface between readable streams and controllers. */ - -#ifndef builtin_streams_ReadableStreamInternals_h -#define builtin_streams_ReadableStreamInternals_h - -#include "jstypes.h" // JS_PUBLIC_API -#include "builtin/streams/ReadableStreamReader.h" // js::ForAuthorCodeBool -#include "js/RootingAPI.h" // JS::Handle -#include "js/Value.h" // JS::Value - -struct JS_PUBLIC_API JSContext; -class JS_PUBLIC_API JSObject; - -namespace js { - -class PlainObject; -class PromiseObject; -class ReadableStream; - -[[nodiscard]] extern PromiseObject* ReadableStreamAddReadOrReadIntoRequest( - JSContext* cx, JS::Handle unwrappedStream); - -[[nodiscard]] extern JSObject* ReadableStreamCancel( - JSContext* cx, JS::Handle unwrappedStream, - JS::Handle reason); - -[[nodiscard]] extern bool ReadableStreamCloseInternal( - JSContext* cx, JS::Handle unwrappedStream); - -[[nodiscard]] extern PlainObject* ReadableStreamCreateReadResult( - JSContext* cx, JS::Handle value, bool done, - ForAuthorCodeBool forAuthorCode); - -[[nodiscard]] extern bool ReadableStreamErrorInternal( - JSContext* cx, JS::Handle unwrappedStream, - JS::Handle e); - -[[nodiscard]] extern bool ReadableStreamFulfillReadOrReadIntoRequest( - JSContext* cx, JS::Handle unwrappedStream, - JS::Handle chunk, bool done); - -extern uint32_t ReadableStreamGetNumReadRequests(ReadableStream* stream); - -[[nodiscard]] extern bool ReadableStreamHasDefaultReader( - JSContext* cx, JS::Handle unwrappedStream, bool* result); - -} // namespace js - -#endif // builtin_streams_ReadableStreamInternals_h diff --git a/js/src/builtin/streams/ReadableStreamOperations.cpp b/js/src/builtin/streams/ReadableStreamOperations.cpp deleted file mode 100644 index 1778fe9bb458..000000000000 --- a/js/src/builtin/streams/ReadableStreamOperations.cpp +++ /dev/null @@ -1,633 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* General readable stream abstract operations. */ - -#include "builtin/streams/ReadableStreamOperations.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} - -#include "builtin/Array.h" // js::NewDenseFullyAllocatedArray -#include "builtin/Promise.h" // js::RejectPromiseWithPendingError -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "builtin/streams/ReadableStreamController.h" // js::ReadableStream{,Default}Controller -#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamDefaultController{Close,Enqueue}, js::ReadableStreamControllerError, js::SourceAlgorithms -#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStreamCancel -#include "builtin/streams/ReadableStreamReader.h" // js::CreateReadableStreamDefaultReader, js::ForAuthorCodeBool, js::ReadableStream{,Default}Reader, js::ReadableStreamDefaultReaderRead -#include "builtin/streams/TeeState.h" // js::TeeState -#include "js/CallAndConstruct.h" // JS::IsCallable -#include "js/CallArgs.h" // JS::CallArgs{,FromVp} -#include "js/Promise.h" // JS::CallOriginalPromiseThen, JS::AddPromiseReactions -#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted -#include "js/Value.h" // JS::Value, JS::UndefinedHandleValue -#include "vm/JSContext.h" // JSContext -#include "vm/NativeObject.h" // js::NativeObject -#include "vm/ObjectOperations.h" // js::GetProperty -#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined - -#include "builtin/HandlerFunction-inl.h" // js::NewHandler, js::TargetFromHandler -#include "builtin/streams/MiscellaneousOperations-inl.h" // js::ResolveUnwrappedPromiseWithValue -#include "builtin/streams/ReadableStreamReader-inl.h" // js::UnwrapReaderFromStream -#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::Unwrap{Callee,Internal}Slot -#include "vm/JSContext-inl.h" // JSContext::check -#include "vm/JSObject-inl.h" // js::IsCallable, js::NewObjectWithClassProto -#include "vm/Realm-inl.h" // js::AutoRealm - -using js::IsCallable; -using js::NewHandler; -using js::NewObjectWithClassProto; -using js::PromiseObject; -using js::ReadableStream; -using js::ReadableStreamDefaultController; -using js::ReadableStreamDefaultControllerEnqueue; -using js::ReadableStreamDefaultReader; -using js::ReadableStreamReader; -using js::SourceAlgorithms; -using js::TargetFromHandler; -using js::TeeState; -using js::UnwrapCalleeSlot; - -using JS::CallArgs; -using JS::CallArgsFromVp; -using JS::Handle; -using JS::MutableHandle; -using JS::ObjectValue; -using JS::Rooted; -using JS::UndefinedHandleValue; -using JS::Value; - -/*** 3.4. General readable stream abstract operations ***********************/ - -// Streams spec, 3.4.1. AcquireReadableStreamBYOBReader ( stream ) -// Always inlined. - -// Streams spec, 3.4.2. AcquireReadableStreamDefaultReader ( stream ) -// Always inlined. See CreateReadableStreamDefaultReader. - -/** - * Streams spec, 3.4.3. CreateReadableStream ( - * startAlgorithm, pullAlgorithm, cancelAlgorithm - * [, highWaterMark [, sizeAlgorithm ] ] ) - * - * The start/pull/cancelAlgorithm arguments are represented instead as four - * arguments: sourceAlgorithms, underlyingSource, pullMethod, cancelMethod. - * See the comment on SetUpReadableStreamDefaultController. - */ -[[nodiscard]] static ReadableStream* CreateReadableStream( - JSContext* cx, SourceAlgorithms sourceAlgorithms, - Handle underlyingSource, - Handle pullMethod = UndefinedHandleValue, - Handle cancelMethod = UndefinedHandleValue, double highWaterMark = 1, - Handle sizeAlgorithm = UndefinedHandleValue, - Handle proto = nullptr) { - cx->check(underlyingSource, sizeAlgorithm, proto); - MOZ_ASSERT(sizeAlgorithm.isUndefined() || IsCallable(sizeAlgorithm)); - - // Step 1: If highWaterMark was not passed, set it to 1 (implicit). - // Step 2: If sizeAlgorithm was not passed, set it to an algorithm that - // returns 1 (implicit). - // Step 3: Assert: ! IsNonNegativeNumber(highWaterMark) is true. - MOZ_ASSERT(highWaterMark >= 0); - - // Step 4: Let stream be ObjectCreate(the original value of ReadableStream's - // prototype property). - // Step 5: Perform ! InitializeReadableStream(stream). - Rooted stream(cx, - ReadableStream::create(cx, nullptr, proto)); - if (!stream) { - return nullptr; - } - - // Step 6: Let controller be ObjectCreate(the original value of - // ReadableStreamDefaultController's prototype property). - // Step 7: Perform ? SetUpReadableStreamDefaultController(stream, - // controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, - // highWaterMark, sizeAlgorithm). - if (!SetUpReadableStreamDefaultController( - cx, stream, sourceAlgorithms, underlyingSource, pullMethod, - cancelMethod, highWaterMark, sizeAlgorithm)) { - return nullptr; - } - - // Step 8: Return stream. - return stream; -} - -// Streams spec, 3.4.4. CreateReadableByteStream ( -// startAlgorithm, pullAlgorithm, cancelAlgorithm -// [, highWaterMark [, autoAllocateChunkSize ] ] ) -// Not implemented. - -/** - * Streams spec, 3.4.5. InitializeReadableStream ( stream ) - */ -/* static */ [[nodiscard]] ReadableStream* ReadableStream::create( - JSContext* cx, void* nsISupportsObject_alreadyAddreffed /* = nullptr */, - Handle proto /* = nullptr */) { - // In the spec, InitializeReadableStream is always passed a newly created - // ReadableStream object. We instead create it here and return it below. - Rooted stream( - cx, NewObjectWithClassProto(cx, proto)); - if (!stream) { - return nullptr; - } - - static_assert(Slot_ISupports == 0, - "Must use right slot for JSCLASS_SLOT0_IS_NSISUPPORTS"); - JS::SetObjectISupports(stream, nsISupportsObject_alreadyAddreffed); - - // Step 1: Set stream.[[state]] to "readable". - stream->initStateBits(Readable); - MOZ_ASSERT(stream->readable()); - - // Step 2: Set stream.[[reader]] and stream.[[storedError]] to - // undefined (implicit). - MOZ_ASSERT(!stream->hasReader()); - MOZ_ASSERT(stream->storedError().isUndefined()); - - // Step 3: Set stream.[[disturbed]] to false (done in step 1). - MOZ_ASSERT(!stream->disturbed()); - - return stream; -} - -// Streams spec, 3.4.6. IsReadableStream ( x ) -// Using UnwrapAndTypeCheck templates instead. - -// Streams spec, 3.4.7. IsReadableStreamDisturbed ( stream ) -// Using stream->disturbed() instead. - -/** - * Streams spec, 3.4.8. IsReadableStreamLocked ( stream ) - */ -bool ReadableStream::locked() const { - // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). - // Step 2: If stream.[[reader]] is undefined, return false. - // Step 3: Return true. - // Special-casing for streams with external sources. Those can be locked - // explicitly via JSAPI, which is indicated by a controller flag. - // IsReadableStreamLocked is called from the controller's constructor, at - // which point we can't yet call stream->controller(), but the source also - // can't be locked yet. - if (hasController() && controller()->sourceLocked()) { - return true; - } - return hasReader(); -} - -// Streams spec, 3.4.9. IsReadableStreamAsyncIterator ( x ) -// -// Not implemented. - -/** - * Streams spec, 3.4.10. ReadableStreamTee steps 12.c.i-x. - */ -static bool TeeReaderReadHandler(JSContext* cx, unsigned argc, Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - Rooted unwrappedTeeState(cx, - UnwrapCalleeSlot(cx, args, 0)); - if (!unwrappedTeeState) { - return false; - } - - Handle resultVal = args.get(0); - - // Step 12.c.i: Set reading to false. - unwrappedTeeState->unsetReading(); - - // Step 12.c.ii: Assert: Type(result) is Object. - Rooted result(cx, &resultVal.toObject()); - - bool done; - { - // Step 12.c.iii: Let done be ? Get(result, "done"). - // (This can fail only if `result` was nuked.) - Rooted doneVal(cx); - if (!GetProperty(cx, result, result, cx->names().done, &doneVal)) { - return false; - } - - // Step 12.c.iv: Assert: Type(done) is Boolean. - done = doneVal.toBoolean(); - } - - if (done) { - // Step 12.3 close steps - - // Step 1: Set reading to false (done unconditionally above). - // Step 2: If canceled1 is false, perform - // ! ReadableStreamDefaultControllerClose(branch1.[[controller]]). - if (!unwrappedTeeState->canceled1()) { - Rooted unwrappedBranch1( - cx, unwrappedTeeState->branch1()); - if (!ReadableStreamDefaultControllerClose(cx, unwrappedBranch1)) { - return false; - } - } - - // Step 3: If canceled2 is false, perform - // ! ReadableStreamDefaultControllerClose(branch2.[[controller]]). - if (!unwrappedTeeState->canceled2()) { - Rooted unwrappedBranch2( - cx, unwrappedTeeState->branch2()); - if (!ReadableStreamDefaultControllerClose(cx, unwrappedBranch2)) { - return false; - } - } - - // Step 4: If canceled1 is false or canceled2 is false, - // resolve cancelPromise with undefined. - if (!unwrappedTeeState->canceled1() || !unwrappedTeeState->canceled2()) { - Rooted unwrappedCancelPromise( - cx, unwrappedTeeState->cancelPromise()); - MOZ_ASSERT(unwrappedCancelPromise != nullptr); - - if (!ResolveUnwrappedPromiseWithUndefined(cx, unwrappedCancelPromise)) { - return false; - } - } - - args.rval().setUndefined(); - return true; - } - - // Step 12.c.vi: Let value be ! Get(result, "value"). - // (This can fail only if `result` was nuked.) - Rooted value(cx); - if (!GetProperty(cx, result, result, cx->names().value, &value)) { - return false; - } - - // Step 12.c.vii: Let value1 and value2 be value. - // Step 12.c.viii: If canceled2 is false and cloneForBranch2 is true, set - // value2 to - // ? StructuredDeserialize(? StructuredSerialize(value2), - // the current Realm Record). - // We don't yet support any specifications that use cloneForBranch2, and - // the Streams spec doesn't offer any way for author code to enable it, - // so it's always false here. - auto& value1 = value; - MOZ_ASSERT(!unwrappedTeeState->cloneForBranch2(), - "support for cloneForBranch2=true is not yet implemented"); - auto& value2 = value; - - Rooted unwrappedController(cx); - - // Step 12.c.ix: If canceled1 is false, perform - // ? ReadableStreamDefaultControllerEnqueue( - // branch1.[[readableStreamController]], value1). - if (!unwrappedTeeState->canceled1()) { - unwrappedController = unwrappedTeeState->branch1(); - if (!ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, - value1)) { - return false; - } - } - - // Step 12.c.x: If canceled2 is false, perform - // ? ReadableStreamDefaultControllerEnqueue( - // branch2.[[readableStreamController]], value2). - if (!unwrappedTeeState->canceled2()) { - unwrappedController = unwrappedTeeState->branch2(); - if (!ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, - value2)) { - return false; - } - } - - args.rval().setUndefined(); - return true; -} - -/** - * Streams spec, 3.4.10. ReadableStreamTee step 12, "Let pullAlgorithm be the - * following steps:" - */ -[[nodiscard]] PromiseObject* js::ReadableStreamTee_Pull( - JSContext* cx, JS::Handle unwrappedTeeState) { - // Combine step 12.a/12.e far below, and handle steps 12.b-12.d after - // inverting step 12.a's "If reading is true" condition. - if (!unwrappedTeeState->reading()) { - // Step 12.b: Set reading to true. - unwrappedTeeState->setReading(); - - // Implicit in the spec: Unpack `reader` from the TeeState (by way of the - // stream stored in one of its slots). - Rooted unwrappedReader(cx); - { - Rooted unwrappedStream( - cx, UnwrapInternalSlot(cx, unwrappedTeeState, - TeeState::Slot_Stream)); - if (!unwrappedStream) { - return nullptr; - } - ReadableStreamReader* unwrappedReaderObj = - UnwrapReaderFromStream(cx, unwrappedStream); - if (!unwrappedReaderObj) { - return nullptr; - } - - unwrappedReader = &unwrappedReaderObj->as(); - } - - // Step 12.c: Let readPromise be the result of reacting to - // ! ReadableStreamDefaultReaderRead(reader) with the following - // fulfillment steps given the argument result: [...] - // Step 12.d: Set readPromise.[[PromiseIsHandled]] to true. - - // First, perform |ReadableStreamDefaultReaderRead(reader)|. - Rooted readerReadResultPromise( - cx, js::ReadableStreamDefaultReaderRead(cx, unwrappedReader)); - if (!readerReadResultPromise) { - return nullptr; - } - - // Next, create a function to perform the fulfillment steps under step 12.c - // (implemented in the |TeeReaderReadHandler| C++ function). - Rooted teeState(cx, unwrappedTeeState); - if (!cx->compartment()->wrap(cx, &teeState)) { - return nullptr; - } - - Rooted onFulfilled( - cx, NewHandler(cx, TeeReaderReadHandler, teeState)); - if (!onFulfilled) { - return nullptr; - } - - // Finally, perform those fulfillment steps when |readerReadResultPromise| - // fulfills. (Step 12.c doesn't provide rejection steps, so don't handle - // rejection.) - // - // The spec's |readPromise| promise is unobservable, so implement this using - // a JSAPI function that acts as if it created |readPromise| but doesn't - // actually do so. - // - // Step 12.d causes |readPromise| to be treated as handled, even if it - // rejects. Use |JS::AddPromiseReactionsIgnoringUnhandledRejection|, not - // |JS::AddPromiseReactions|, to avoid reporting a freshly-consed-up promise - // as rejected if |readerReadResultPromise| rejects. - if (!JS::AddPromiseReactionsIgnoringUnhandledRejection( - cx, readerReadResultPromise, onFulfilled, nullptr)) { - return nullptr; - } - } - - // Step 12.a: (If reading is true,) return a promise resolved with undefined. - // Step 12.e: Return a promise resolved with undefined. - return PromiseResolvedWithUndefined(cx); -} - -/** - * Cancel one branch of a tee'd stream with the given |reason_|. - * - * Streams spec, 3.4.10. ReadableStreamTee steps 13 and 14: "Let - * cancel1Algorithm/cancel2Algorithm be the following steps, taking a reason - * argument:" - */ -[[nodiscard]] JSObject* js::ReadableStreamTee_Cancel( - JSContext* cx, JS::Handle unwrappedTeeState, - JS::Handle unwrappedBranch, - JS::Handle reason) { - Rooted unwrappedStream( - cx, UnwrapInternalSlot(cx, unwrappedTeeState, - TeeState::Slot_Stream)); - if (!unwrappedStream) { - return nullptr; - } - - bool bothBranchesCanceled = false; - - // Step 13/14.a: Set canceled1/canceled2 to true. - // Step 13/14.b: Set reason1/reason2 to reason. - { - AutoRealm ar(cx, unwrappedTeeState); - - Rooted unwrappedReason(cx, reason); - if (!cx->compartment()->wrap(cx, &unwrappedReason)) { - return nullptr; - } - - if (unwrappedBranch->isTeeBranch1()) { - unwrappedTeeState->setCanceled1(unwrappedReason); - bothBranchesCanceled = unwrappedTeeState->canceled2(); - } else { - MOZ_ASSERT(unwrappedBranch->isTeeBranch2()); - unwrappedTeeState->setCanceled2(unwrappedReason); - bothBranchesCanceled = unwrappedTeeState->canceled1(); - } - } - - Rooted unwrappedCancelPromise( - cx, unwrappedTeeState->cancelPromise()); - MOZ_ASSERT(unwrappedCancelPromise != nullptr); - - // Step 13/14.c: If canceled2/canceled1 is true, - if (bothBranchesCanceled) { - // Step 13/14.c.i: Let compositeReason be - // ! CreateArrayFromList(« reason1, reason2 »). - Rooted compositeReason(cx); - { - Rooted reason1(cx, unwrappedTeeState->reason1()); - Rooted reason2(cx, unwrappedTeeState->reason2()); - if (!cx->compartment()->wrap(cx, &reason1) || - !cx->compartment()->wrap(cx, &reason2)) { - return nullptr; - } - - ArrayObject* reasonArray = NewDenseFullyAllocatedArray(cx, 2); - if (!reasonArray) { - return nullptr; - } - reasonArray->setDenseInitializedLength(2); - reasonArray->initDenseElement(0, reason1); - reasonArray->initDenseElement(1, reason2); - - compositeReason = ObjectValue(*reasonArray); - } - - // Step 13/14.c.ii: Let cancelResult be - // ! ReadableStreamCancel(stream, compositeReason). - // In our implementation, this can fail with OOM. The best course then - // is to reject cancelPromise with an OOM error. - Rooted cancelResult( - cx, js::ReadableStreamCancel(cx, unwrappedStream, compositeReason)); - if (!cancelResult) { - // Handle the OOM case mentioned above. - AutoRealm ar(cx, unwrappedCancelPromise); - if (!RejectPromiseWithPendingError(cx, unwrappedCancelPromise)) { - return nullptr; - } - } else { - // Step 13/14.c.iii: Resolve cancelPromise with cancelResult. - Rooted cancelResultVal(cx, ObjectValue(*cancelResult)); - if (!ResolveUnwrappedPromiseWithValue(cx, unwrappedCancelPromise, - cancelResultVal)) { - return nullptr; - } - } - } - - // Step 13/14.d: Return cancelPromise. - Rooted cancelPromise(cx, unwrappedCancelPromise); - if (!cx->compartment()->wrap(cx, &cancelPromise)) { - return nullptr; - } - - return cancelPromise; -} - -/* - * https://streams.spec.whatwg.org/#readable-stream-tee - * ReadableStreamTee(stream, cloneForBranch2) - * - * Step 18: Upon rejection of reader.[[closedPromise]] with reason r, - */ -static bool TeeReaderErroredHandler(JSContext* cx, unsigned argc, - JS::Value* vp) { - CallArgs args = CallArgsFromVp(argc, vp); - - Rooted teeState(cx, TargetFromHandler(args)); - Handle reason = args.get(0); - - Rooted unwrappedBranchController(cx); - - // Step 18.1: Perform - // ! ReadableStreamDefaultControllerError( - // branch1.[[controller]], r). - unwrappedBranchController = teeState->branch1(); - if (!ReadableStreamControllerError(cx, unwrappedBranchController, reason)) { - return false; - } - - // Step 18.2: Perform - // ! ReadableStreamDefaultControllerError( - // branch2.[[controller]], r). - unwrappedBranchController = teeState->branch2(); - if (!ReadableStreamControllerError(cx, unwrappedBranchController, reason)) { - return false; - } - - // Step 18.3: If canceled1 is false or canceled2 is false, - // resolve cancelPromise with undefined. - if (!teeState->canceled1() || !teeState->canceled2()) { - Rooted unwrappedCancelPromise(cx, - teeState->cancelPromise()); - MOZ_ASSERT(unwrappedCancelPromise != nullptr); - - if (!ResolveUnwrappedPromiseWithUndefined(cx, unwrappedCancelPromise)) { - return false; - } - } - - args.rval().setUndefined(); - return true; -} - -/** - * Streams spec, 3.4.10. ReadableStreamTee ( stream, cloneForBranch2 ) - */ -[[nodiscard]] bool js::ReadableStreamTee( - JSContext* cx, JS::Handle unwrappedStream, - bool cloneForBranch2, JS::MutableHandle branch1Stream, - JS::MutableHandle branch2Stream) { - // Step 1: Assert: ! IsReadableStream(stream) is true (implicit). - - // Step 2: Assert: Type(cloneForBranch2) is Boolean (implicit). - // - // The streams spec only ever passes |cloneForBranch2 = false|. It's expected - // that external specs that pass |cloneForBranch2 = true| will at some point - // come into existence, but we don't presently implement any such specs. - MOZ_ASSERT(!cloneForBranch2, - "support for cloneForBranch2=true is not yet implemented"); - - // Step 3: Let reader be ? AcquireReadableStreamDefaultReader(stream). - Rooted reader( - cx, CreateReadableStreamDefaultReader(cx, unwrappedStream, - ForAuthorCodeBool::No)); - if (!reader) { - return false; - } - - // Several algorithms close over the variables initialized in the next few - // steps, so we allocate them in an object, the TeeState. The algorithms - // also close over `stream` and `reader`, so TeeState gets a reference to - // the stream. - // - // Step 4: Let reading be false. - // Step 5: Let canceled1 be false. - // Step 6: Let canceled2 be false. - // Step 7: Let reason1 be undefined. - // Step 8: Let reason2 be undefined. - // Step 9: Let branch1 be undefined. - // Step 10: Let branch2 be undefined. - // Step 11: Let cancelPromise be a new promise. - Rooted teeState(cx, TeeState::create(cx, unwrappedStream)); - if (!teeState) { - return false; - } - - MOZ_ASSERT(!teeState->reading()); - MOZ_ASSERT(!teeState->canceled1()); - MOZ_ASSERT(!teeState->canceled2()); - - // Step 12: Let pullAlgorithm be the following steps: [...] - // Step 13: Let cancel1Algorithm be the following steps: [...] - // Step 14: Let cancel2Algorithm be the following steps: [...] - // Step 15: Let startAlgorithm be an algorithm that returns undefined. - // - // Implicit. Our implementation does not use objects to represent - // [[pullAlgorithm]], [[cancelAlgorithm]], and so on. Instead, we decide - // which one to perform based on class checks. For example, our - // implementation of ReadableStreamControllerCallPullIfNeeded checks - // whether the stream's underlyingSource is a TeeState object. - - // Step 16: Set branch1 to - // ! CreateReadableStream(startAlgorithm, pullAlgorithm, - // cancel1Algorithm). - Rooted underlyingSource(cx, ObjectValue(*teeState)); - branch1Stream.set( - CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource)); - if (!branch1Stream) { - return false; - } - - Rooted branch1(cx); - branch1 = &branch1Stream->controller()->as(); - branch1->setTeeBranch1(); - teeState->setBranch1(branch1); - - // Step 17: Set branch2 to - // ! CreateReadableStream(startAlgorithm, pullAlgorithm, - // cancel2Algorithm). - branch2Stream.set( - CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource)); - if (!branch2Stream) { - return false; - } - - Rooted branch2(cx); - branch2 = &branch2Stream->controller()->as(); - branch2->setTeeBranch2(); - teeState->setBranch2(branch2); - - // Step 18: Upon rejection of reader.[[closedPromise]] with reason r, [...] - Rooted closedPromise(cx, reader->closedPromise()); - - Rooted onRejected( - cx, NewHandler(cx, TeeReaderErroredHandler, teeState)); - if (!onRejected) { - return false; - } - - if (!JS::AddPromiseReactions(cx, closedPromise, nullptr, onRejected)) { - return false; - } - - // Step 19: Return « branch1, branch2 ». - return true; -} diff --git a/js/src/builtin/streams/ReadableStreamOperations.h b/js/src/builtin/streams/ReadableStreamOperations.h deleted file mode 100644 index 0936c814d411..000000000000 --- a/js/src/builtin/streams/ReadableStreamOperations.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* General readable stream abstract operations. */ - -#ifndef builtin_streams_ReadableStreamOperations_h -#define builtin_streams_ReadableStreamOperations_h - -#include "js/RootingAPI.h" // JS::Handle -#include "js/Value.h" // JS::Value - -class JS_PUBLIC_API JSObject; - -namespace js { - -class PromiseObject; -class ReadableStream; -class ReadableStreamDefaultController; -class TeeState; - -[[nodiscard]] extern PromiseObject* ReadableStreamTee_Pull( - JSContext* cx, JS::Handle unwrappedTeeState); - -[[nodiscard]] extern JSObject* ReadableStreamTee_Cancel( - JSContext* cx, JS::Handle unwrappedTeeState, - JS::Handle unwrappedBranch, - JS::Handle reason); - -[[nodiscard]] extern bool ReadableStreamTee( - JSContext* cx, JS::Handle unwrappedStream, - bool cloneForBranch2, JS::MutableHandle branch1Stream, - JS::MutableHandle branch2Stream); - -} // namespace js - -#endif // builtin_streams_ReadableStreamOperations_h diff --git a/js/src/builtin/streams/ReadableStreamReader-inl.h b/js/src/builtin/streams/ReadableStreamReader-inl.h deleted file mode 100644 index e3b4eabbf8a7..000000000000 --- a/js/src/builtin/streams/ReadableStreamReader-inl.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -#ifndef builtin_streams_ReadableStreamReader_inl_h -#define builtin_streams_ReadableStreamReader_inl_h - -#include "builtin/streams/ReadableStreamReader.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT - -#include "jsfriendapi.h" // JS_IsDeadWrapper - -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "js/Proxy.h" // js::IsProxy -#include "js/RootingAPI.h" // JS::Handle -#include "vm/NativeObject.h" // js::NativeObject::getFixedSlot - -#include "vm/Compartment-inl.h" // js::UnwrapInternalSlot - -namespace js { - -/** - * Returns the stream associated with the given reader. - */ -[[nodiscard]] inline ReadableStream* UnwrapStreamFromReader( - JSContext* cx, JS::Handle reader) { - MOZ_ASSERT(reader->hasStream()); - return UnwrapInternalSlot(cx, reader, - ReadableStreamReader::Slot_Stream); -} - -/** - * Returns the reader associated with the given stream. - * - * Must only be called on ReadableStreams that already have a reader - * associated with them. - * - * If the reader is a wrapper, it will be unwrapped, so the result might not be - * an object from the currently active compartment. - */ -[[nodiscard]] inline ReadableStreamReader* UnwrapReaderFromStream( - JSContext* cx, JS::Handle stream) { - return UnwrapInternalSlot(cx, stream, - ReadableStream::Slot_Reader); -} - -[[nodiscard]] inline ReadableStreamReader* UnwrapReaderFromStreamNoThrow( - ReadableStream* stream) { - JSObject* readerObj = - &stream->getFixedSlot(ReadableStream::Slot_Reader).toObject(); - if (IsProxy(readerObj)) { - if (JS_IsDeadWrapper(readerObj)) { - return nullptr; - } - - readerObj = readerObj->maybeUnwrapAs(); - if (!readerObj) { - return nullptr; - } - } - - return &readerObj->as(); -} - -} // namespace js - -#endif // builtin_streams_ReadableStreamReader_inl_h diff --git a/js/src/builtin/streams/ReadableStreamReader.cpp b/js/src/builtin/streams/ReadableStreamReader.cpp deleted file mode 100644 index 959d694394bd..000000000000 --- a/js/src/builtin/streams/ReadableStreamReader.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* ReadableStream reader abstract operations. */ - -#include "builtin/streams/ReadableStreamReader-inl.h" - -#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} - -#include "jsfriendapi.h" // JS_ReportErrorNumberASCII - -#include "builtin/Stream.h" // js::ReadableStreamController, js::ReadableStreamControllerPullSteps -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "builtin/streams/ReadableStreamController.h" // js::ReadableStreamController -#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{Cancel,CreateReadResult} -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/RootingAPI.h" // JS::Handle, JS::Rooted -#include "js/Value.h" // JS::Value, JS::UndefinedHandleValue -#include "vm/Interpreter.h" // js::GetAndClearException -#include "vm/JSContext.h" // JSContext -#include "vm/PlainObject.h" // js::PlainObject -#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseResolvedWithUndefined -#include "vm/Runtime.h" // JSRuntime - -#include "builtin/Promise-inl.h" // js::SetSettledPromiseIsHandled -#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapInternalSlot -#include "vm/List-inl.h" // js::StoreNewListInFixedSlot -#include "vm/Realm-inl.h" // js::AutoRealm - -using JS::Handle; -using JS::Rooted; -using JS::Value; - -using js::PromiseObject; -using js::ReadableStreamController; -using js::UnwrapStreamFromReader; - -/*** 3.8. Readable stream reader abstract operations ************************/ - -// Streams spec, 3.8.1. IsReadableStreamDefaultReader ( x ) -// Implemented via is() - -// Streams spec, 3.8.2. IsReadableStreamBYOBReader ( x ) -// Implemented via is() - -/** - * Streams spec, 3.8.3. ReadableStreamReaderGenericCancel ( reader, reason ) - */ -[[nodiscard]] JSObject* js::ReadableStreamReaderGenericCancel( - JSContext* cx, Handle unwrappedReader, - Handle reason) { - // Step 1: Let stream be reader.[[ownerReadableStream]]. - // Step 2: Assert: stream is not undefined (implicit). - Rooted unwrappedStream( - cx, UnwrapStreamFromReader(cx, unwrappedReader)); - if (!unwrappedStream) { - return nullptr; - } - - // Step 3: Return ! ReadableStreamCancel(stream, reason). - return js::ReadableStreamCancel(cx, unwrappedStream, reason); -} - -/** - * Streams spec, 3.8.4. - * ReadableStreamReaderGenericInitialize ( reader, stream ) - */ -[[nodiscard]] bool js::ReadableStreamReaderGenericInitialize( - JSContext* cx, Handle reader, - Handle unwrappedStream, ForAuthorCodeBool forAuthorCode) { - cx->check(reader); - - // Step 1: Set reader.[[forAuthorCode]] to true. - reader->setForAuthorCode(forAuthorCode); - - // Step 2: Set reader.[[ownerReadableStream]] to stream. - { - Rooted readerCompartmentStream(cx, unwrappedStream); - if (!cx->compartment()->wrap(cx, &readerCompartmentStream)) { - return false; - } - reader->setStream(readerCompartmentStream); - } - - // Step 3 is moved to the end. - - // Step 4: If stream.[[state]] is "readable", - Rooted promise(cx); - if (unwrappedStream->readable()) { - // Step a: Set reader.[[closedPromise]] to a new promise. - promise = PromiseObject::createSkippingExecutor(cx); - } else if (unwrappedStream->closed()) { - // Step 5: Otherwise, if stream.[[state]] is "closed", - // Step a: Set reader.[[closedPromise]] to a promise resolved with - // undefined. - promise = PromiseResolvedWithUndefined(cx); - } else { - // Step 6: Otherwise, - // Step a: Assert: stream.[[state]] is "errored". - MOZ_ASSERT(unwrappedStream->errored()); - - // Step b: Set reader.[[closedPromise]] to a promise rejected with - // stream.[[storedError]]. - Rooted storedError(cx, unwrappedStream->storedError()); - if (!cx->compartment()->wrap(cx, &storedError)) { - return false; - } - promise = PromiseObject::unforgeableReject(cx, storedError); - if (!promise) { - return false; - } - - // Step c. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. - js::SetSettledPromiseIsHandled(cx, promise); - } - - if (!promise) { - return false; - } - - reader->setClosedPromise(promise); - - // Step 4 of caller 3.6.3. new ReadableStreamDefaultReader(stream): - // Step 5 of caller 3.7.3. new ReadableStreamBYOBReader(stream): - // Set this.[[read{Into}Requests]] to a new empty List. - if (!StoreNewListInFixedSlot(cx, reader, - ReadableStreamReader::Slot_Requests)) { - return false; - } - - // Step 3: Set stream.[[reader]] to reader. - // Doing this last prevents a partially-initialized reader from being - // attached to the stream (and possibly left there on OOM). - { - AutoRealm ar(cx, unwrappedStream); - Rooted streamCompartmentReader(cx, reader); - if (!cx->compartment()->wrap(cx, &streamCompartmentReader)) { - return false; - } - unwrappedStream->setReader(streamCompartmentReader); - } - - return true; -} - -/** - * Streams spec, 3.8.5. ReadableStreamReaderGenericRelease ( reader ) - */ -[[nodiscard]] bool js::ReadableStreamReaderGenericRelease( - JSContext* cx, Handle unwrappedReader) { - // Step 1: Assert: reader.[[ownerReadableStream]] is not undefined. - Rooted unwrappedStream( - cx, UnwrapStreamFromReader(cx, unwrappedReader)); - if (!unwrappedStream) { - return false; - } - - // Step 2: Assert: reader.[[ownerReadableStream]].[[reader]] is reader. -#ifdef DEBUG - // The assertion is weakened a bit to allow for nuked wrappers. - ReadableStreamReader* unwrappedReader2 = - UnwrapReaderFromStreamNoThrow(unwrappedStream); - MOZ_ASSERT_IF(unwrappedReader2, unwrappedReader2 == unwrappedReader); -#endif - - // Create an exception to reject promises with below. We don't have a - // clean way to do this, unfortunately. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMREADER_RELEASED); - Rooted exn(cx); - if (!cx->isExceptionPending() || !GetAndClearException(cx, &exn)) { - // Uncatchable error. Die immediately without resolving - // reader.[[closedPromise]]. - return false; - } - - // Step 3: If reader.[[ownerReadableStream]].[[state]] is "readable", reject - // reader.[[closedPromise]] with a TypeError exception. - Rooted unwrappedClosedPromise(cx); - if (unwrappedStream->readable()) { - unwrappedClosedPromise = UnwrapInternalSlot( - cx, unwrappedReader, ReadableStreamReader::Slot_ClosedPromise); - if (!unwrappedClosedPromise) { - return false; - } - - AutoRealm ar(cx, unwrappedClosedPromise); - if (!cx->compartment()->wrap(cx, &exn)) { - return false; - } - if (!PromiseObject::reject(cx, unwrappedClosedPromise, exn)) { - return false; - } - } else { - // Step 4: Otherwise, set reader.[[closedPromise]] to a new promise - // rejected with a TypeError exception. - Rooted closedPromise(cx, - PromiseObject::unforgeableReject(cx, exn)); - if (!closedPromise) { - return false; - } - unwrappedClosedPromise = &closedPromise->as(); - - AutoRealm ar(cx, unwrappedReader); - if (!cx->compartment()->wrap(cx, &closedPromise)) { - return false; - } - unwrappedReader->setClosedPromise(closedPromise); - } - - // Step 5: Set reader.[[closedPromise]].[[PromiseIsHandled]] to true. - js::SetSettledPromiseIsHandled(cx, unwrappedClosedPromise); - - // Step 6: Set reader.[[ownerReadableStream]].[[reader]] to undefined. - unwrappedStream->clearReader(); - - // Step 7: Set reader.[[ownerReadableStream]] to undefined. - unwrappedReader->clearStream(); - - return true; -} - -/** - * Streams spec, 3.8.7. - * ReadableStreamDefaultReaderRead ( reader [, forAuthorCode ] ) - */ -[[nodiscard]] PromiseObject* js::ReadableStreamDefaultReaderRead( - JSContext* cx, Handle unwrappedReader) { - // Step 1: If forAuthorCode was not passed, set it to false (implicit). - - // Step 2: Let stream be reader.[[ownerReadableStream]]. - // Step 3: Assert: stream is not undefined. - Rooted unwrappedStream( - cx, UnwrapStreamFromReader(cx, unwrappedReader)); - if (!unwrappedStream) { - return nullptr; - } - - // Step 4: Set stream.[[disturbed]] to true. - unwrappedStream->setDisturbed(); - - // Step 5: If stream.[[state]] is "closed", return a promise resolved with - // ! ReadableStreamCreateReadResult(undefined, true, forAuthorCode). - if (unwrappedStream->closed()) { - PlainObject* iterResult = ReadableStreamCreateReadResult( - cx, UndefinedHandleValue, true, unwrappedReader->forAuthorCode()); - if (!iterResult) { - return nullptr; - } - - Rooted iterResultVal(cx, JS::ObjectValue(*iterResult)); - return PromiseObject::unforgeableResolveWithNonPromise(cx, iterResultVal); - } - - // Step 6: If stream.[[state]] is "errored", return a promise rejected - // with stream.[[storedError]]. - if (unwrappedStream->errored()) { - Rooted storedError(cx, unwrappedStream->storedError()); - if (!cx->compartment()->wrap(cx, &storedError)) { - return nullptr; - } - return PromiseObject::unforgeableReject(cx, storedError); - } - - // Step 7: Assert: stream.[[state]] is "readable". - MOZ_ASSERT(unwrappedStream->readable()); - - // Step 8: Return ! stream.[[readableStreamController]].[[PullSteps]](). - Rooted unwrappedController( - cx, unwrappedStream->controller()); - return ReadableStreamControllerPullSteps(cx, unwrappedController); -} diff --git a/js/src/builtin/streams/ReadableStreamReader.h b/js/src/builtin/streams/ReadableStreamReader.h deleted file mode 100644 index 055ce827feb5..000000000000 --- a/js/src/builtin/streams/ReadableStreamReader.h +++ /dev/null @@ -1,154 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* ReadableStream readers and generic reader operations. */ - -#ifndef builtin_streams_ReadableStreamReader_h -#define builtin_streams_ReadableStreamReader_h - -#include "jstypes.h" // JS_PUBLIC_API -#include "js/Class.h" // JSClass, js::ClassSpec -#include "js/RootingAPI.h" // JS::Handle -#include "js/Value.h" // JS::{,Boolean,Object,Undefined}Value -#include "vm/JSObject.h" // JSObject::is -#include "vm/List.h" // js::ListObject -#include "vm/NativeObject.h" // js::NativeObject - -struct JS_PUBLIC_API JSContext; - -namespace js { - -class PromiseObject; -class ReadableStream; - -/** - * Tells whether or not read() result objects inherit from Object.prototype. - * Generally, they should do so only if the reader was created by author code. - * See . - */ -enum class ForAuthorCodeBool { No, Yes }; - -class ReadableStreamReader : public NativeObject { - public: - /** - * Memory layout of Stream Reader instances. - * - * See https://streams.spec.whatwg.org/#default-reader-internal-slots and - * https://streams.spec.whatwg.org/#byob-reader-internal-slots for details. - * - * Note that [[readRequests]] and [[readIntoRequests]] are treated the same - * in our implementation. - * - * Of the stored values, Stream and ClosedPromise might be - * cross-compartment wrapper wrappers. - * - * For Stream, this can happen if the Reader was created by applying a - * different compartment's ReadableStream.prototype.getReader method. - * - * For ClosedPromise, it can be caused by applying a different - * compartment's ReadableStream*Reader.prototype.releaseLock method. - * - * Requests is guaranteed to be in the same compartment as the Reader, but - * can contain wrapped request objects from other globals. - */ - enum Slots { - Slot_Stream, - Slot_Requests, - Slot_ClosedPromise, - Slot_ForAuthorCode, - SlotCount, - }; - - bool hasStream() const { return !getFixedSlot(Slot_Stream).isUndefined(); } - void setStream(JSObject* stream) { - setFixedSlot(Slot_Stream, JS::ObjectValue(*stream)); - } - void clearStream() { setFixedSlot(Slot_Stream, JS::UndefinedValue()); } - bool isClosed() { return !hasStream(); } - - /** - * Tells whether this reader was created by author code. - * - * This returns Yes for readers created using `stream.getReader()`, and No - * for readers created for the internal use of algorithms like - * `stream.tee()` and `new Response(stream)`. - * - * The standard does not have this field. Instead, eight algorithms take a - * forAuthorCode parameter, and a [[forAuthorCode]] field is part of each - * read request. But the behavior is always equivalent to treating readers - * created by author code as having a bit set on them. We implement it that - * way for simplicity. - */ - ForAuthorCodeBool forAuthorCode() const { - return getFixedSlot(Slot_ForAuthorCode).toBoolean() ? ForAuthorCodeBool::Yes - : ForAuthorCodeBool::No; - } - void setForAuthorCode(ForAuthorCodeBool value) { - setFixedSlot(Slot_ForAuthorCode, - JS::BooleanValue(value == ForAuthorCodeBool::Yes)); - } - - ListObject* requests() const { - return &getFixedSlot(Slot_Requests).toObject().as(); - } - void clearRequests() { setFixedSlot(Slot_Requests, JS::UndefinedValue()); } - - JSObject* closedPromise() const { - return &getFixedSlot(Slot_ClosedPromise).toObject(); - } - void setClosedPromise(JSObject* wrappedPromise) { - setFixedSlot(Slot_ClosedPromise, JS::ObjectValue(*wrappedPromise)); - } - - static const JSClass class_; -}; - -class ReadableStreamDefaultReader : public ReadableStreamReader { - public: - static bool constructor(JSContext* cx, unsigned argc, JS::Value* vp); - static const ClassSpec classSpec_; - static const JSClass class_; - static const ClassSpec protoClassSpec_; - static const JSClass protoClass_; -}; - -[[nodiscard]] extern ReadableStreamDefaultReader* -CreateReadableStreamDefaultReader(JSContext* cx, - JS::Handle unwrappedStream, - ForAuthorCodeBool forAuthorCode, - JS::Handle proto = nullptr); - -[[nodiscard]] extern JSObject* ReadableStreamReaderGenericCancel( - JSContext* cx, JS::Handle unwrappedReader, - JS::Handle reason); - -[[nodiscard]] extern bool ReadableStreamReaderGenericInitialize( - JSContext* cx, JS::Handle reader, - JS::Handle unwrappedStream, - ForAuthorCodeBool forAuthorCode); - -[[nodiscard]] extern bool ReadableStreamReaderGenericRelease( - JSContext* cx, JS::Handle unwrappedReader); - -[[nodiscard]] extern PromiseObject* ReadableStreamDefaultReaderRead( - JSContext* cx, JS::Handle unwrappedReader); - -} // namespace js - -template <> -inline bool JSObject::is() const { - return is(); -} - -namespace js { - -[[nodiscard]] extern JSObject* CreateReadableStreamBYOBReader( - JSContext* cx, JS::Handle unwrappedStream, - ForAuthorCodeBool forAuthorCode, JS::Handle proto = nullptr); - -} // namespace js - -#endif // builtin_streams_ReadableStreamReader_h diff --git a/js/src/builtin/streams/StreamAPI.cpp b/js/src/builtin/streams/StreamAPI.cpp deleted file mode 100644 index 8d88412b6be8..000000000000 --- a/js/src/builtin/streams/StreamAPI.cpp +++ /dev/null @@ -1,603 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Public and friend stream APIs for external use. */ - -#include "mozilla/Assertions.h" // MOZ_ASSERT{,_IF} - -#include // uint32_t, uintptr_t - -#include "jsfriendapi.h" // js::IsObjectInContextCompartment -#include "jstypes.h" // JS_{FRIEND,PUBLIC}_API - -#include "builtin/Stream.h" // js::ReadableByteStreamController{,Close}, js::ReadableStreamDefaultController{,Close}, js::StreamController -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "builtin/streams/ReadableStreamController.h" // js::CheckReadableStreamControllerCanCloseOrEnqueue -#include "builtin/streams/ReadableStreamDefaultControllerOperations.h" // js::ReadableStreamController{Error,GetDesiredSizeUnchecked}, js::SetUpReadableStreamDefaultControllerFromUnderlyingSource -#include "builtin/streams/ReadableStreamInternals.h" // js::ReadableStream{Cancel,FulfillReadOrReadIntoRequest,GetNumReadRequests,HasDefaultReader} -#include "builtin/streams/ReadableStreamOperations.h" // js::ReadableStreamTee -#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStream{,Default}Reader, js::ForAuthorCodeBool -#include "builtin/streams/StreamController.h" // js::StreamController -#include "gc/Zone.h" // JS::Zone -#include "js/Context.h" // js::AssertHeapIsIdle -#include "js/ErrorReport.h" // JS_ReportErrorNumberASCII -#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_NewUint8Array -#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* -#include "js/GCAPI.h" // JS::AutoCheckCannotGC, JS::AutoSuppressGCAnalysis -#include "js/Object.h" // JS::SetObjectISupports -#include "js/RootingAPI.h" // JS::{,Mutable}Handle, JS::Rooted -#include "js/Stream.h" // JS::ReadableStreamUnderlyingSource -#include "js/Value.h" // JS::{,Object,Undefined}Value -#include "vm/ArrayBufferViewObject.h" // js::ArrayBufferViewObject -#include "vm/JSContext.h" // JSContext, CHECK_THREAD -#include "vm/JSObject.h" // JSObject -#include "vm/PlainObject.h" // js::PlainObject -#include "vm/PromiseObject.h" // js::PromiseObject - -#include "builtin/streams/ReadableStreamReader-inl.h" // js::UnwrapStreamFromReader -#include "vm/Compartment-inl.h" // JS::Compartment::wrap, js::UnwrapAndDowncastObject -#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance -#include "vm/Realm-inl.h" // js::AutoRealm - -using js::ArrayBufferViewObject; -using js::AssertHeapIsIdle; -using js::AutoRealm; -using js::CheckReadableStreamControllerCanCloseOrEnqueue; -using js::ForAuthorCodeBool; -using js::GetErrorMessage; -using js::IsObjectInContextCompartment; -using js::NewBuiltinClassInstance; -using js::PlainObject; -using js::ReadableByteStreamController; -using js::ReadableByteStreamControllerClose; -using js::ReadableStream; -using js::ReadableStreamController; -using js::ReadableStreamControllerError; -using js::ReadableStreamControllerGetDesiredSizeUnchecked; -using js::ReadableStreamDefaultController; -using js::ReadableStreamDefaultControllerClose; -using js::ReadableStreamDefaultReader; -using js::ReadableStreamFulfillReadOrReadIntoRequest; -using js::ReadableStreamGetNumReadRequests; -using js::ReadableStreamHasDefaultReader; -using js::ReadableStreamReader; -using js::ReadableStreamTee; -using js::SetUpReadableStreamDefaultControllerFromUnderlyingSource; -using js::StreamController; -using js::UnwrapAndDowncastObject; -using js::UnwrapStreamFromReader; - -JS_PUBLIC_API JSObject* js::UnwrapReadableStream(JSObject* obj) { - return obj->maybeUnwrapIf(); -} - -JS_PUBLIC_API JSObject* JS::NewReadableDefaultStreamObject( - JSContext* cx, JS::Handle underlyingSource /* = nullptr */, - JS::Handle size /* = nullptr */, - double highWaterMark /* = 1 */, - JS::Handle proto /* = nullptr */) { - MOZ_ASSERT(!cx->zone()->isAtomsZone()); - AssertHeapIsIdle(); - CHECK_THREAD(cx); - cx->check(underlyingSource, size, proto); - MOZ_ASSERT(highWaterMark >= 0); - - // A copy of ReadableStream::constructor, with most of the - // argument-checking done implicitly by C++ type checking. - Rooted stream(cx, ReadableStream::create(cx)); - if (!stream) { - return nullptr; - } - Rooted sourceVal(cx); - if (underlyingSource) { - sourceVal.setObject(*underlyingSource); - } else { - JSObject* source = NewPlainObject(cx); - if (!source) { - return nullptr; - } - sourceVal.setObject(*source); - } - Rooted sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue()); - - if (!SetUpReadableStreamDefaultControllerFromUnderlyingSource( - cx, stream, sourceVal, highWaterMark, sizeVal)) { - return nullptr; - } - - return stream; -} - -JS_PUBLIC_API JSObject* JS::NewReadableExternalSourceStreamObject( - JSContext* cx, JS::ReadableStreamUnderlyingSource* underlyingSource, - void* nsISupportsObject_alreadyAddreffed /* = nullptr */, - Handle proto /* = nullptr */) { - MOZ_ASSERT(!cx->zone()->isAtomsZone()); - AssertHeapIsIdle(); - CHECK_THREAD(cx); - MOZ_ASSERT(underlyingSource); - MOZ_ASSERT((uintptr_t(underlyingSource) & 1) == 0, - "external underlying source pointers must be aligned"); - cx->check(proto); - - return ReadableStream::createExternalSourceStream( - cx, underlyingSource, nsISupportsObject_alreadyAddreffed, proto); -} - -JS_PUBLIC_API bool JS::IsReadableStream(JSObject* obj) { - return obj->canUnwrapAs(); -} - -JS_PUBLIC_API bool JS::IsReadableStreamReader(JSObject* obj) { - return obj->canUnwrapAs(); -} - -JS_PUBLIC_API bool JS::IsReadableStreamDefaultReader(JSObject* obj) { - return obj->canUnwrapAs(); -} - -template -[[nodiscard]] static T* APIUnwrapAndDowncast(JSContext* cx, JSObject* obj) { - cx->check(obj); - return UnwrapAndDowncastObject(cx, obj); -} - -JS_PUBLIC_API bool JS::ReadableStreamIsReadable(JSContext* cx, - Handle streamObj, - bool* result) { - ReadableStream* unwrappedStream = - APIUnwrapAndDowncast(cx, streamObj); - if (!unwrappedStream) { - return false; - } - - *result = unwrappedStream->readable(); - return true; -} - -JS_PUBLIC_API bool JS::ReadableStreamIsLocked(JSContext* cx, - Handle streamObj, - bool* result) { - ReadableStream* unwrappedStream = - APIUnwrapAndDowncast(cx, streamObj); - if (!unwrappedStream) { - return false; - } - - *result = unwrappedStream->locked(); - return true; -} - -JS_PUBLIC_API bool JS::ReadableStreamIsDisturbed(JSContext* cx, - Handle streamObj, - bool* result) { - ReadableStream* unwrappedStream = - APIUnwrapAndDowncast(cx, streamObj); - if (!unwrappedStream) { - return false; - } - - *result = unwrappedStream->disturbed(); - return true; -} - -JS_PUBLIC_API JSObject* JS::ReadableStreamCancel(JSContext* cx, - Handle streamObj, - Handle reason) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - cx->check(reason); - - Rooted unwrappedStream( - cx, APIUnwrapAndDowncast(cx, streamObj)); - if (!unwrappedStream) { - return nullptr; - } - - return js::ReadableStreamCancel(cx, unwrappedStream, reason); -} - -JS_PUBLIC_API bool JS::ReadableStreamGetMode(JSContext* cx, - Handle streamObj, - JS::ReadableStreamMode* mode) { - ReadableStream* unwrappedStream = - APIUnwrapAndDowncast(cx, streamObj); - if (!unwrappedStream) { - return false; - } - - *mode = unwrappedStream->mode(); - return true; -} - -JS_PUBLIC_API JSObject* JS::ReadableStreamGetReader( - JSContext* cx, Handle streamObj, ReadableStreamReaderMode mode) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - - Rooted unwrappedStream( - cx, APIUnwrapAndDowncast(cx, streamObj)); - if (!unwrappedStream) { - return nullptr; - } - - JSObject* result = CreateReadableStreamDefaultReader(cx, unwrappedStream, - ForAuthorCodeBool::No); - MOZ_ASSERT_IF(result, IsObjectInContextCompartment(result, cx)); - return result; -} - -JS_PUBLIC_API bool JS::ReadableStreamGetExternalUnderlyingSource( - JSContext* cx, Handle streamObj, - JS::ReadableStreamUnderlyingSource** source) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - - Rooted unwrappedStream( - cx, APIUnwrapAndDowncast(cx, streamObj)); - if (!unwrappedStream) { - return false; - } - - MOZ_ASSERT(unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource); - if (unwrappedStream->locked()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_LOCKED); - return false; - } - if (!unwrappedStream->readable()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, - "ReadableStreamGetExternalUnderlyingSource"); - return false; - } - - auto unwrappedController = - &unwrappedStream->controller()->as(); - unwrappedController->setSourceLocked(); - *source = unwrappedController->externalSource(); - return true; -} - -JS_PUBLIC_API bool JS::ReadableStreamReleaseExternalUnderlyingSource( - JSContext* cx, Handle streamObj) { - ReadableStream* unwrappedStream = - APIUnwrapAndDowncast(cx, streamObj); - if (!unwrappedStream) { - return false; - } - - MOZ_ASSERT(unwrappedStream->mode() == JS::ReadableStreamMode::ExternalSource); - MOZ_ASSERT(unwrappedStream->locked()); - MOZ_ASSERT(unwrappedStream->controller()->sourceLocked()); - unwrappedStream->controller()->clearSourceLocked(); - return true; -} - -JS_PUBLIC_API bool JS::ReadableStreamUpdateDataAvailableFromSource( - JSContext* cx, JS::Handle streamObj, uint32_t availableData) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - - Rooted unwrappedStream( - cx, APIUnwrapAndDowncast(cx, streamObj)); - if (!unwrappedStream) { - return false; - } - - // This is based on Streams spec 3.11.4.4. enqueue(chunk) steps 1-3 and - // 3.13.9. ReadableByteStreamControllerEnqueue(controller, chunk) steps - // 8-9. - // - // Adapted to handling updates signaled by the embedding for streams with - // external underlying sources. - // - // The remaining steps of those two functions perform checks and asserts - // that don't apply to streams with external underlying sources. - - Rooted unwrappedController( - cx, &unwrappedStream->controller()->as()); - - // Step 2: If this.[[closeRequested]] is true, throw a TypeError exception. - if (unwrappedController->closeRequested()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_CLOSED, "enqueue"); - return false; - } - - // Step 3: If this.[[controlledReadableStream]].[[state]] is not "readable", - // throw a TypeError exception. - if (!unwrappedController->stream()->readable()) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, - "enqueue"); - return false; - } - - unwrappedController->clearPullFlags(); - -#if DEBUG - uint32_t oldAvailableData = - unwrappedController->getFixedSlot(StreamController::Slot_TotalSize) - .toInt32(); -#endif // DEBUG - unwrappedController->setQueueTotalSize(availableData); - - // 3.139. ReadableByteStreamControllerEnqueue - // Step 8.a: If ! ReadableStreamGetNumReadRequests(stream) is 0, - // Reordered because for externally-sourced streams it applies regardless - // of reader type. - if (ReadableStreamGetNumReadRequests(unwrappedStream) == 0) { - return true; - } - - // Step 8: If ! ReadableStreamHasDefaultReader(stream) is true - bool hasDefaultReader; - if (!ReadableStreamHasDefaultReader(cx, unwrappedStream, &hasDefaultReader)) { - return false; - } - if (hasDefaultReader) { - // Step b: Otherwise, - // Step i: Assert: controller.[[queue]] is empty. - MOZ_ASSERT(oldAvailableData == 0); - - // Step ii: Let transferredView be - // ! Construct(%Uint8Array%, transferredBuffer, - // byteOffset, byteLength). - JSObject* viewObj = JS_NewUint8Array(cx, availableData); - if (!viewObj) { - return false; - } - Rooted transferredView( - cx, &viewObj->as()); - if (!transferredView) { - return false; - } - - JS::ReadableStreamUnderlyingSource* source = - unwrappedController->externalSource(); - - size_t bytesWritten; - { - AutoRealm ar(cx, unwrappedStream); - source->writeIntoReadRequestBuffer(cx, unwrappedStream, transferredView, - availableData, &bytesWritten); - } - - // Step iii: Perform ! ReadableStreamFulfillReadRequest(stream, - // transferredView, - // false). - Rooted chunk(cx, ObjectValue(*transferredView)); - if (!ReadableStreamFulfillReadOrReadIntoRequest(cx, unwrappedStream, chunk, - false)) { - return false; - } - - unwrappedController->setQueueTotalSize(availableData - bytesWritten); - } else { - // Step 9: Otherwise, if ! ReadableStreamHasBYOBReader(stream) is true, - // [...] - // (Omitted. BYOB readers are not implemented.) - - // Step 10: Otherwise, - // Step a: Assert: ! IsReadableStreamLocked(stream) is false. - MOZ_ASSERT(!unwrappedStream->locked()); - - // Step b: Perform ! ReadableByteStreamControllerEnqueueChunkToQueue( - // controller, transferredBuffer, byteOffset, byteLength). - // (Not needed for external underlying sources.) - } - - return true; -} - -JS_PUBLIC_API void JS::ReadableStreamReleaseCCObject(JSObject* streamObj) { - MOZ_ASSERT(JS::IsReadableStream(streamObj)); - JS::SetObjectISupports(streamObj, nullptr); -} - -JS_PUBLIC_API bool JS::ReadableStreamTee(JSContext* cx, - Handle streamObj, - MutableHandle branch1Obj, - MutableHandle branch2Obj) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - - Rooted unwrappedStream( - cx, APIUnwrapAndDowncast(cx, streamObj)); - if (!unwrappedStream) { - return false; - } - - Rooted branch1Stream(cx); - Rooted branch2Stream(cx); - if (!ReadableStreamTee(cx, unwrappedStream, false, &branch1Stream, - &branch2Stream)) { - return false; - } - - branch1Obj.set(branch1Stream); - branch2Obj.set(branch2Stream); - return true; -} - -JS_PUBLIC_API bool JS::ReadableStreamGetDesiredSize(JSContext* cx, - JSObject* streamObj, - bool* hasValue, - double* value) { - ReadableStream* unwrappedStream = - APIUnwrapAndDowncast(cx, streamObj); - if (!unwrappedStream) { - return false; - } - - if (unwrappedStream->errored()) { - *hasValue = false; - return true; - } - - *hasValue = true; - - if (unwrappedStream->closed()) { - *value = 0; - return true; - } - - *value = ReadableStreamControllerGetDesiredSizeUnchecked( - unwrappedStream->controller()); - return true; -} - -JS_PUBLIC_API bool JS::ReadableStreamClose(JSContext* cx, - Handle streamObj) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - - Rooted unwrappedStream( - cx, APIUnwrapAndDowncast(cx, streamObj)); - if (!unwrappedStream) { - return false; - } - - Rooted unwrappedControllerObj( - cx, unwrappedStream->controller()); - if (!CheckReadableStreamControllerCanCloseOrEnqueue( - cx, unwrappedControllerObj, "close")) { - return false; - } - - if (unwrappedControllerObj->is()) { - Rooted unwrappedController(cx); - unwrappedController = - &unwrappedControllerObj->as(); - return ReadableStreamDefaultControllerClose(cx, unwrappedController); - } - - Rooted unwrappedController(cx); - unwrappedController = - &unwrappedControllerObj->as(); - return ReadableByteStreamControllerClose(cx, unwrappedController); -} - -JS_PUBLIC_API bool JS::ReadableStreamEnqueue(JSContext* cx, - Handle streamObj, - Handle chunk) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - cx->check(chunk); - - Rooted unwrappedStream( - cx, APIUnwrapAndDowncast(cx, streamObj)); - if (!unwrappedStream) { - return false; - } - - if (unwrappedStream->mode() != JS::ReadableStreamMode::Default) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, - "JS::ReadableStreamEnqueue"); - return false; - } - - Rooted unwrappedController(cx); - unwrappedController = - &unwrappedStream->controller()->as(); - - MOZ_ASSERT(!unwrappedController->closeRequested()); - MOZ_ASSERT(unwrappedStream->readable()); - - return ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, chunk); -} - -JS_PUBLIC_API bool JS::ReadableStreamError(JSContext* cx, - Handle streamObj, - Handle error) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - cx->check(error); - - Rooted unwrappedStream( - cx, APIUnwrapAndDowncast(cx, streamObj)); - if (!unwrappedStream) { - return false; - } - - Rooted unwrappedController( - cx, unwrappedStream->controller()); - return ReadableStreamControllerError(cx, unwrappedController, error); -} - -JS_PUBLIC_API bool JS::ReadableStreamReaderIsClosed(JSContext* cx, - Handle readerObj, - bool* result) { - Rooted unwrappedReader( - cx, APIUnwrapAndDowncast(cx, readerObj)); - if (!unwrappedReader) { - return false; - } - - *result = unwrappedReader->isClosed(); - return true; -} - -JS_PUBLIC_API bool JS::ReadableStreamReaderCancel(JSContext* cx, - Handle readerObj, - Handle reason) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - cx->check(reason); - - Rooted unwrappedReader( - cx, APIUnwrapAndDowncast(cx, readerObj)); - if (!unwrappedReader) { - return false; - } - MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No, - "C++ code should not touch readers created by scripts"); - - return ReadableStreamReaderGenericCancel(cx, unwrappedReader, reason); -} - -JS_PUBLIC_API bool JS::ReadableStreamReaderReleaseLock( - JSContext* cx, Handle readerObj) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - - Rooted unwrappedReader( - cx, APIUnwrapAndDowncast(cx, readerObj)); - if (!unwrappedReader) { - return false; - } - MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No, - "C++ code should not touch readers created by scripts"); - -#ifdef DEBUG - Rooted unwrappedStream( - cx, UnwrapStreamFromReader(cx, unwrappedReader)); - if (!unwrappedStream) { - return false; - } - MOZ_ASSERT(ReadableStreamGetNumReadRequests(unwrappedStream) == 0); -#endif // DEBUG - - return ReadableStreamReaderGenericRelease(cx, unwrappedReader); -} - -JS_PUBLIC_API JSObject* JS::ReadableStreamDefaultReaderRead( - JSContext* cx, Handle readerObj) { - AssertHeapIsIdle(); - CHECK_THREAD(cx); - - Rooted unwrappedReader( - cx, APIUnwrapAndDowncast(cx, readerObj)); - if (!unwrappedReader) { - return nullptr; - } - MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No, - "C++ code should not touch readers created by scripts"); - - return js::ReadableStreamDefaultReaderRead(cx, unwrappedReader); -} diff --git a/js/src/builtin/streams/StreamController-inl.h b/js/src/builtin/streams/StreamController-inl.h deleted file mode 100644 index c19bb38c8fb7..000000000000 --- a/js/src/builtin/streams/StreamController-inl.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Base stream controller inlines. */ - -#ifndef builtin_streams_StreamController_inl_h -#define builtin_streams_StreamController_inl_h - -#include "builtin/streams/StreamController.h" // js::StreamController -#include "builtin/streams/ReadableStreamController.h" // js::Readable{ByteStream,StreamDefault}Controller -#include "vm/JSObject.h" // JSObject - -template <> -inline bool JSObject::is() const { - return is() || - is(); -} - -#endif // builtin_streams_ReadableStreamController_inl_h diff --git a/js/src/builtin/streams/StreamController.h b/js/src/builtin/streams/StreamController.h deleted file mode 100644 index 4986421ab0d5..000000000000 --- a/js/src/builtin/streams/StreamController.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -/* Base class for readable and writable stream controllers. */ - -#ifndef builtin_streams_StreamController_h -#define builtin_streams_StreamController_h - -#include "js/Value.h" // JS::Value, JS::NumberValue -#include "vm/JSObject.h" // JSObject -#include "vm/List.h" // js::ListObject -#include "vm/NativeObject.h" // js::NativeObject - -namespace js { - -/** - * Common base class of both readable and writable stream controllers. - */ -class StreamController : public NativeObject { - public: - /** - * Memory layout for stream controllers. - * - * Both ReadableStreamDefaultController and ReadableByteStreamController - * are queue containers and must have these slots at identical offsets. - * - * The queue is guaranteed to be in the same compartment as the container, - * but might contain wrappers for objects from other compartments. - */ - enum Slots { Slot_Queue, Slot_TotalSize, SlotCount }; - - ListObject* queue() const { - return &getFixedSlot(Slot_Queue).toObject().as(); - } - double queueTotalSize() const { - return getFixedSlot(Slot_TotalSize).toNumber(); - } - void setQueueTotalSize(double size) { - setFixedSlot(Slot_TotalSize, JS::NumberValue(size)); - } -}; - -} // namespace js - -template <> -inline bool JSObject::is() const; - -#endif // builtin_streams_StreamController_h diff --git a/js/src/builtin/streams/TeeState.cpp b/js/src/builtin/streams/TeeState.cpp deleted file mode 100644 index b5faa96f8c89..000000000000 --- a/js/src/builtin/streams/TeeState.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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 teeing state. */ - -#include "builtin/streams/TeeState.h" - -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "js/Class.h" // JSClass, JSCLASS_HAS_RESERVED_SLOTS -#include "js/RootingAPI.h" // JS::Handle, JS::Rooted -#include "vm/Compartment.h" // JS::Compartment -#include "vm/JSContext.h" // JSContext -#include "vm/PromiseObject.h" // js::PromiseObject - -#include "vm/JSObject-inl.h" // js::NewBuiltinClassInstance - -using js::ReadableStream; -using js::TeeState; - -using JS::Handle; -using JS::Int32Value; -using JS::ObjectValue; -using JS::Rooted; - -/* static */ TeeState* TeeState::create( - JSContext* cx, Handle unwrappedStream) { - Rooted state(cx, NewBuiltinClassInstance(cx)); - if (!state) { - return nullptr; - } - - Rooted cancelPromise( - cx, PromiseObject::createSkippingExecutor(cx)); - if (!cancelPromise) { - return nullptr; - } - - state->setFixedSlot(Slot_Flags, Int32Value(0)); - state->setFixedSlot(Slot_CancelPromise, ObjectValue(*cancelPromise)); - Rooted wrappedStream(cx, unwrappedStream); - if (!cx->compartment()->wrap(cx, &wrappedStream)) { - return nullptr; - } - state->setFixedSlot(Slot_Stream, JS::ObjectValue(*wrappedStream)); - - return state; -} - -const JSClass TeeState::class_ = {"TeeState", - JSCLASS_HAS_RESERVED_SLOTS(SlotCount)}; diff --git a/js/src/builtin/streams/TeeState.h b/js/src/builtin/streams/TeeState.h deleted file mode 100644 index beb42ff8e675..000000000000 --- a/js/src/builtin/streams/TeeState.h +++ /dev/null @@ -1,155 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - * This Source Code Form is subject to the terms of the Mozilla Public - * 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 teeing state. */ - -#ifndef builtin_streams_TeeState_h -#define builtin_streams_TeeState_h - -#include "mozilla/Assertions.h" // MOZ_ASSERT - -#include // uint32_t - -#include "builtin/streams/ReadableStreamController.h" // js::ReadableStreamDefaultController -#include "js/Class.h" // JSClass -#include "js/Value.h" // JS::{Int32,Object}Value -#include "vm/NativeObject.h" // js::NativeObject -#include "vm/PromiseObject.h" // js::PromiseObject - -namespace js { - -/** - * TeeState objects implement the local variables in Streams spec 3.3.9 - * ReadableStreamTee, which are accessed by several algorithms. - */ -class TeeState : public NativeObject { - public: - /** - * Memory layout for TeeState instances. - * - * The Reason1 and Reason2 slots store opaque values, which might be - * wrapped objects from other compartments. Since we don't treat them as - * objects in Streams-specific code, we don't have to worry about that - * apart from ensuring that the values are properly wrapped before storing - * them. - * - * CancelPromise is always created in TeeState::create below, so is - * guaranteed to be in the same compartment as the TeeState instance - * itself. - * - * Stream can be from another compartment. It is automatically wrapped - * before storing it and unwrapped upon retrieval. That means that - * TeeState consumers need to be able to deal with unwrapped - * ReadableStream instances from non-current compartments. - * - * Branch1 and Branch2 are always created in the same compartment as the - * TeeState instance, so cannot be from another compartment. - */ - enum Slots { - Slot_Flags = 0, - Slot_Reason1, - Slot_Reason2, - Slot_CancelPromise, - Slot_Stream, - Slot_Branch1, - Slot_Branch2, - SlotCount - }; - - private: - enum Flags { - Flag_Reading = 1 << 0, - Flag_Canceled1 = 1 << 1, - Flag_Canceled2 = 1 << 2, - - // No internal user ever sets the cloneForBranch2 flag to true, and the - // streams spec doesn't expose a way to set the flag to true. So for the - // moment, don't even reserve flag-space to store it. - // Flag_CloneForBranch2 = 1 << 3, - }; - uint32_t flags() const { return getFixedSlot(Slot_Flags).toInt32(); } - void setFlags(uint32_t flags) { - setFixedSlot(Slot_Flags, JS::Int32Value(flags)); - } - - public: - static const JSClass class_; - - // Consistent with not even storing this always-false flag, expose it as - // compile-time constant false. - static constexpr bool cloneForBranch2() { return false; } - - bool reading() const { return flags() & Flag_Reading; } - void setReading() { - MOZ_ASSERT(!(flags() & Flag_Reading)); - setFlags(flags() | Flag_Reading); - } - void unsetReading() { - MOZ_ASSERT(flags() & Flag_Reading); - setFlags(flags() & ~Flag_Reading); - } - - bool canceled1() const { return flags() & Flag_Canceled1; } - void setCanceled1(HandleValue reason) { - MOZ_ASSERT(!(flags() & Flag_Canceled1)); - setFlags(flags() | Flag_Canceled1); - setFixedSlot(Slot_Reason1, reason); - } - - bool canceled2() const { return flags() & Flag_Canceled2; } - void setCanceled2(HandleValue reason) { - MOZ_ASSERT(!(flags() & Flag_Canceled2)); - setFlags(flags() | Flag_Canceled2); - setFixedSlot(Slot_Reason2, reason); - } - - JS::Value reason1() const { - MOZ_ASSERT(canceled1()); - return getFixedSlot(Slot_Reason1); - } - - JS::Value reason2() const { - MOZ_ASSERT(canceled2()); - return getFixedSlot(Slot_Reason2); - } - - PromiseObject* cancelPromise() { - return &getFixedSlot(Slot_CancelPromise).toObject().as(); - } - - ReadableStreamDefaultController* branch1() { - ReadableStreamDefaultController* controller = - &getFixedSlot(Slot_Branch1) - .toObject() - .as(); - MOZ_ASSERT(controller->isTeeBranch1()); - return controller; - } - void setBranch1(ReadableStreamDefaultController* controller) { - MOZ_ASSERT(controller->isTeeBranch1()); - setFixedSlot(Slot_Branch1, JS::ObjectValue(*controller)); - } - - ReadableStreamDefaultController* branch2() { - ReadableStreamDefaultController* controller = - &getFixedSlot(Slot_Branch2) - .toObject() - .as(); - MOZ_ASSERT(controller->isTeeBranch2()); - return controller; - } - void setBranch2(ReadableStreamDefaultController* controller) { - MOZ_ASSERT(controller->isTeeBranch2()); - setFixedSlot(Slot_Branch2, JS::ObjectValue(*controller)); - } - - static TeeState* create(JSContext* cx, - Handle unwrappedStream); -}; - -} // namespace js - -#endif // builtin_streams_TeeState_h diff --git a/js/src/fuzz-tests/tests.cpp b/js/src/fuzz-tests/tests.cpp index 7ae58726fb9d..f6002bd739c9 100644 --- a/js/src/fuzz-tests/tests.cpp +++ b/js/src/fuzz-tests/tests.cpp @@ -33,7 +33,7 @@ static const JSClass* getGlobalClass() { static JSObject* jsfuzz_createGlobal(JSContext* cx, JSPrincipals* principals) { /* Create the global object. */ JS::RealmOptions options; - options.creationOptions().setStreamsEnabled(true).setWeakRefsEnabled( + options.creationOptions().setWeakRefsEnabled( JS::WeakRefSpecifier::EnabledWithCleanupSome); return JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook, options); diff --git a/js/src/jsapi-tests/moz.build b/js/src/jsapi-tests/moz.build index 886c0e20867d..97011f0e4330 100644 --- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -162,11 +162,6 @@ if not CONFIG["JS_CODEGEN_NONE"]: "testsJit.cpp", ] -if CONFIG["MOZ_JS_STREAMS"]: - UNIFIED_SOURCES += [ - "testReadableStream.cpp", - ] - if CONFIG["NIGHTLY_BUILD"]: # The Error interceptor only exists on Nightly. UNIFIED_SOURCES += [ diff --git a/js/src/jsapi-tests/testReadableStream.cpp b/js/src/jsapi-tests/testReadableStream.cpp deleted file mode 100644 index edae0826589e..000000000000 --- a/js/src/jsapi-tests/testReadableStream.cpp +++ /dev/null @@ -1,1195 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: set ts=8 sts=2 et sw=2 tw=80: - */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -#include "jsapi.h" -#include "jsfriendapi.h" -#include "js/experimental/TypedData.h" // JS_GetArrayBufferViewData, JS_IsUint8Array -#include "js/PropertyAndElement.h" // JS_GetProperty, JS_HasProperty, JS_SetProperty -#include "js/Stream.h" -#include "jsapi-tests/tests.h" - -using namespace JS; - -char testBufferData[] = - "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - -struct StubExternalUnderlyingSource - : public JS::ReadableStreamUnderlyingSource { - void* buffer = testBufferData; - bool dataRequestCBCalled = false; - bool writeIntoRequestBufferCBCalled = false; - bool cancelStreamCBCalled = false; - Value cancelStreamReason; - bool streamClosedCBCalled = false; - Value streamClosedReason; - bool streamErroredCBCalled = false; - Value streamErroredReason; - bool finalizeStreamCBCalled = false; - void* finalizedStreamUnderlyingSource; - - static StubExternalUnderlyingSource instance; - - void requestData(JSContext* cx, HandleObject stream, - size_t desiredSize) override { - js::AssertSameCompartment(cx, stream); - MOZ_RELEASE_ASSERT(!dataRequestCBCalled, "Invalid test setup"); - dataRequestCBCalled = true; - } - - void writeIntoReadRequestBuffer(JSContext* cx, HandleObject stream, - JS::HandleObject chunk, size_t length, - size_t* bytesWritten) override { - js::AssertSameCompartment(cx, stream); - MOZ_RELEASE_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup"); - writeIntoRequestBufferCBCalled = true; - - MOZ_RELEASE_ASSERT(this == &StubExternalUnderlyingSource::instance); - MOZ_RELEASE_ASSERT(StubExternalUnderlyingSource::instance.buffer == - testBufferData); - MOZ_RELEASE_ASSERT(length <= sizeof(testBufferData)); - - JS::AutoCheckCannotGC noGC; - bool isSharedMemory; - - void* buffer = JS_GetArrayBufferViewData(chunk, &isSharedMemory, noGC); - MOZ_ASSERT(!isSharedMemory); - - memcpy(buffer, testBufferData, length); - *bytesWritten = length; - } - - Value cancel(JSContext* cx, HandleObject stream, - HandleValue reason) override { - js::AssertSameCompartment(cx, stream); - js::AssertSameCompartment(cx, reason); - MOZ_RELEASE_ASSERT(!cancelStreamCBCalled, "Invalid test setup"); - cancelStreamCBCalled = true; - cancelStreamReason = reason; - return reason; - } - - void onClosed(JSContext* cx, HandleObject stream) override { - js::AssertSameCompartment(cx, stream); - MOZ_RELEASE_ASSERT(!streamClosedCBCalled, "Invalid test setup"); - streamClosedCBCalled = true; - } - - void onErrored(JSContext* cx, HandleObject stream, - HandleValue reason) override { - js::AssertSameCompartment(cx, stream); - js::AssertSameCompartment(cx, reason); - MOZ_RELEASE_ASSERT(!streamErroredCBCalled, "Invalid test setup"); - streamErroredCBCalled = true; - streamErroredReason = reason; - } - - void finalize() override { - MOZ_RELEASE_ASSERT(!finalizeStreamCBCalled, "Invalid test setup"); - finalizeStreamCBCalled = true; - finalizedStreamUnderlyingSource = this; - } - - void reset() { - dataRequestCBCalled = false; - writeIntoRequestBufferCBCalled = false; - cancelStreamReason = UndefinedValue(); - cancelStreamCBCalled = false; - streamClosedCBCalled = false; - streamErroredCBCalled = false; - finalizeStreamCBCalled = false; - } -}; - -StubExternalUnderlyingSource StubExternalUnderlyingSource::instance; - -static_assert(MOZ_ALIGNOF(StubExternalUnderlyingSource) > 1, - "UnderlyingSource pointers must not have the low bit set"); - -static JSObject* NewDefaultStream(JSContext* cx, HandleObject source = nullptr, - HandleFunction size = nullptr, - double highWaterMark = 1, - HandleObject proto = nullptr) { - RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size, - highWaterMark, proto)); - if (stream) { - MOZ_RELEASE_ASSERT(IsReadableStream(stream)); - } - return stream; -} - -static bool GetIterResult(JSContext* cx, HandleObject promise, - MutableHandleValue value, bool* done) { - RootedObject iterResult(cx, &GetPromiseResult(promise).toObject()); - - bool found; - if (!JS_HasProperty(cx, iterResult, "value", &found)) { - return false; - } - MOZ_RELEASE_ASSERT(found); - if (!JS_HasProperty(cx, iterResult, "done", &found)) { - return false; - } - MOZ_RELEASE_ASSERT(found); - - RootedValue doneVal(cx); - if (!JS_GetProperty(cx, iterResult, "value", value)) { - return false; - } - if (!JS_GetProperty(cx, iterResult, "done", &doneVal)) { - return false; - } - - *done = doneVal.toBoolean(); - if (*done) { - MOZ_RELEASE_ASSERT(value.isUndefined()); - } - - return true; -} - -static JSObject* GetReadChunk(JSContext* cx, HandleObject readRequest) { - MOZ_RELEASE_ASSERT(GetPromiseState(readRequest) == PromiseState::Fulfilled); - RootedValue resultVal(cx, GetPromiseResult(readRequest)); - MOZ_RELEASE_ASSERT(resultVal.isObject()); - RootedObject result(cx, &resultVal.toObject()); - RootedValue chunkVal(cx); - JS_GetProperty(cx, result, "value", &chunkVal); - return &chunkVal.toObject(); -} - -struct StreamTestFixture : public JSAPITest { - virtual ~StreamTestFixture() {} -}; - -BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_NewReadableStream) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - ReadableStreamMode mode; - CHECK(ReadableStreamGetMode(cx, stream, &mode)); - CHECK(mode == ReadableStreamMode::Default); - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_NewReadableStream) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamGetReaderDefault) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - CHECK(IsReadableStreamDefaultReader(reader)); - bool locked; - CHECK(ReadableStreamIsLocked(cx, stream, &locked)); - CHECK(locked); - bool closed; - CHECK(ReadableStreamReaderIsClosed(cx, reader, &closed)); - CHECK(!closed); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamGetReaderDefault) - -BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamTee) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject leftStream(cx); - RootedObject rightStream(cx); - CHECK(ReadableStreamTee(cx, stream, &leftStream, &rightStream)); - bool locked; - CHECK(ReadableStreamIsLocked(cx, stream, &locked)); - CHECK(locked); - CHECK(leftStream); - CHECK(IsReadableStream(leftStream)); - CHECK(rightStream); - CHECK(IsReadableStream(rightStream)); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamTee) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamEnqueue) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject chunk(cx, JS_NewPlainObject(cx)); - CHECK(chunk); - RootedValue chunkVal(cx, ObjectValue(*chunk)); - CHECK(ReadableStreamEnqueue(cx, stream, chunkVal)); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamEnqueue) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamDefaultReaderRead) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - - RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - RootedObject chunk(cx, JS_NewPlainObject(cx)); - CHECK(chunk); - RootedValue chunkVal(cx, ObjectValue(*chunk)); - CHECK(ReadableStreamEnqueue(cx, stream, chunkVal)); - - CHECK(GetReadChunk(cx, request) == chunk); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamDefaultReaderRead) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamDefaultReaderClose) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - - RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - CHECK(ReadableStreamClose(cx, stream)); - - bool done; - RootedValue value(cx); - CHECK(GetPromiseState(request) == PromiseState::Fulfilled); - CHECK(GetIterResult(cx, request, &value, &done)); - CHECK(value.isUndefined()); - CHECK(done); - - // The callbacks are only invoked for external streams. - CHECK(!StubExternalUnderlyingSource::instance.streamClosedCBCalled); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamDefaultReaderClose) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamDefaultReaderError) { - StubExternalUnderlyingSource::instance.reset(); - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - - RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - bool locked; - CHECK(ReadableStreamIsLocked(cx, stream, &locked)); - CHECK(locked); - bool readable; - CHECK(ReadableStreamIsReadable(cx, stream, &readable)); - CHECK(readable); - RootedValue error(cx, Int32Value(42)); - CHECK(ReadableStreamError(cx, stream, error)); - - CHECK(GetPromiseState(request) == PromiseState::Rejected); - RootedValue reason(cx, GetPromiseResult(request)); - CHECK(reason.isInt32()); - CHECK(reason.toInt32() == 42); - - // The callbacks are only invoked for external streams. - CHECK(!StubExternalUnderlyingSource::instance.streamErroredCBCalled); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamDefaultReaderError) - -static JSObject* NewExternalSourceStream( - JSContext* cx, ReadableStreamUnderlyingSource* source) { - RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, source)); - if (stream) { - MOZ_RELEASE_ASSERT(IsReadableStream(stream)); - } - return stream; -} - -static JSObject* NewExternalSourceStream(JSContext* cx) { - return NewExternalSourceStream(cx, &StubExternalUnderlyingSource::instance); -} - -BEGIN_FIXTURE_TEST( - StreamTestFixture, - testReadableStream_CreateReadableByteStreamWithExternalSource) { - StubExternalUnderlyingSource::instance.reset(); - - RootedObject stream(cx, NewExternalSourceStream(cx)); - CHECK(stream); - ReadableStreamMode mode; - CHECK(ReadableStreamGetMode(cx, stream, &mode)); - CHECK(mode == ReadableStreamMode::ExternalSource); - ReadableStreamUnderlyingSource* underlyingSource; - CHECK( - ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource)); - CHECK(underlyingSource == &StubExternalUnderlyingSource::instance); - bool locked; - CHECK(ReadableStreamIsLocked(cx, stream, &locked)); - CHECK(locked); - CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_CreateReadableByteStreamWithExternalSource) - -BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceCancel) { - StubExternalUnderlyingSource::instance.reset(); - - RootedObject stream(cx, NewExternalSourceStream(cx)); - CHECK(stream); - RootedValue reason(cx, Int32Value(42)); - CHECK(ReadableStreamCancel(cx, stream, reason)); - CHECK(StubExternalUnderlyingSource::instance.cancelStreamCBCalled); - CHECK(StubExternalUnderlyingSource::instance.cancelStreamReason == reason); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceCancel) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ExternalSourceGetReader) { - StubExternalUnderlyingSource::instance.reset(); - - RootedObject stream(cx, NewExternalSourceStream(cx)); - CHECK(stream); - - RootedValue streamVal(cx, ObjectValue(*stream)); - CHECK(JS_SetProperty(cx, global, "stream", streamVal)); - RootedValue rval(cx); - EVAL("stream.getReader()", &rval); - CHECK(rval.isObject()); - RootedObject reader(cx, &rval.toObject()); - CHECK(IsReadableStreamDefaultReader(reader)); - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ExternalSourceGetReader) - -enum class CompartmentMode { - Same, - Cross, -}; - -struct ReadFromExternalSourceFixture : public StreamTestFixture { - virtual ~ReadFromExternalSourceFixture() {} - - // On success, streamGlobal is a global object (not a wrapper) - // and stream is in the same compartment as cx (it may be a CCW). - bool createExternalSourceStream(CompartmentMode compartmentMode, - MutableHandleObject streamGlobal, - MutableHandleObject stream) { - if (compartmentMode == CompartmentMode::Same) { - streamGlobal.set(global); - stream.set(NewExternalSourceStream(cx)); - if (!stream) { - return false; - } - } else { - RootedObject savedGlobal(cx, global); - streamGlobal.set(createGlobal()); - if (!streamGlobal) { - return false; - } - global = savedGlobal; - - { - JSAutoRealm ar(cx, streamGlobal); - stream.set(NewExternalSourceStream(cx)); - if (!stream) { - return false; - } - } - if (!JS_WrapObject(cx, stream)) { - return false; - } - } - return true; - } - - bool readWithoutDataAvailable(CompartmentMode compartmentMode, - const char* evalSrc, const char* evalSrc2, - uint32_t writtenLength) { - StubExternalUnderlyingSource::instance.reset(); - definePrint(); - - // Create the stream. - RootedObject streamGlobal(cx); - RootedObject stream(cx); // can be a wrapper - CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream)); - js::RunJobs(cx); - - // GetExternalUnderlyingSource locks the stream. - ReadableStreamUnderlyingSource* underlyingSource; - CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, - &underlyingSource)); - CHECK(underlyingSource == &StubExternalUnderlyingSource::instance); - bool locked; - CHECK(ReadableStreamIsLocked(cx, stream, &locked)); - CHECK(locked); - CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); - - // Run caller-supplied JS code to read from the stream. - RootedValue streamVal(cx, ObjectValue(*stream)); - CHECK(JS_SetProperty(cx, global, "stream", streamVal)); - RootedValue rval(cx); - EVAL(evalSrc, &rval); - CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled); - CHECK( - !StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled); - CHECK(rval.isObject()); - RootedObject unwrappedPromise(cx, - js::CheckedUnwrapStatic(&rval.toObject())); - CHECK(unwrappedPromise); - CHECK(IsPromiseObject(unwrappedPromise)); - CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Pending); - - // Stream in some data; this resolves the read() result promise. - size_t length = sizeof(testBufferData); - CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length)); - CHECK( - StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled); - CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled); - RootedObject chunk(cx); - { - JSAutoRealm ar(cx, unwrappedPromise); - RootedValue iterVal(cx); - bool done; - if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) { - return false; - } - CHECK(!done); - chunk = &iterVal.toObject(); - } - CHECK(JS_WrapObject(cx, &chunk)); - CHECK(JS_IsUint8Array(chunk)); - - { - JS::AutoCheckCannotGC noGC(cx); - bool dummy; - void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC); - CHECK(!memcmp(buffer, testBufferData, writtenLength)); - } - - // Check the callbacks fired by calling read() again. - StubExternalUnderlyingSource::instance.dataRequestCBCalled = false; - StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled = - false; - EVAL(evalSrc2, &rval); - CHECK(StubExternalUnderlyingSource::instance.dataRequestCBCalled); - CHECK( - !StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled); - - return true; - } - - bool readWithDataAvailable(CompartmentMode compartmentMode, - const char* evalSrc, uint32_t writtenLength) { - StubExternalUnderlyingSource::instance.reset(); - definePrint(); - - // Create a stream. - RootedObject streamGlobal(cx); - RootedObject stream(cx); - CHECK(createExternalSourceStream(compartmentMode, &streamGlobal, &stream)); - - // Getting the underlying source locks the stream. - ReadableStreamUnderlyingSource* underlyingSource; - CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, - &underlyingSource)); - CHECK(underlyingSource == &StubExternalUnderlyingSource::instance); - bool locked; - CHECK(ReadableStreamIsLocked(cx, stream, &locked)); - CHECK(locked); - CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); - - // Make some data available. - size_t length = sizeof(testBufferData); - CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, length)); - - // Read from the stream. - RootedValue streamVal(cx, ObjectValue(*stream)); - CHECK(JS_SetProperty(cx, global, "stream", streamVal)); - RootedValue rval(cx); - EVAL(evalSrc, &rval); - CHECK( - StubExternalUnderlyingSource::instance.writeIntoRequestBufferCBCalled); - CHECK(rval.isObject()); - RootedObject unwrappedPromise(cx, - js::CheckedUnwrapStatic(&rval.toObject())); - CHECK(unwrappedPromise); - CHECK(IsPromiseObject(unwrappedPromise)); - CHECK(GetPromiseState(unwrappedPromise) == PromiseState::Fulfilled); - RootedObject chunk(cx); - { - JSAutoRealm ar(cx, unwrappedPromise); - RootedValue iterVal(cx); - bool done; - if (!GetIterResult(cx, unwrappedPromise, &iterVal, &done)) { - return false; - } - CHECK(!done); - chunk = &iterVal.toObject(); - } - CHECK(JS_WrapObject(cx, &chunk)); - CHECK(JS_IsUint8Array(chunk)); - - { - JS::AutoCheckCannotGC noGC(cx); - bool dummy; - void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC); - CHECK(!memcmp(buffer, testBufferData, writtenLength)); - } - - return true; - } -}; - -BEGIN_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable) { - return readWithoutDataAvailable(CompartmentMode::Same, - "r = stream.getReader(); r.read()", - "r.read()", sizeof(testBufferData)); -} -END_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable) - -BEGIN_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1) { - // Scenario 1: The stream and reader are both in the same compartment, but - // ReadableStreamUpdateDataAvailableFromSource is applied to a wrapper. - return readWithoutDataAvailable(CompartmentMode::Cross, - "r = stream.getReader(); r.read()", - "r.read()", sizeof(testBufferData)); -} -END_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment1) - -BEGIN_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2) { - // Scenario 2: The stream and reader are in the same compartment, but a - // `read` method from another compartment is used on the reader. - return readWithoutDataAvailable( - CompartmentMode::Cross, - "r = stream.getReader(); read = new " - "ReadableStream({start(){}}).getReader().read; read.call(r)", - "read.call(r)", sizeof(testBufferData)); -} -END_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment2) - -BEGIN_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3) { - // Scenario 3: The stream and reader are in different compartments. - return readWithoutDataAvailable( - CompartmentMode::Cross, - "r = ReadableStream.prototype.getReader.call(stream); r.read()", - "r.read()", sizeof(testBufferData)); -} -END_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable_CrossCompartment3) - -BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceCloseWithPendingRead) { - CHECK(readWithoutDataAvailable(CompartmentMode::Same, - "r = stream.getReader(); request0 = r.read(); " - "request1 = r.read(); request0", - "r.read()", sizeof(testBufferData))); - - RootedValue val(cx); - CHECK(JS_GetProperty(cx, global, "request1", &val)); - CHECK(val.isObject()); - RootedObject request(cx, &val.toObject()); - CHECK(IsPromiseObject(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - CHECK(JS_GetProperty(cx, global, "stream", &val)); - RootedObject stream(cx, &val.toObject()); - ReadableStreamClose(cx, stream); - - val = GetPromiseResult(request); - CHECK(val.isObject()); - RootedObject result(cx, &val.toObject()); - - JS_GetProperty(cx, result, "done", &val); - CHECK(val.isBoolean()); - CHECK(val.toBoolean() == true); - - JS_GetProperty(cx, result, "value", &val); - CHECK(val.isUndefined()); - return true; -} -END_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceCloseWithPendingRead) - -BEGIN_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable) { - return readWithDataAvailable(CompartmentMode::Same, - "r = stream.getReader(); r.read()", - sizeof(testBufferData)); -} -END_FIXTURE_TEST(ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable) - -BEGIN_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1) { - // Scenario 1: The stream and reader are both in the same compartment, but - // ReadableStreamUpdateDataAvailableFromSource is applied to a wrapper. - return readWithDataAvailable(CompartmentMode::Cross, - "r = stream.getReader(); r.read()", - sizeof(testBufferData)); -} -END_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment1) - -BEGIN_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2) { - // Scenario 2: The stream and reader are in the same compartment, but a - // `read` method from another compartment is used on the reader. - return readWithDataAvailable( - CompartmentMode::Cross, - "r = stream.getReader(); read = new " - "ReadableStream({start(){}}).getReader().read; read.call(r)", - sizeof(testBufferData)); -} -END_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment2) - -BEGIN_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3) { - // Scenario 3: The stream and reader are in different compartments. - return readWithDataAvailable( - CompartmentMode::Cross, - "r = ReadableStream.prototype.getReader.call(stream); r.read()", - sizeof(testBufferData)); -} -END_FIXTURE_TEST( - ReadFromExternalSourceFixture, - testReadableStream_ExternalSourceReadDefaultWithDataAvailable_CrossCompartment3) - -// Cross-global tests: -BEGIN_FIXTURE_TEST( - StreamTestFixture, - testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - - RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader)); - CHECK(request); - CHECK(IsPromiseObject(request)); - CHECK(!js::IsWrapper(request)); - CHECK(GetPromiseState(request) == PromiseState::Pending); - - RootedObject chunk(cx, JS_NewPlainObject(cx)); - CHECK(chunk); - RootedValue chunkVal(cx, ObjectValue(*chunk)); - CHECK(ReadableStreamEnqueue(cx, stream, chunkVal)); - - CHECK(GetReadChunk(cx, request) == chunk); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamOtherGlobalDefaultReaderRead) - -BEGIN_FIXTURE_TEST( - StreamTestFixture, - testReadableStream_ReadableStreamGetExternalUnderlyingSource) { - StubExternalUnderlyingSource::instance.reset(); - - RootedObject stream(cx, NewExternalSourceStream(cx)); - CHECK(stream); - ReadableStreamUnderlyingSource* source; - CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source)); - CHECK(source == &StubExternalUnderlyingSource::instance); - CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - ReadableStreamUnderlyingSource* source; - CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &source)); - CHECK(source == &StubExternalUnderlyingSource::instance); - CHECK(ReadableStreamReleaseExternalUnderlyingSource(cx, stream)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamGetExternalUnderlyingSource) - -BEGIN_FIXTURE_TEST( - StreamTestFixture, - testReadableStream_ReadableStreamUpdateDataAvailableFromSource) { - RootedObject stream(cx, NewExternalSourceStream(cx)); - CHECK(stream); - CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, 0)); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - CHECK(ReadableStreamUpdateDataAvailableFromSource(cx, stream, 1)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamUpdateDataAvailableFromSource) - -BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStream) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - CHECK(IsReadableStream(stream)); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - CHECK(IsReadableStream(stream)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStream) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamGetMode) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - ReadableStreamMode mode; - CHECK(ReadableStreamGetMode(cx, stream, &mode)); - CHECK(mode == ReadableStreamMode::Default); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - CHECK(ReadableStreamGetMode(cx, stream, &mode)); - CHECK(mode == ReadableStreamMode::Default); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamGetMode) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamIsReadable) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - bool result; - CHECK(ReadableStreamIsReadable(cx, stream, &result)); - CHECK(result); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - CHECK(ReadableStreamIsReadable(cx, stream, &result)); - CHECK(result); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamIsReadable) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamIsLocked) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - bool result; - CHECK(ReadableStreamIsLocked(cx, stream, &result)); - CHECK_EQUAL(result, false); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - CHECK(ReadableStreamIsLocked(cx, stream, &result)); - CHECK_EQUAL(result, false); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamIsLocked) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamIsDisturbed) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - bool result; - CHECK(ReadableStreamIsDisturbed(cx, stream, &result)); - CHECK_EQUAL(result, false); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - CHECK(ReadableStreamIsDisturbed(cx, stream, &result)); - CHECK_EQUAL(result, false); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamIsDisturbed) - -BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamCancel) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedValue reason(cx); - JSObject* callResult = ReadableStreamCancel(cx, stream, reason); - CHECK(callResult); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - RootedValue reason(cx); - JSObject* callResult = ReadableStreamCancel(cx, stream, reason); - CHECK(callResult); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamCancel) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamGetReader) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject reader(cx); - reader = - ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default); - CHECK(reader); - CHECK(IsReadableStreamDefaultReader(reader)); - CHECK(ReadableStreamReaderReleaseLock(cx, reader)); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - JSObject* callResult = - ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default); - CHECK(callResult); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamGetReader) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamTee_CrossCompartment) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject branch1Stream(cx); - RootedObject branch2Stream(cx); - CHECK(ReadableStreamTee(cx, stream, &branch1Stream, &branch2Stream)); - CHECK(IsReadableStream(branch1Stream)); - CHECK(IsReadableStream(branch2Stream)); - stream = branch1Stream; - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - CHECK(ReadableStreamTee(cx, stream, &branch1Stream, &branch2Stream)); - CHECK(IsReadableStream(branch1Stream)); - CHECK(IsReadableStream(branch2Stream)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamTee_CrossCompartment) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamGetDesiredSize) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - bool hasValue; - double value; - CHECK(ReadableStreamGetDesiredSize(cx, stream, &hasValue, &value)); - CHECK_EQUAL(hasValue, true); - CHECK_EQUAL(value, 1.0); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - hasValue = false; - value = 0; - CHECK(ReadableStreamGetDesiredSize(cx, stream, &hasValue, &value)); - CHECK_EQUAL(hasValue, true); - CHECK_EQUAL(value, 1.0); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamGetDesiredSize) - -BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamClose) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - CHECK(ReadableStreamClose(cx, stream)); - - stream = NewDefaultStream(cx); - CHECK(stream); - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - CHECK(ReadableStreamClose(cx, stream)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamClose) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamEnqueue_CrossCompartment) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedValue chunk(cx); - CHECK(ReadableStreamEnqueue(cx, stream, chunk)); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - RootedValue chunk(cx); - CHECK(ReadableStreamEnqueue(cx, stream, chunk)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamEnqueue_CrossCompartment) - -BEGIN_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamError) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedValue error(cx); - CHECK(ReadableStreamError(cx, stream, error)); - - stream = NewDefaultStream(cx); - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &stream)); - RootedValue error(cx); - CHECK(ReadableStreamError(cx, stream, error)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_ReadableStreamError) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_IsReadableStreamReader) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - CHECK(IsReadableStreamReader(reader)); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &reader)); - CHECK(IsReadableStreamReader(reader)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, testReadableStream_IsReadableStreamReader) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_IsReadableStreamDefaultReader) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - CHECK(IsReadableStreamDefaultReader(reader)); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &reader)); - CHECK(IsReadableStreamDefaultReader(reader)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_IsReadableStreamDefaultReader) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamReaderIsClosed) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - bool result; - CHECK(ReadableStreamReaderIsClosed(cx, reader, &result)); - CHECK_EQUAL(result, false); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &reader)); - bool result; - CHECK(ReadableStreamReaderIsClosed(cx, reader, &result)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamReaderIsClosed) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamReaderCancel) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - RootedValue reason(cx); - CHECK(ReadableStreamReaderCancel(cx, reader, reason)); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &reader)); - RootedValue reason(cx); - CHECK(ReadableStreamReaderCancel(cx, reader, reason)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamReaderCancel) - -BEGIN_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamReaderReleaseLock) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - CHECK(reader); - CHECK(ReadableStreamReaderReleaseLock(cx, reader)); - - // Repeat the test cross-compartment. This creates a new reader, since - // releasing the lock above deactivated the first reader. - reader = - ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default); - CHECK(reader); - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &reader)); - CHECK(ReadableStreamReaderReleaseLock(cx, reader)); - } - - return true; -} -END_FIXTURE_TEST(StreamTestFixture, - testReadableStream_ReadableStreamReaderReleaseLock) - -BEGIN_FIXTURE_TEST( - StreamTestFixture, - testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment) { - RootedObject stream(cx, NewDefaultStream(cx)); - CHECK(stream); - RootedObject reader(cx, ReadableStreamGetReader( - cx, stream, ReadableStreamReaderMode::Default)); - JSObject* callResult = ReadableStreamDefaultReaderRead(cx, reader); - CHECK(callResult); - - RootedObject otherGlobal(cx, createGlobal()); - CHECK(otherGlobal); - { - JSAutoRealm ar(cx, otherGlobal); - CHECK(JS_WrapObject(cx, &reader)); - JSObject* callResult = ReadableStreamDefaultReaderRead(cx, reader); - CHECK(callResult); - } - - return true; -} -END_FIXTURE_TEST( - StreamTestFixture, - testReadableStream_ReadableStreamDefaultReaderRead_CrossCompartment) diff --git a/js/src/jsapi-tests/tests.cpp b/js/src/jsapi-tests/tests.cpp index dfbf5e861a1f..9633e6a63001 100644 --- a/js/src/jsapi-tests/tests.cpp +++ b/js/src/jsapi-tests/tests.cpp @@ -116,7 +116,6 @@ JSObject* JSAPITest::createGlobal(JSPrincipals* principals) { JS::RootedObject newGlobal(cx); JS::RealmOptions options; options.creationOptions() - .setStreamsEnabled(true) .setWeakRefsEnabled(JS::WeakRefSpecifier::EnabledWithCleanupSome) .setSharedMemoryAndAtomicsEnabled(true); newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, diff --git a/js/src/moz.build b/js/src/moz.build index 7c6a98fe26e3..9e43c66ecc13 100755 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -200,7 +200,6 @@ EXPORTS.js += [ "../public/SourceText.h", "../public/StableStringChars.h", "../public/Stack.h", - "../public/Stream.h", "../public/StreamConsumer.h", "../public/String.h", "../public/StructuredClone.h", diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8a3dcc9a08b2..db97f7e1b7f7 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -604,7 +604,6 @@ bool shell::enableTestWasmAwaitTier2 = false; bool shell::enableSourcePragmas = true; bool shell::enableAsyncStacks = false; bool shell::enableAsyncStackCaptureDebuggeeOnly = false; -bool shell::enableStreams = false; bool shell::enableWeakRefs = false; bool shell::enableToSource = false; bool shell::enablePropertyErrorMessageFix = false; @@ -3841,7 +3840,6 @@ static void SetStandardRealmOptions(JS::RealmOptions& options) { options.creationOptions() .setSharedMemoryAndAtomicsEnabled(enableSharedMemory) .setCoopAndCoepEnabled(false) - .setStreamsEnabled(enableStreams) .setWeakRefsEnabled(enableWeakRefs ? JS::WeakRefSpecifier::EnabledWithCleanupSome : JS::WeakRefSpecifier::Disabled) @@ -10601,7 +10599,6 @@ static bool SetContextOptions(JSContext* cx, const OptionParser& op) { enableAsyncStacks = !op.getBoolOption("no-async-stacks"); enableAsyncStackCaptureDebuggeeOnly = op.getBoolOption("async-stacks-capture-debuggee-only"); - enableStreams = !op.getBoolOption("no-streams"); enableWeakRefs = !op.getBoolOption("disable-weak-refs"); enableToSource = !op.getBoolOption("disable-tosource"); enablePropertyErrorMessageFix = @@ -11575,9 +11572,6 @@ int main(int argc, char** argv) { !op.addBoolOption('\0', "less-debug-code", "Emit less machine code for " "checking assertions under DEBUG.") || - !op.addBoolOption('\0', "enable-streams", - "Enable WHATWG Streams (default)") || - !op.addBoolOption('\0', "no-streams", "Disable WHATWG Streams") || !op.addBoolOption('\0', "disable-weak-refs", "Disable weak references") || !op.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") || !op.addBoolOption('\0', "disable-property-error-message-fix", diff --git a/js/src/shell/jsshell.h b/js/src/shell/jsshell.h index 3e87cdfce3c8..7e22bc06079d 100644 --- a/js/src/shell/jsshell.h +++ b/js/src/shell/jsshell.h @@ -122,11 +122,6 @@ extern bool enableTestWasmAwaitTier2; extern bool enableSourcePragmas; extern bool enableAsyncStacks; extern bool enableAsyncStackCaptureDebuggeeOnly; -extern bool enableStreams; -extern bool enableReadableByteStreams; -extern bool enableBYOBStreamReaders; - -extern bool enableReadableStreamPipeTo; extern bool enableWeakRefs; extern bool enableToSource; extern bool enablePropertyErrorMessageFix; diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index c066c6251606..b92a0a35d8bd 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -25,11 +25,6 @@ #include "builtin/FinalizationRegistryObject.h" #include "builtin/MapObject.h" #include "builtin/ShadowRealm.h" -#include "builtin/Stream.h" -#include "builtin/streams/QueueingStrategies.h" // js::{ByteLength,Count}QueueingStrategy -#include "builtin/streams/ReadableStream.h" // js::ReadableStream -#include "builtin/streams/ReadableStreamController.h" // js::Readable{StreamDefault,ByteStream}Controller -#include "builtin/streams/ReadableStreamReader.h" // js::ReadableStreamDefaultReader #include "builtin/Symbol.h" #include "builtin/WeakMapObject.h" #include "builtin/WeakRefObject.h" @@ -180,15 +175,6 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) { case JSProto_RelativeTimeFormat: return false; #endif -#ifdef MOZ_JS_STREAMS - case JSProto_ReadableStream: - case JSProto_ReadableStreamDefaultReader: - case JSProto_ReadableStreamDefaultController: - case JSProto_ReadableByteStreamController: - case JSProto_ByteLengthQueuingStrategy: - case JSProto_CountQueuingStrategy: - return !cx->realm()->creationOptions().getStreamsEnabled(); -#endif // Return true if the given constructor has been disabled at run-time. case JSProto_Atomics: diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index d8395a57151d..f5e0c6ddfece 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -53,7 +53,6 @@ #include "js/MemoryMetrics.h" #include "js/Object.h" // JS::GetClass #include "js/RealmIterators.h" -#include "js/Stream.h" // JS::AbortSignalIsAborted, JS::InitPipeToHandling #include "js/SliceBudget.h" #include "js/UbiNode.h" #include "js/UbiNodeUtils.h"