Bug 1795914 - Remove JS Streams implementation r=jandem

Differential Revision: https://phabricator.services.mozilla.com/D159610
This commit is contained in:
Matthew Gaudet 2022-10-24 16:05:58 +00:00
Родитель 3d10b581aa
Коммит 4852cc8cc4
50 изменённых файлов: 79 добавлений и 8835 удалений

Просмотреть файл

@ -8,7 +8,6 @@
#define mozilla_dom_BodyStream_h #define mozilla_dom_BodyStream_h
#include "jsapi.h" #include "jsapi.h"
#include "js/Stream.h"
#include "mozilla/AlreadyAddRefed.h" #include "mozilla/AlreadyAddRefed.h"
#include "mozilla/dom/ByteStreamHelpers.h" #include "mozilla/dom/ByteStreamHelpers.h"
#include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/BindingDeclarations.h"

Просмотреть файл

@ -6,7 +6,6 @@
#include "FetchStreamReader.h" #include "FetchStreamReader.h"
#include "InternalResponse.h" #include "InternalResponse.h"
#include "js/Stream.h"
#include "mozilla/ConsoleReportCollector.h" #include "mozilla/ConsoleReportCollector.h"
#include "mozilla/ErrorResult.h" #include "mozilla/ErrorResult.h"
#include "mozilla/dom/AutoEntryScript.h" #include "mozilla/dom/AutoEntryScript.h"

Просмотреть файл

@ -168,24 +168,6 @@ def enable_decorators(value):
set_config("ENABLE_DECORATORS", enable_decorators) set_config("ENABLE_DECORATORS", enable_decorators)
set_define("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 # JIT support
# ======================================================= # =======================================================
@depends(target, "--enable-record-tuple") @depends(target, "--enable-record-tuple")

Просмотреть файл

@ -46,94 +46,84 @@
# define IF_WASM_TYPE(REAL, IMAGINARY) IMAGINARY # define IF_WASM_TYPE(REAL, IMAGINARY) IMAGINARY
#endif #endif
#define JS_FOR_PROTOTYPES_(REAL, IMAGINARY, REAL_IF_INTL, REAL_IF_WASM_TYPE) \ #define JS_FOR_PROTOTYPES_(REAL, IMAGINARY, REAL_IF_INTL, REAL_IF_WASM_TYPE) \
IMAGINARY(Null, dummy) \ IMAGINARY(Null, dummy) \
REAL(Object, OCLASP(Plain)) \ REAL(Object, OCLASP(Plain)) \
REAL(Function, &FunctionClass) \ REAL(Function, &FunctionClass) \
REAL(Array, OCLASP(Array)) \ REAL(Array, OCLASP(Array)) \
REAL(Boolean, OCLASP(Boolean)) \ REAL(Boolean, OCLASP(Boolean)) \
REAL(JSON, CLASP(JSON)) \ REAL(JSON, CLASP(JSON)) \
REAL(Date, OCLASP(Date)) \ REAL(Date, OCLASP(Date)) \
REAL(Math, CLASP(Math)) \ REAL(Math, CLASP(Math)) \
REAL(Number, OCLASP(Number)) \ REAL(Number, OCLASP(Number)) \
REAL(String, OCLASP(String)) \ REAL(String, OCLASP(String)) \
REAL(RegExp, OCLASP(RegExp)) \ REAL(RegExp, OCLASP(RegExp)) \
REAL(Error, ERROR_CLASP(JSEXN_ERR)) \ REAL(Error, ERROR_CLASP(JSEXN_ERR)) \
REAL(InternalError, ERROR_CLASP(JSEXN_INTERNALERR)) \ REAL(InternalError, ERROR_CLASP(JSEXN_INTERNALERR)) \
REAL(AggregateError, ERROR_CLASP(JSEXN_AGGREGATEERR)) \ REAL(AggregateError, ERROR_CLASP(JSEXN_AGGREGATEERR)) \
REAL(EvalError, ERROR_CLASP(JSEXN_EVALERR)) \ REAL(EvalError, ERROR_CLASP(JSEXN_EVALERR)) \
REAL(RangeError, ERROR_CLASP(JSEXN_RANGEERR)) \ REAL(RangeError, ERROR_CLASP(JSEXN_RANGEERR)) \
REAL(ReferenceError, ERROR_CLASP(JSEXN_REFERENCEERR)) \ REAL(ReferenceError, ERROR_CLASP(JSEXN_REFERENCEERR)) \
REAL(SyntaxError, ERROR_CLASP(JSEXN_SYNTAXERR)) \ REAL(SyntaxError, ERROR_CLASP(JSEXN_SYNTAXERR)) \
REAL(TypeError, ERROR_CLASP(JSEXN_TYPEERR)) \ REAL(TypeError, ERROR_CLASP(JSEXN_TYPEERR)) \
REAL(URIError, ERROR_CLASP(JSEXN_URIERR)) \ REAL(URIError, ERROR_CLASP(JSEXN_URIERR)) \
REAL(DebuggeeWouldRun, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN)) \ REAL(DebuggeeWouldRun, ERROR_CLASP(JSEXN_DEBUGGEEWOULDRUN)) \
REAL(CompileError, ERROR_CLASP(JSEXN_WASMCOMPILEERROR)) \ REAL(CompileError, ERROR_CLASP(JSEXN_WASMCOMPILEERROR)) \
REAL(LinkError, ERROR_CLASP(JSEXN_WASMLINKERROR)) \ REAL(LinkError, ERROR_CLASP(JSEXN_WASMLINKERROR)) \
REAL(RuntimeError, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR)) \ REAL(RuntimeError, ERROR_CLASP(JSEXN_WASMRUNTIMEERROR)) \
REAL(ArrayBuffer, OCLASP(ArrayBuffer)) \ REAL(ArrayBuffer, OCLASP(ArrayBuffer)) \
REAL(Int8Array, TYPED_ARRAY_CLASP(Int8)) \ REAL(Int8Array, TYPED_ARRAY_CLASP(Int8)) \
REAL(Uint8Array, TYPED_ARRAY_CLASP(Uint8)) \ REAL(Uint8Array, TYPED_ARRAY_CLASP(Uint8)) \
REAL(Int16Array, TYPED_ARRAY_CLASP(Int16)) \ REAL(Int16Array, TYPED_ARRAY_CLASP(Int16)) \
REAL(Uint16Array, TYPED_ARRAY_CLASP(Uint16)) \ REAL(Uint16Array, TYPED_ARRAY_CLASP(Uint16)) \
REAL(Int32Array, TYPED_ARRAY_CLASP(Int32)) \ REAL(Int32Array, TYPED_ARRAY_CLASP(Int32)) \
REAL(Uint32Array, TYPED_ARRAY_CLASP(Uint32)) \ REAL(Uint32Array, TYPED_ARRAY_CLASP(Uint32)) \
REAL(Float32Array, TYPED_ARRAY_CLASP(Float32)) \ REAL(Float32Array, TYPED_ARRAY_CLASP(Float32)) \
REAL(Float64Array, TYPED_ARRAY_CLASP(Float64)) \ REAL(Float64Array, TYPED_ARRAY_CLASP(Float64)) \
REAL(Uint8ClampedArray, TYPED_ARRAY_CLASP(Uint8Clamped)) \ REAL(Uint8ClampedArray, TYPED_ARRAY_CLASP(Uint8Clamped)) \
REAL(BigInt64Array, TYPED_ARRAY_CLASP(BigInt64)) \ REAL(BigInt64Array, TYPED_ARRAY_CLASP(BigInt64)) \
REAL(BigUint64Array, TYPED_ARRAY_CLASP(BigUint64)) \ REAL(BigUint64Array, TYPED_ARRAY_CLASP(BigUint64)) \
REAL(BigInt, OCLASP(BigInt)) \ REAL(BigInt, OCLASP(BigInt)) \
REAL(Proxy, CLASP(Proxy)) \ REAL(Proxy, CLASP(Proxy)) \
REAL(WeakMap, OCLASP(WeakMap)) \ REAL(WeakMap, OCLASP(WeakMap)) \
REAL(Map, OCLASP(Map)) \ REAL(Map, OCLASP(Map)) \
REAL(Set, OCLASP(Set)) \ REAL(Set, OCLASP(Set)) \
REAL(DataView, OCLASP(DataView)) \ REAL(DataView, OCLASP(DataView)) \
REAL(Symbol, OCLASP(Symbol)) \ REAL(Symbol, OCLASP(Symbol)) \
REAL(ShadowRealm, OCLASP(ShadowRealm)) \ REAL(ShadowRealm, OCLASP(ShadowRealm)) \
REAL(SharedArrayBuffer, OCLASP(SharedArrayBuffer)) \ REAL(SharedArrayBuffer, OCLASP(SharedArrayBuffer)) \
REAL_IF_INTL(Intl, CLASP(Intl)) \ REAL_IF_INTL(Intl, CLASP(Intl)) \
REAL_IF_INTL(Collator, OCLASP(Collator)) \ REAL_IF_INTL(Collator, OCLASP(Collator)) \
REAL_IF_INTL(DateTimeFormat, OCLASP(DateTimeFormat)) \ REAL_IF_INTL(DateTimeFormat, OCLASP(DateTimeFormat)) \
REAL_IF_INTL(DisplayNames, OCLASP(DisplayNames)) \ REAL_IF_INTL(DisplayNames, OCLASP(DisplayNames)) \
REAL_IF_INTL(ListFormat, OCLASP(ListFormat)) \ REAL_IF_INTL(ListFormat, OCLASP(ListFormat)) \
REAL_IF_INTL(Locale, OCLASP(Locale)) \ REAL_IF_INTL(Locale, OCLASP(Locale)) \
REAL_IF_INTL(NumberFormat, OCLASP(NumberFormat)) \ REAL_IF_INTL(NumberFormat, OCLASP(NumberFormat)) \
REAL_IF_INTL(PluralRules, OCLASP(PluralRules)) \ REAL_IF_INTL(PluralRules, OCLASP(PluralRules)) \
REAL_IF_INTL(RelativeTimeFormat, OCLASP(RelativeTimeFormat)) \ REAL_IF_INTL(RelativeTimeFormat, OCLASP(RelativeTimeFormat)) \
REAL(Reflect, CLASP(Reflect)) \ REAL(Reflect, CLASP(Reflect)) \
REAL(WeakSet, OCLASP(WeakSet)) \ REAL(WeakSet, OCLASP(WeakSet)) \
REAL(TypedArray, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \ REAL(TypedArray, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
REAL(Atomics, OCLASP(Atomics)) \ REAL(Atomics, OCLASP(Atomics)) \
REAL(SavedFrame, &js::SavedFrame::class_) \ REAL(SavedFrame, &js::SavedFrame::class_) \
REAL(Promise, OCLASP(Promise)) \ REAL(Promise, OCLASP(Promise)) \
REAL(AsyncFunction, CLASP(AsyncFunction)) \ REAL(AsyncFunction, CLASP(AsyncFunction)) \
REAL(GeneratorFunction, CLASP(GeneratorFunction)) \ REAL(GeneratorFunction, CLASP(GeneratorFunction)) \
REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction)) \ REAL(AsyncGeneratorFunction, CLASP(AsyncGeneratorFunction)) \
IF_JS_STREAMS(REAL(ReadableStream, &js::ReadableStream::class_)) \ REAL(WebAssembly, OCLASP(WasmNamespace)) \
IF_JS_STREAMS(REAL(ReadableStreamDefaultReader, \ REAL(WasmModule, OCLASP(WasmModule)) \
&js::ReadableStreamDefaultReader::class_)) \ REAL(WasmInstance, OCLASP(WasmInstance)) \
IF_JS_STREAMS(REAL(ReadableStreamDefaultController, \ REAL(WasmMemory, OCLASP(WasmMemory)) \
&js::ReadableStreamDefaultController::class_)) \ REAL(WasmTable, OCLASP(WasmTable)) \
IF_JS_STREAMS(REAL(ReadableByteStreamController, \ REAL(WasmGlobal, OCLASP(WasmGlobal)) \
&js::ReadableByteStreamController::class_)) \ REAL(WasmTag, OCLASP(WasmTag)) \
IF_JS_STREAMS( \ REAL_IF_WASM_TYPE(WasmFunction, CLASP(WasmFunction)) \
REAL(ByteLengthQueuingStrategy, &js::ByteLengthQueuingStrategy::class_)) \ REAL(WasmException, OCLASP(WasmException)) \
IF_JS_STREAMS(REAL(CountQueuingStrategy, &js::CountQueuingStrategy::class_)) \ REAL(FinalizationRegistry, OCLASP(FinalizationRegistry)) \
REAL(WebAssembly, OCLASP(WasmNamespace)) \ REAL(WeakRef, OCLASP(WeakRef)) \
REAL(WasmModule, OCLASP(WasmModule)) \ REAL(Iterator, OCLASP(Iterator)) \
REAL(WasmInstance, OCLASP(WasmInstance)) \ REAL(AsyncIterator, OCLASP(AsyncIterator)) \
REAL(WasmMemory, OCLASP(WasmMemory)) \ IF_RECORD_TUPLE(REAL(Record, (&RecordType::class_))) \
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_))) IF_RECORD_TUPLE(REAL(Tuple, (&TupleType::class_)))
#define JS_FOR_PROTOTYPES(REAL, IMAGINARY) \ #define JS_FOR_PROTOTYPES(REAL, IMAGINARY) \

Просмотреть файл

@ -172,19 +172,6 @@ class JS_PUBLIC_API RealmCreationOptions {
bool getCoopAndCoepEnabled() const; bool getCoopAndCoepEnabled() const;
RealmCreationOptions& setCoopAndCoepEnabled(bool flag); 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_; } WeakRefSpecifier getWeakRefsEnabled() const { return weakRefs_; }
RealmCreationOptions& setWeakRefsEnabled(WeakRefSpecifier spec) { RealmCreationOptions& setWeakRefsEnabled(WeakRefSpecifier spec) {
weakRefs_ = spec; weakRefs_ = spec;
@ -280,7 +267,6 @@ class JS_PUBLIC_API RealmCreationOptions {
bool sharedMemoryAndAtomics_ = false; bool sharedMemoryAndAtomics_ = false;
bool defineSharedArrayBufferConstructor_ = true; bool defineSharedArrayBufferConstructor_ = true;
bool coopAndCoep_ = false; bool coopAndCoep_ = false;
bool streams_ = false;
bool toSource_ = false; bool toSource_ = false;
bool propertyErrorMessageFix_ = false; bool propertyErrorMessageFix_ = false;
bool iteratorHelpers_ = false; bool iteratorHelpers_ = false;

Просмотреть файл

@ -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 <stddef.h>
#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<JSObject*> 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 <https://streams.spec.whatwg.org/#default-reader-cancel> and
* <https://streams.spec.whatwg.org/#byob-reader-cancel>).
*
* `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 <https://streams.spec.whatwg.org/#default-reader-release-lock> and
* <https://streams.spec.whatwg.org/#byob-reader-release-lock>).
*
* `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
* (<https://streams.spec.whatwg.org/#default-reader-read>).
*
* 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

Просмотреть файл

@ -141,13 +141,6 @@ using jsid = JS::PropertyKey;
# define IF_RECORD_TUPLE(x, ...) __VA_ARGS__ # define IF_RECORD_TUPLE(x, ...) __VA_ARGS__
#endif #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 // Follows the same pattern as IF_RECORD_TUPLE
#ifdef ENABLE_DECORATORS #ifdef ENABLE_DECORATORS
# define IF_DECORATORS(x, ...) x # define IF_DECORATORS(x, ...) x

Просмотреть файл

@ -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 <stdint.h> // 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<ArrayBufferObject>();
}
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<ByteStreamChunk*> chunk(cx, NewBuiltinClassInstance<ByteStreamChunk>(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<ReadableStream*> stream,
HandleValue underlyingByteSource,
HandleValue highWaterMarkVal)
{
cx->check(stream, underlyingByteSource, highWaterMarkVal);
Rooted<ReadableByteStreamController*> controller(cx,
NewBuiltinClassInstance<ReadableByteStreamController>(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<ReadableStreamController*> 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<ReadableStream*> stream,
JS::ReadableStreamUnderlyingSource* source) {
// Done elsewhere in the standard: Create the controller object.
Rooted<ReadableByteStreamController*> controller(
cx, NewBuiltinClassInstance<ReadableByteStreamController>(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
// <https://github.com/whatwg/streams/issues/975>.
// 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<PromiseObject*> 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<ReadableByteStreamController>();
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<ReadableStreamController*> unwrappedController);
/**
* Streams spec, 3.11.5.2. [[PullSteps]] ( forAuthorCode )
*/
[[nodiscard]] static PromiseObject* ReadableByteStreamControllerPullSteps(
JSContext* cx, Handle<ReadableByteStreamController*> unwrappedController) {
// Step 1: Let stream be this.[[controlledReadableByteStream]].
Rooted<ReadableStream*> 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<ListObject*> unwrappedQueue(cx, unwrappedController->queue());
Rooted<ByteStreamChunk*> unwrappedEntry(cx,
UnwrapAndDowncastObject<ByteStreamChunk>(
cx, &unwrappedQueue->popFirstAs<JSObject>(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<PlainObject*> 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<ArrayBufferObject>());
// 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<PromiseObject*> 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<ReadableStreamController*> unwrappedController) {
if (unwrappedController->is<ReadableStreamDefaultController>()) {
Rooted<ReadableStreamDefaultController*> unwrappedDefaultController(
cx, &unwrappedController->as<ReadableStreamDefaultController>());
return ReadableStreamDefaultControllerPullSteps(cx,
unwrappedDefaultController);
}
Rooted<ReadableByteStreamController*> unwrappedByteController(
cx, &unwrappedController->as<ReadableByteStreamController>());
return ReadableByteStreamControllerPullSteps(cx, unwrappedByteController);
}
/*** 3.13. Readable stream BYOB controller abstract operations **************/
// Streams spec, 3.13.1. IsReadableStreamBYOBRequest ( x )
// Implemented via is<ReadableStreamBYOBRequest>()
// Streams spec, 3.13.2. IsReadableByteStreamController ( x )
// Implemented via is<ReadableByteStreamController>()
// Streams spec, 3.13.3.
// ReadableByteStreamControllerCallPullIfNeeded ( controller )
// Unified with 3.9.2 above.
[[nodiscard]] static bool ReadableByteStreamControllerInvalidateBYOBRequest(
JSContext* cx, Handle<ReadableByteStreamController*> unwrappedController);
/**
* Streams spec, 3.13.5.
* ReadableByteStreamControllerClearPendingPullIntos ( controller )
*/
[[nodiscard]] bool js::ReadableByteStreamControllerClearPendingPullIntos(
JSContext* cx, Handle<ReadableByteStreamController*> 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<ReadableByteStreamController*> unwrappedController) {
// Step 1: Let stream be controller.[[controlledReadableByteStream]].
Rooted<ReadableStream*> 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<ListObject*> unwrappedPendingPullIntos(
cx, unwrappedController->pendingPullIntos());
if (unwrappedPendingPullIntos->length() != 0) {
// Step a: Let firstPendingPullInto be the first element of
// controller.[[pendingPullIntos]].
Rooted<PullIntoDescriptor*> unwrappedFirstPendingPullInto(
cx, UnwrapAndDowncastObject<PullIntoDescriptor>(
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<SavedFrame*> 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<ReadableStreamController*> unwrappedController) {
MOZ_ASSERT(unwrappedController->is<ReadableByteStreamController>());
// Step 1: Assert: controller.[[controlledReadableStream]].[[state]]
// is "readable".
Rooted<ReadableStream*> 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<ReadableByteStreamController*> unwrappedController) {
// Step 1: If controller.[[byobRequest]] is undefined, return.
RootedValue unwrappedBYOBRequestVal(cx, unwrappedController->byobRequest());
if (unwrappedBYOBRequestVal.isUndefined()) {
return true;
}
Rooted<NativeObject*> unwrappedBYOBRequest(
cx, UnwrapAndDowncastValue<NativeObject>(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.

Просмотреть файл

@ -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<ReadableByteStreamController*> unwrappedController);
[[nodiscard]] extern bool ReadableByteStreamControllerClose(
JSContext* cx,
JS::Handle<ReadableByteStreamController*> unwrappedController);
[[nodiscard]] extern PromiseObject* ReadableStreamControllerPullSteps(
JSContext* cx, JS::Handle<ReadableStreamController*> unwrappedController);
} // namespace js
#endif /* builtin_Stream_h */

Просмотреть файл

@ -3879,12 +3879,6 @@ static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) {
return result; 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 unsigned finalizeCount = 0;
static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) { static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) {
@ -8326,10 +8320,6 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
"rejectPromise(promise, reason)", "rejectPromise(promise, reason)",
" Reject a Promise by calling the JSAPI function JS::RejectPromise."), " 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, JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
"makeFinalizeObserver()", "makeFinalizeObserver()",
" Get a special object whose finalization increases the counter returned\n" " Get a special object whose finalization increases the counter returned\n"

Просмотреть файл

@ -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<cls::constructor, nCtorArgs, \
js::gc::AllocKind::FUNCTION>, \
js::GenericCreatePrototype<cls>, \
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

Просмотреть файл

@ -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<JS::Value>.
*/
template <class... Args>
[[nodiscard]] inline JSObject* PromiseCall(JSContext* cx,
JS::Handle<JS::Value> F,
JS::Handle<JS::Value> 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<JS::Value> 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<JS::Value> value) {
cx->check(value);
JS::Rooted<JSObject*> 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<JSObject*> unwrappedPromise,
JS::Handle<JS::Value> 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<JS::Value> error) {
JS::Rooted<JSObject*> promise(cx, unwrappedPromise);
return RejectUnwrappedPromiseWithError(cx, &promise, error);
}
} // namespace js
#endif // builtin_streams_MiscellaneousOperations_inl_h

Просмотреть файл

@ -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<Value> 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<Value> underlyingObject,
const char* methodNameForErrorMessage, Handle<PropertyName*> methodName,
MutableHandle<Value> 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<Value> O,
Handle<PropertyName*> P, Handle<Value> arg,
MutableHandle<Value> 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<Value> 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<Value> 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<Value> 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;
}

Просмотреть файл

@ -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<JS::Value> underlyingObject,
const char* methodNameForErrorMessage, JS::Handle<PropertyName*> methodName,
JS::MutableHandle<JS::Value> 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<JS::Value> O,
JS::Handle<PropertyName*> P,
JS::Handle<JS::Value> arg,
JS::MutableHandle<JS::Value> rval);
/**
* Streams spec, 6.3.7. ValidateAndNormalizeHighWaterMark ( highWaterMark )
*/
[[nodiscard]] extern bool ValidateAndNormalizeHighWaterMark(
JSContext* cx, JS::Handle<JS::Value> highWaterMarkVal,
double* highWaterMark);
/**
* Streams spec, 6.3.8. MakeSizeAlgorithmFromSizeFunction ( size )
*/
[[nodiscard]] extern bool MakeSizeAlgorithmFromSizeFunction(
JSContext* cx, JS::Handle<JS::Value> size);
template <class T>
inline bool IsMaybeWrapped(const JS::Handle<JS::Value> v) {
return v.isObject() && v.toObject().canUnwrapAs<T>();
}
} // namespace js
#endif // builtin_streams_MiscellaneousOperations_h

Просмотреть файл

@ -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 <stdint.h> // 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<ArrayBufferObject*> buffer, uint32_t byteOffset,
uint32_t byteLength, uint32_t bytesFilled, uint32_t elementSize,
Handle<JSObject*> ctor, ReaderType readerType) {
Rooted<PullIntoDescriptor*> descriptor(
cx, NewBuiltinClassInstance<PullIntoDescriptor>(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<int32_t>(readerType)));
return descriptor;
}
const JSClass PullIntoDescriptor::class_ = {
"PullIntoDescriptor", JSCLASS_HAS_RESERVED_SLOTS(SlotCount)};

Просмотреть файл

@ -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 <stdint.h> // 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<ArrayBufferObject>();
}
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<ArrayBufferObject*> buffer,
uint32_t byteOffset, uint32_t byteLength,
uint32_t bytesFilled, uint32_t elementSize,
JS::Handle<JSObject*> ctor,
ReaderType readerType);
};
} // namespace js
#endif // builtin_streams_PullIntoDescriptor_h

Просмотреть файл

@ -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<ListObject*> unwrappedQueue,
JS::Handle<JS::Value> 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

Просмотреть файл

@ -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<StreamController*> unwrappedContainer,
MutableHandle<Value> chunk) {
// Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal
// slots (implicit).
// Step 2: Assert: queue is not empty.
Rooted<ListObject*> 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<StreamController*> unwrappedContainer,
Handle<Value> value, Handle<Value> 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<ListObject*> unwrappedQueue(cx, unwrappedContainer->queue());
Rooted<Value> 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<StreamController*> 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;
}

Просмотреть файл

@ -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<StreamController*> unwrappedContainer,
JS::MutableHandle<JS::Value> 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<StreamController*> unwrappedContainer,
JS::Handle<JS::Value> value, JS::Handle<JS::Value> sizeVal);
/**
* Streams spec, 6.2.4. ResetQueue ( container ) nothrow
*/
[[nodiscard]] extern bool ResetQueue(
JSContext* cx, JS::Handle<StreamController*> 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

Просмотреть файл

@ -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<JSObject*> proto(cx);
if (!GetPrototypeFromBuiltinConstructor(
cx, args, JSProto_ByteLengthQueuingStrategy, &proto)) {
return false;
}
Rooted<ByteLengthQueuingStrategy*> strategy(
cx, NewObjectWithClassProto<ByteLengthQueuingStrategy>(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<ByteLengthQueuingStrategy*> unwrappedStrategy(
cx, UnwrapAndTypeCheckThis<ByteLengthQueuingStrategy>(
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<CountQueuingStrategy*> strategy(
cx, NewObjectWithClassProto<CountQueuingStrategy>(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<CountQueuingStrategy*> unwrappedStrategy(
cx, UnwrapAndTypeCheckThis<CountQueuingStrategy>(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);

Просмотреть файл

@ -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

Просмотреть файл

@ -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<ReadableStreamDefaultController>()) {
return JS::ReadableStreamMode::Default;
}
return controller->as<ReadableByteStreamController>().hasExternalSource()
? JS::ReadableStreamMode::ExternalSource
: JS::ReadableStreamMode::Byte;
}
ReadableStream* ReadableStream::createExternalSourceStream(
JSContext* cx, JS::ReadableStreamUnderlyingSource* source,
void* nsISupportsObject_alreadyAddreffed /* = nullptr */,
Handle<JSObject*> proto /* = nullptr */) {
Rooted<ReadableStream*> 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<Value> underlyingSource(cx, args.get(0));
if (underlyingSource.isUndefined()) {
JSObject* emptyObj = NewPlainObject(cx);
if (!emptyObj) {
return false;
}
underlyingSource = ObjectValue(*emptyObj);
}
Rooted<Value> 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<JSObject*> proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ReadableStream,
&proto)) {
return false;
}
Rooted<ReadableStream*> stream(cx,
ReadableStream::create(cx, nullptr, proto));
if (!stream) {
return false;
}
// Step 2: Let size be ? GetV(strategy, "size").
Rooted<Value> size(cx);
if (!GetProperty(cx, strategy, cx->names().size, &size)) {
return false;
}
// Step 3: Let highWaterMark be ? GetV(strategy, "highWaterMark").
Rooted<Value> highWaterMarkVal(cx);
if (!GetProperty(cx, strategy, cx->names().highWaterMark,
&highWaterMarkVal)) {
return false;
}
// Step 4: Let type be ? GetV(underlyingSource, "type").
Rooted<Value> type(cx);
if (!GetProperty(cx, underlyingSource, cx->names().type, &type)) {
return false;
}
// Step 5: Let typeString be ? ToString(type).
Rooted<JSString*> typeString(cx, ToString<CanGC>(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<ReadableStream*> unwrappedStream(
cx, UnwrapAndTypeCheckThis<ReadableStream>(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<ReadableStream*> unwrappedStream(
cx, UnwrapAndTypeCheckThis<ReadableStream>(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<JSObject*> 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<ReadableStream*> unwrappedStream(
cx, UnwrapAndTypeCheckThis<ReadableStream>(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<Value> 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<JS::ReadableStreamReaderMode> mode;
// Step 4: ...
//
// - Optimized for one dictionary member.
// - Treat non-object options as non-existing "mode" member.
if (optionsVal.isObject()) {
Rooted<Value> 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<JSString*> modeStr(cx, ToString<CanGC>(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<JSObject*> 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<ReadableStream*> unwrappedStream(
cx, UnwrapAndTypeCheckThis<ReadableStream>(cx, args, "tee"));
if (!unwrappedStream) {
return false;
}
// Step 2: Let branches be ? ReadableStreamTee(this, false).
Rooted<ReadableStream*> branch1(cx);
Rooted<ReadableStream*> branch2(cx);
if (!ReadableStreamTee(cx, unwrappedStream, false, &branch1, &branch2)) {
return false;
}
// Step 3: Return ! CreateArrayFromList(branches).
Rooted<NativeObject*> 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<ReadableStream::constructor, 0,
js::gc::AllocKind::FUNCTION>,
js::GenericCreatePrototype<ReadableStream>,
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_};

Просмотреть файл

@ -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 <stdint.h> // 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<JS::Value> value) {
setFixedSlot(Slot_StoredError, value);
}
JS::ReadableStreamMode mode() const;
bool locked() const;
[[nodiscard]] static ReadableStream* create(
JSContext* cx, void* nsISupportsObject_alreadyAddreffed = nullptr,
JS::Handle<JSObject*> proto = nullptr);
static ReadableStream* createExternalSourceStream(
JSContext* cx, JS::ReadableStreamUnderlyingSource* source,
void* nsISupportsObject_alreadyAddreffed = nullptr,
JS::Handle<JSObject*> 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<ReadableStream*> stream,
JS::ReadableStreamUnderlyingSource* source);
} // namespace js
#endif // builtin_streams_ReadableStream_h

Просмотреть файл

@ -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<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode, Handle<JSObject*> 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;
}

Просмотреть файл

@ -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 <stdint.h> // 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<ReadableStream>();
}
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<JS::ReadableStreamUnderlyingSource*>(
underlyingSource().toPrivate());
}
void setExternalSource(JS::ReadableStreamUnderlyingSource* underlyingSource) {
setUnderlyingSource(JS::PrivateValue(underlyingSource));
addFlags(Flag_ExternalSource);
}
static void clearUnderlyingSource(
JS::Handle<ReadableStreamController*> 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<ListObject>();
}
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<ReadableStreamController*> unwrappedController,
const char* action);
[[nodiscard]] extern JSObject* ReadableStreamControllerCancelSteps(
JSContext* cx, JS::Handle<ReadableStreamController*> unwrappedController,
JS::Handle<JS::Value> reason);
extern PromiseObject* ReadableStreamDefaultControllerPullSteps(
JSContext* cx,
JS::Handle<ReadableStreamDefaultController*> 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<js::ReadableStreamController>() const {
return is<js::ReadableStreamDefaultController>() ||
is<js::ReadableByteStreamController>();
}
namespace js {
inline ReadableStreamController* ReadableStream::controller() const {
return &getFixedSlot(Slot_Controller)
.toObject()
.as<ReadableStreamController>();
}
inline void ReadableStream::setController(
ReadableStreamController* controller) {
setFixedSlot(Slot_Controller, JS::ObjectValue(*controller));
}
} // namespace js
#endif // builtin_streams_ReadableStreamController_h

Просмотреть файл

@ -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<ReadableStreamController*> controller(
cx, TargetFromHandler<ReadableStreamController>(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<ReadableStreamController*> controller(
cx, TargetFromHandler<ReadableStreamController>(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<ReadableStreamController*> unwrappedController(
cx, UnwrapAndTypeCheckThis<ReadableStreamDefaultController>(
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<ReadableStreamController*> 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<ReadableStreamDefaultController*> unwrappedController(
cx, UnwrapAndTypeCheckThis<ReadableStreamDefaultController>(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<ReadableStreamDefaultController*> unwrappedController(
cx, UnwrapAndTypeCheckThis<ReadableStreamDefaultController>(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<ReadableStreamDefaultController*> unwrappedController(
cx, UnwrapAndTypeCheckThis<ReadableStreamDefaultController>(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<ReadableStreamController*> unwrappedController,
Handle<Value> reason) {
AssertSameCompartment(cx, reason);
// Step 1 of 3.11.5.1: If this.[[pendingPullIntos]] is not empty,
if (!unwrappedController->is<ReadableStreamDefaultController>()) {
Rooted<ListObject*> unwrappedPendingPullIntos(
cx, unwrappedController->as<ReadableByteStreamController>()
.pendingPullIntos());
if (unwrappedPendingPullIntos->length() != 0) {
// Step a: Let firstDescriptor be the first element of
// this.[[pendingPullIntos]].
PullIntoDescriptor* unwrappedDescriptor =
UnwrapAndDowncastObject<PullIntoDescriptor>(
cx, &unwrappedPendingPullIntos->get(0).toObject());
if (!unwrappedDescriptor) {
return nullptr;
}
// Step b: Set firstDescriptor.[[bytesFilled]] to 0.
unwrappedDescriptor->setBytesFilled(0);
}
}
Rooted<Value> 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<JSObject*> result(cx);
if (IsMaybeWrapped<TeeState>(unwrappedUnderlyingSource)) {
// The cancel algorithm given in ReadableStreamTee step 13 or 14.
MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is<TeeState>(),
"tee streams and controllers are always same-compartment with "
"the TeeState object");
Rooted<TeeState*> unwrappedTeeState(
cx, &unwrappedUnderlyingSource.toObject().as<TeeState>());
Rooted<ReadableStreamDefaultController*> unwrappedDefaultController(
cx, &unwrappedController->as<ReadableStreamDefaultController>());
result = ReadableStreamTee_Cancel(cx, unwrappedTeeState,
unwrappedDefaultController, reason);
} else if (unwrappedController->hasExternalSource()) {
// An embedding-provided cancel algorithm.
Rooted<Value> rval(cx);
{
AutoRealm ar(cx, unwrappedController);
JS::ReadableStreamUnderlyingSource* source =
unwrappedController->externalSource();
Rooted<ReadableStream*> stream(cx, unwrappedController->stream());
Rooted<Value> 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<Value> 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<Value> 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<ReadableStreamDefaultController*> unwrappedController) {
// Step 1: Let stream be this.[[controlledReadableStream]].
Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
// Step 2: If this.[[queue]] is not empty,
Rooted<ListObject*> unwrappedQueue(cx);
Rooted<Value> val(
cx, unwrappedController->getFixedSlot(StreamController::Slot_Queue));
if (val.isObject()) {
unwrappedQueue = &val.toObject().as<ListObject>();
}
if (unwrappedQueue && unwrappedQueue->length() != 0) {
// Step a: Let chunk be ! DequeueValue(this).
Rooted<Value> 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<Value> readResult(cx, ObjectValue(*readResultObj));
return PromiseObject::unforgeableResolveWithNonPromise(cx, readResult);
}
// Step 3: Let pendingPromise be
// ! ReadableStreamAddReadRequest(stream, forAuthorCode).
Rooted<PromiseObject*> 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;
}

Просмотреть файл

@ -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<ReadableStreamDefaultController>()
/**
* 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<ReadableStreamController*> unwrappedController(
cx, UnwrapCalleeSlot<ReadableStreamController>(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<Value> e = args.get(0);
Rooted<ReadableStreamController*> controller(
cx, UnwrapCalleeSlot<ReadableStreamController>(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<ReadableStreamController*> 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<JSObject*> 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<JSObject*> pullPromise(cx);
Rooted<Value> unwrappedUnderlyingSource(
cx, unwrappedController->underlyingSource());
if (IsMaybeWrapped<TeeState>(unwrappedUnderlyingSource)) {
// The pull algorithm given in ReadableStreamTee step 12.
MOZ_ASSERT(unwrappedUnderlyingSource.toObject().is<TeeState>(),
"tee streams and controllers are always same-compartment with "
"the TeeState object");
Rooted<TeeState*> unwrappedTeeState(
cx, &unwrappedUnderlyingSource.toObject().as<TeeState>());
pullPromise = ReadableStreamTee_Pull(cx, unwrappedTeeState);
} else if (unwrappedController->hasExternalSource()) {
// An embedding-provided pull algorithm.
{
AutoRealm ar(cx, unwrappedController);
JS::ReadableStreamUnderlyingSource* source =
unwrappedController->externalSource();
Rooted<ReadableStream*> 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<Value> 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<Value> 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<JSObject*> onPullFulfilled(
cx, NewHandler(cx, ControllerPullHandler, wrappedController));
if (!onPullFulfilled) {
return false;
}
Rooted<JSObject*> 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<ReadableStreamController*> 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<ReadableStreamDefaultController>()) {
controller->as<ReadableStreamDefaultController>().setStrategySize(
UndefinedHandleValue);
}
}
/**
* Streams spec, 3.10.5. ReadableStreamDefaultControllerClose ( controller )
*/
[[nodiscard]] bool js::ReadableStreamDefaultControllerClose(
JSContext* cx,
Handle<ReadableStreamDefaultController*> unwrappedController) {
// Step 1: Let stream be controller.[[controlledReadableStream]].
Rooted<ReadableStream*> 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<ListObject*> 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<ReadableStreamDefaultController*> unwrappedController,
Handle<Value> chunk) {
AssertSameCompartment(cx, chunk);
// Step 1: Let stream be controller.[[controlledReadableStream]].
Rooted<ReadableStream*> 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<Value> chunkSize(cx, Int32Value(1));
bool success = true;
Rooted<Value> 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<Value> exn(cx);
Rooted<SavedFrame*> 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<ReadableStreamController*> unwrappedController,
Handle<Value> e) {
MOZ_ASSERT(!cx->isExceptionPending());
AssertSameCompartment(cx, e);
// Step 1: Let stream be controller.[[controlledReadableStream]]
// (or controller.[[controlledReadableByteStream]]).
Rooted<ReadableStream*> 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<ReadableByteStreamController>()) {
Rooted<ReadableByteStreamController*> unwrappedByteStreamController(
cx, &unwrappedController->as<ReadableByteStreamController>());
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<ReadableStream*> stream,
SourceAlgorithms sourceAlgorithms, Handle<Value> underlyingSource,
Handle<Value> pullMethod, Handle<Value> cancelMethod, double highWaterMark,
Handle<Value> 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<ReadableStreamDefaultController*> controller(
cx, NewBuiltinClassInstance<ReadableStreamDefaultController>(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<Value> startResult(cx);
if (sourceAlgorithms == SourceAlgorithms::Script) {
Rooted<Value> 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<JSObject*> 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<JSObject*> onStartFulfilled(
cx, NewHandler(cx, ReadableStreamControllerStartHandler, controller));
if (!onStartFulfilled) {
return false;
}
Rooted<JSObject*> 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<ReadableStream*> stream,
Handle<Value> underlyingSource, double highWaterMark,
Handle<Value> 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<Value> 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<Value> 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);
}

Просмотреть файл

@ -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<ReadableStreamDefaultController*> unwrappedController,
JS::Handle<JS::Value> chunk);
[[nodiscard]] extern bool ReadableStreamControllerError(
JSContext* cx, JS::Handle<ReadableStreamController*> unwrappedController,
JS::Handle<JS::Value> e);
[[nodiscard]] extern bool ReadableStreamDefaultControllerClose(
JSContext* cx,
JS::Handle<ReadableStreamDefaultController*> unwrappedController);
[[nodiscard]] extern double ReadableStreamControllerGetDesiredSizeUnchecked(
ReadableStreamController* controller);
[[nodiscard]] extern bool ReadableStreamControllerCallPullIfNeeded(
JSContext* cx, JS::Handle<ReadableStreamController*> unwrappedController);
extern void ReadableStreamControllerClearAlgorithms(
JS::Handle<ReadableStreamController*> 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<ReadableStream*> stream,
SourceAlgorithms sourceAlgorithms, JS::Handle<JS::Value> underlyingSource,
JS::Handle<JS::Value> pullMethod, JS::Handle<JS::Value> cancelMethod,
double highWaterMark, JS::Handle<JS::Value> size);
[[nodiscard]] extern bool
SetUpReadableStreamDefaultControllerFromUnderlyingSource(
JSContext* cx, JS::Handle<ReadableStream*> stream,
JS::Handle<JS::Value> underlyingSource, double highWaterMark,
JS::Handle<JS::Value> sizeAlgorithm);
} // namespace js
#endif // builtin_streams_ReadableStreamDefaultControllerOperations_h

Просмотреть файл

@ -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<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode,
Handle<JSObject*> proto /* = nullptr */) {
Rooted<ReadableStreamDefaultReader*> reader(
cx, NewObjectWithClassProto<ReadableStreamDefaultReader>(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<JSObject*> proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Null, &proto)) {
return false;
}
// Step 1: If ! IsReadableStream(stream) is false, throw a TypeError
// exception.
Rooted<ReadableStream*> unwrappedStream(
cx, UnwrapAndTypeCheckArgument<ReadableStream>(
cx, args, "ReadableStreamDefaultReader constructor", 0));
if (!unwrappedStream) {
return false;
}
Rooted<JSObject*> 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<ReadableStreamDefaultReader*> unwrappedReader(
cx, UnwrapAndTypeCheckThis<ReadableStreamDefaultReader>(cx, args,
"get closed"));
if (!unwrappedReader) {
return ReturnPromiseRejectedWithPendingError(cx, args);
}
// Step 2: Return this.[[closedPromise]].
Rooted<JSObject*> 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<ReadableStreamDefaultReader*> unwrappedReader(
cx,
UnwrapAndTypeCheckThis<ReadableStreamDefaultReader>(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<ReadableStreamDefaultReader*> unwrappedReader(
cx,
UnwrapAndTypeCheckThis<ReadableStreamDefaultReader>(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<ReadableStreamDefaultReader*> reader(
cx, UnwrapAndTypeCheckThis<ReadableStreamDefaultReader>(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<ListObject>();
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);

Просмотреть файл

@ -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 <stdint.h> // 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<ReadableStream*> unwrappedStream) {
// Step 1: Assert: ! IsReadableStream{BYOB,Default}Reader(stream.[[reader]])
// is true.
// (Only default readers exist so far.)
Rooted<ReadableStreamReader*> unwrappedReader(
cx, UnwrapReaderFromStream(cx, unwrappedStream));
if (!unwrappedReader) {
return nullptr;
}
MOZ_ASSERT(unwrappedReader->is<ReadableStreamDefaultReader>());
// 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<ReadableStreamDefaultReader>(),
unwrappedStream->readable());
// Step 3: Let promise be a new promise.
Rooted<PromiseObject*> 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<ReadableStream*> unwrappedStream,
Handle<Value> 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<Value> 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<ReadableStreamController*> unwrappedController(
cx, unwrappedStream->controller());
Rooted<JSObject*> 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<PropertyName*> funName = cx->names().empty;
Rooted<JSFunction*> 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<ReadableStream*> 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<ReadableStreamReader*> unwrappedReader(
cx, UnwrapReaderFromStream(cx, unwrappedStream));
if (!unwrappedReader) {
return false;
}
// Step 5: If ! IsReadableStreamDefaultReader(reader) is true,
if (unwrappedReader->is<ReadableStreamDefaultReader>()) {
ForAuthorCodeBool forAuthorCode = unwrappedReader->forAuthorCode();
// Step a: Repeat for each readRequest that is an element of
// reader.[[readRequests]],
Rooted<ListObject*> unwrappedReadRequests(cx, unwrappedReader->requests());
uint32_t len = unwrappedReadRequests->length();
Rooted<JSObject*> readRequest(cx);
Rooted<JSObject*> resultObj(cx);
Rooted<Value> resultVal(cx);
for (uint32_t i = 0; i < len; i++) {
// Step i: Resolve readRequest.[[promise]] with
// ! ReadableStreamCreateReadResult(undefined, true,
// readRequest.[[forAuthorCode]]).
readRequest = &unwrappedReadRequests->getAs<JSObject>(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> value, bool done,
ForAuthorCodeBool forAuthorCode) {
// Step 1: Let prototype be null.
// Step 2: If forAuthorCode is true, set prototype to %ObjectPrototype%.
Rooted<PlainObject*> 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<ReadableStream*> unwrappedStream, Handle<Value> 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<Value> 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<ReadableStreamReader*> 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<ListObject*> unwrappedReadRequests(cx, unwrappedReader->requests());
Rooted<JSObject*> 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<JSObject*> closedPromise(cx, unwrappedReader->closedPromise());
js::SetSettledPromiseIsHandled(cx, closedPromise.as<PromiseObject>());
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<Value> 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<ReadableStream*> unwrappedStream, Handle<Value> chunk,
bool done) {
cx->check(chunk);
// Step 1: Let reader be stream.[[reader]].
Rooted<ReadableStreamReader*> 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<ListObject*> unwrappedReadIntoRequests(cx,
unwrappedReader->requests());
Rooted<JSObject*> readIntoRequest(
cx, &unwrappedReadIntoRequests->popFirstAs<JSObject>(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<Value> 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<ReadableStream*> 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<ReadableStreamReader*> 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<ReadableStreamDefaultReader>();
return true;
}

Просмотреть файл

@ -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<ReadableStream*> unwrappedStream);
[[nodiscard]] extern JSObject* ReadableStreamCancel(
JSContext* cx, JS::Handle<ReadableStream*> unwrappedStream,
JS::Handle<JS::Value> reason);
[[nodiscard]] extern bool ReadableStreamCloseInternal(
JSContext* cx, JS::Handle<ReadableStream*> unwrappedStream);
[[nodiscard]] extern PlainObject* ReadableStreamCreateReadResult(
JSContext* cx, JS::Handle<JS::Value> value, bool done,
ForAuthorCodeBool forAuthorCode);
[[nodiscard]] extern bool ReadableStreamErrorInternal(
JSContext* cx, JS::Handle<ReadableStream*> unwrappedStream,
JS::Handle<JS::Value> e);
[[nodiscard]] extern bool ReadableStreamFulfillReadOrReadIntoRequest(
JSContext* cx, JS::Handle<ReadableStream*> unwrappedStream,
JS::Handle<JS::Value> chunk, bool done);
extern uint32_t ReadableStreamGetNumReadRequests(ReadableStream* stream);
[[nodiscard]] extern bool ReadableStreamHasDefaultReader(
JSContext* cx, JS::Handle<ReadableStream*> unwrappedStream, bool* result);
} // namespace js
#endif // builtin_streams_ReadableStreamInternals_h

Просмотреть файл

@ -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<Value> underlyingSource,
Handle<Value> pullMethod = UndefinedHandleValue,
Handle<Value> cancelMethod = UndefinedHandleValue, double highWaterMark = 1,
Handle<Value> sizeAlgorithm = UndefinedHandleValue,
Handle<JSObject*> 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<ReadableStream*> 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<JSObject*> proto /* = nullptr */) {
// In the spec, InitializeReadableStream is always passed a newly created
// ReadableStream object. We instead create it here and return it below.
Rooted<ReadableStream*> stream(
cx, NewObjectWithClassProto<ReadableStream>(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<TeeState*> unwrappedTeeState(cx,
UnwrapCalleeSlot<TeeState>(cx, args, 0));
if (!unwrappedTeeState) {
return false;
}
Handle<Value> resultVal = args.get(0);
// Step 12.c.i: Set reading to false.
unwrappedTeeState->unsetReading();
// Step 12.c.ii: Assert: Type(result) is Object.
Rooted<JSObject*> 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<Value> 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<ReadableStreamDefaultController*> unwrappedBranch1(
cx, unwrappedTeeState->branch1());
if (!ReadableStreamDefaultControllerClose(cx, unwrappedBranch1)) {
return false;
}
}
// Step 3: If canceled2 is false, perform
// ! ReadableStreamDefaultControllerClose(branch2.[[controller]]).
if (!unwrappedTeeState->canceled2()) {
Rooted<ReadableStreamDefaultController*> 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<PromiseObject*> 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> 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<ReadableStreamDefaultController*> 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<TeeState*> 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<ReadableStreamDefaultReader*> unwrappedReader(cx);
{
Rooted<ReadableStream*> unwrappedStream(
cx, UnwrapInternalSlot<ReadableStream>(cx, unwrappedTeeState,
TeeState::Slot_Stream));
if (!unwrappedStream) {
return nullptr;
}
ReadableStreamReader* unwrappedReaderObj =
UnwrapReaderFromStream(cx, unwrappedStream);
if (!unwrappedReaderObj) {
return nullptr;
}
unwrappedReader = &unwrappedReaderObj->as<ReadableStreamDefaultReader>();
}
// 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<PromiseObject*> 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<JSObject*> teeState(cx, unwrappedTeeState);
if (!cx->compartment()->wrap(cx, &teeState)) {
return nullptr;
}
Rooted<JSObject*> 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<TeeState*> unwrappedTeeState,
JS::Handle<ReadableStreamDefaultController*> unwrappedBranch,
JS::Handle<Value> reason) {
Rooted<ReadableStream*> unwrappedStream(
cx, UnwrapInternalSlot<ReadableStream>(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<Value> 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<PromiseObject*> 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<Value> compositeReason(cx);
{
Rooted<Value> reason1(cx, unwrappedTeeState->reason1());
Rooted<Value> 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<JSObject*> 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<Value> cancelResultVal(cx, ObjectValue(*cancelResult));
if (!ResolveUnwrappedPromiseWithValue(cx, unwrappedCancelPromise,
cancelResultVal)) {
return nullptr;
}
}
}
// Step 13/14.d: Return cancelPromise.
Rooted<JSObject*> 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*> teeState(cx, TargetFromHandler<TeeState>(args));
Handle<Value> reason = args.get(0);
Rooted<ReadableStreamDefaultController*> 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<PromiseObject*> 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<ReadableStream*> unwrappedStream,
bool cloneForBranch2, JS::MutableHandle<ReadableStream*> branch1Stream,
JS::MutableHandle<ReadableStream*> 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<ReadableStreamDefaultReader*> 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*> 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<Value> underlyingSource(cx, ObjectValue(*teeState));
branch1Stream.set(
CreateReadableStream(cx, SourceAlgorithms::Tee, underlyingSource));
if (!branch1Stream) {
return false;
}
Rooted<ReadableStreamDefaultController*> branch1(cx);
branch1 = &branch1Stream->controller()->as<ReadableStreamDefaultController>();
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<ReadableStreamDefaultController*> branch2(cx);
branch2 = &branch2Stream->controller()->as<ReadableStreamDefaultController>();
branch2->setTeeBranch2();
teeState->setBranch2(branch2);
// Step 18: Upon rejection of reader.[[closedPromise]] with reason r, [...]
Rooted<JSObject*> closedPromise(cx, reader->closedPromise());
Rooted<JSObject*> 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;
}

Просмотреть файл

@ -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<TeeState*> unwrappedTeeState);
[[nodiscard]] extern JSObject* ReadableStreamTee_Cancel(
JSContext* cx, JS::Handle<TeeState*> unwrappedTeeState,
JS::Handle<ReadableStreamDefaultController*> unwrappedBranch,
JS::Handle<JS::Value> reason);
[[nodiscard]] extern bool ReadableStreamTee(
JSContext* cx, JS::Handle<ReadableStream*> unwrappedStream,
bool cloneForBranch2, JS::MutableHandle<ReadableStream*> branch1Stream,
JS::MutableHandle<ReadableStream*> branch2Stream);
} // namespace js
#endif // builtin_streams_ReadableStreamOperations_h

Просмотреть файл

@ -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<ReadableStreamReader*> reader) {
MOZ_ASSERT(reader->hasStream());
return UnwrapInternalSlot<ReadableStream>(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<ReadableStream*> stream) {
return UnwrapInternalSlot<ReadableStreamReader>(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<ReadableStreamReader>();
if (!readerObj) {
return nullptr;
}
}
return &readerObj->as<ReadableStreamReader>();
}
} // namespace js
#endif // builtin_streams_ReadableStreamReader_inl_h

Просмотреть файл

@ -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<ReadableStreamDefaultReader>()
// Streams spec, 3.8.2. IsReadableStreamBYOBReader ( x )
// Implemented via is<ReadableStreamBYOBReader>()
/**
* Streams spec, 3.8.3. ReadableStreamReaderGenericCancel ( reader, reason )
*/
[[nodiscard]] JSObject* js::ReadableStreamReaderGenericCancel(
JSContext* cx, Handle<ReadableStreamReader*> unwrappedReader,
Handle<Value> reason) {
// Step 1: Let stream be reader.[[ownerReadableStream]].
// Step 2: Assert: stream is not undefined (implicit).
Rooted<ReadableStream*> 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<ReadableStreamReader*> reader,
Handle<ReadableStream*> unwrappedStream, ForAuthorCodeBool forAuthorCode) {
cx->check(reader);
// Step 1: Set reader.[[forAuthorCode]] to true.
reader->setForAuthorCode(forAuthorCode);
// Step 2: Set reader.[[ownerReadableStream]] to stream.
{
Rooted<JSObject*> 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<PromiseObject*> 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<Value> 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<JSObject*> 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<ReadableStreamReader*> unwrappedReader) {
// Step 1: Assert: reader.[[ownerReadableStream]] is not undefined.
Rooted<ReadableStream*> 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<Value> 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<PromiseObject*> unwrappedClosedPromise(cx);
if (unwrappedStream->readable()) {
unwrappedClosedPromise = UnwrapInternalSlot<PromiseObject>(
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<JSObject*> closedPromise(cx,
PromiseObject::unforgeableReject(cx, exn));
if (!closedPromise) {
return false;
}
unwrappedClosedPromise = &closedPromise->as<PromiseObject>();
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<ReadableStreamDefaultReader*> 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<ReadableStream*> 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<Value> 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<Value> 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<ReadableStreamController*> unwrappedController(
cx, unwrappedStream->controller());
return ReadableStreamControllerPullSteps(cx, unwrappedController);
}

Просмотреть файл

@ -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 <https://streams.spec.whatwg.org/#readable-stream-create-read-result>.
*/
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<ListObject>();
}
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<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode,
JS::Handle<JSObject*> proto = nullptr);
[[nodiscard]] extern JSObject* ReadableStreamReaderGenericCancel(
JSContext* cx, JS::Handle<ReadableStreamReader*> unwrappedReader,
JS::Handle<JS::Value> reason);
[[nodiscard]] extern bool ReadableStreamReaderGenericInitialize(
JSContext* cx, JS::Handle<ReadableStreamReader*> reader,
JS::Handle<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode);
[[nodiscard]] extern bool ReadableStreamReaderGenericRelease(
JSContext* cx, JS::Handle<ReadableStreamReader*> unwrappedReader);
[[nodiscard]] extern PromiseObject* ReadableStreamDefaultReaderRead(
JSContext* cx, JS::Handle<ReadableStreamDefaultReader*> unwrappedReader);
} // namespace js
template <>
inline bool JSObject::is<js::ReadableStreamReader>() const {
return is<js::ReadableStreamDefaultReader>();
}
namespace js {
[[nodiscard]] extern JSObject* CreateReadableStreamBYOBReader(
JSContext* cx, JS::Handle<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode, JS::Handle<JSObject*> proto = nullptr);
} // namespace js
#endif // builtin_streams_ReadableStreamReader_h

Просмотреть файл

@ -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 <stdint.h> // 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<ReadableStream>();
}
JS_PUBLIC_API JSObject* JS::NewReadableDefaultStreamObject(
JSContext* cx, JS::Handle<JSObject*> underlyingSource /* = nullptr */,
JS::Handle<JSFunction*> size /* = nullptr */,
double highWaterMark /* = 1 */,
JS::Handle<JSObject*> 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<ReadableStream*> stream(cx, ReadableStream::create(cx));
if (!stream) {
return nullptr;
}
Rooted<Value> sourceVal(cx);
if (underlyingSource) {
sourceVal.setObject(*underlyingSource);
} else {
JSObject* source = NewPlainObject(cx);
if (!source) {
return nullptr;
}
sourceVal.setObject(*source);
}
Rooted<Value> 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<JSObject*> 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<ReadableStream>();
}
JS_PUBLIC_API bool JS::IsReadableStreamReader(JSObject* obj) {
return obj->canUnwrapAs<ReadableStreamDefaultReader>();
}
JS_PUBLIC_API bool JS::IsReadableStreamDefaultReader(JSObject* obj) {
return obj->canUnwrapAs<ReadableStreamDefaultReader>();
}
template <class T>
[[nodiscard]] static T* APIUnwrapAndDowncast(JSContext* cx, JSObject* obj) {
cx->check(obj);
return UnwrapAndDowncastObject<T>(cx, obj);
}
JS_PUBLIC_API bool JS::ReadableStreamIsReadable(JSContext* cx,
Handle<JSObject*> streamObj,
bool* result) {
ReadableStream* unwrappedStream =
APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
if (!unwrappedStream) {
return false;
}
*result = unwrappedStream->readable();
return true;
}
JS_PUBLIC_API bool JS::ReadableStreamIsLocked(JSContext* cx,
Handle<JSObject*> streamObj,
bool* result) {
ReadableStream* unwrappedStream =
APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
if (!unwrappedStream) {
return false;
}
*result = unwrappedStream->locked();
return true;
}
JS_PUBLIC_API bool JS::ReadableStreamIsDisturbed(JSContext* cx,
Handle<JSObject*> streamObj,
bool* result) {
ReadableStream* unwrappedStream =
APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
if (!unwrappedStream) {
return false;
}
*result = unwrappedStream->disturbed();
return true;
}
JS_PUBLIC_API JSObject* JS::ReadableStreamCancel(JSContext* cx,
Handle<JSObject*> streamObj,
Handle<Value> reason) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
cx->check(reason);
Rooted<ReadableStream*> unwrappedStream(
cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
if (!unwrappedStream) {
return nullptr;
}
return js::ReadableStreamCancel(cx, unwrappedStream, reason);
}
JS_PUBLIC_API bool JS::ReadableStreamGetMode(JSContext* cx,
Handle<JSObject*> streamObj,
JS::ReadableStreamMode* mode) {
ReadableStream* unwrappedStream =
APIUnwrapAndDowncast<ReadableStream>(cx, streamObj);
if (!unwrappedStream) {
return false;
}
*mode = unwrappedStream->mode();
return true;
}
JS_PUBLIC_API JSObject* JS::ReadableStreamGetReader(
JSContext* cx, Handle<JSObject*> streamObj, ReadableStreamReaderMode mode) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStream*> unwrappedStream(
cx, APIUnwrapAndDowncast<ReadableStream>(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<JSObject*> streamObj,
JS::ReadableStreamUnderlyingSource** source) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStream*> unwrappedStream(
cx, APIUnwrapAndDowncast<ReadableStream>(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<ReadableByteStreamController>();
unwrappedController->setSourceLocked();
*source = unwrappedController->externalSource();
return true;
}
JS_PUBLIC_API bool JS::ReadableStreamReleaseExternalUnderlyingSource(
JSContext* cx, Handle<JSObject*> streamObj) {
ReadableStream* unwrappedStream =
APIUnwrapAndDowncast<ReadableStream>(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<JSObject*> streamObj, uint32_t availableData) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStream*> unwrappedStream(
cx, APIUnwrapAndDowncast<ReadableStream>(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<ReadableByteStreamController*> unwrappedController(
cx, &unwrappedStream->controller()->as<ReadableByteStreamController>());
// 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<ArrayBufferViewObject*> transferredView(
cx, &viewObj->as<ArrayBufferViewObject>());
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<Value> 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<JSObject*> streamObj,
MutableHandle<JSObject*> branch1Obj,
MutableHandle<JSObject*> branch2Obj) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStream*> unwrappedStream(
cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
if (!unwrappedStream) {
return false;
}
Rooted<ReadableStream*> branch1Stream(cx);
Rooted<ReadableStream*> 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<ReadableStream>(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<JSObject*> streamObj) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStream*> unwrappedStream(
cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
if (!unwrappedStream) {
return false;
}
Rooted<ReadableStreamController*> unwrappedControllerObj(
cx, unwrappedStream->controller());
if (!CheckReadableStreamControllerCanCloseOrEnqueue(
cx, unwrappedControllerObj, "close")) {
return false;
}
if (unwrappedControllerObj->is<ReadableStreamDefaultController>()) {
Rooted<ReadableStreamDefaultController*> unwrappedController(cx);
unwrappedController =
&unwrappedControllerObj->as<ReadableStreamDefaultController>();
return ReadableStreamDefaultControllerClose(cx, unwrappedController);
}
Rooted<ReadableByteStreamController*> unwrappedController(cx);
unwrappedController =
&unwrappedControllerObj->as<ReadableByteStreamController>();
return ReadableByteStreamControllerClose(cx, unwrappedController);
}
JS_PUBLIC_API bool JS::ReadableStreamEnqueue(JSContext* cx,
Handle<JSObject*> streamObj,
Handle<Value> chunk) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
cx->check(chunk);
Rooted<ReadableStream*> unwrappedStream(
cx, APIUnwrapAndDowncast<ReadableStream>(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<ReadableStreamDefaultController*> unwrappedController(cx);
unwrappedController =
&unwrappedStream->controller()->as<ReadableStreamDefaultController>();
MOZ_ASSERT(!unwrappedController->closeRequested());
MOZ_ASSERT(unwrappedStream->readable());
return ReadableStreamDefaultControllerEnqueue(cx, unwrappedController, chunk);
}
JS_PUBLIC_API bool JS::ReadableStreamError(JSContext* cx,
Handle<JSObject*> streamObj,
Handle<Value> error) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
cx->check(error);
Rooted<ReadableStream*> unwrappedStream(
cx, APIUnwrapAndDowncast<ReadableStream>(cx, streamObj));
if (!unwrappedStream) {
return false;
}
Rooted<ReadableStreamController*> unwrappedController(
cx, unwrappedStream->controller());
return ReadableStreamControllerError(cx, unwrappedController, error);
}
JS_PUBLIC_API bool JS::ReadableStreamReaderIsClosed(JSContext* cx,
Handle<JSObject*> readerObj,
bool* result) {
Rooted<ReadableStreamReader*> unwrappedReader(
cx, APIUnwrapAndDowncast<ReadableStreamReader>(cx, readerObj));
if (!unwrappedReader) {
return false;
}
*result = unwrappedReader->isClosed();
return true;
}
JS_PUBLIC_API bool JS::ReadableStreamReaderCancel(JSContext* cx,
Handle<JSObject*> readerObj,
Handle<Value> reason) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
cx->check(reason);
Rooted<ReadableStreamReader*> unwrappedReader(
cx, APIUnwrapAndDowncast<ReadableStreamReader>(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<JSObject*> readerObj) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStreamReader*> unwrappedReader(
cx, APIUnwrapAndDowncast<ReadableStreamReader>(cx, readerObj));
if (!unwrappedReader) {
return false;
}
MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
"C++ code should not touch readers created by scripts");
#ifdef DEBUG
Rooted<ReadableStream*> 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<JSObject*> readerObj) {
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStreamDefaultReader*> unwrappedReader(
cx, APIUnwrapAndDowncast<ReadableStreamDefaultReader>(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);
}

Просмотреть файл

@ -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<js::StreamController>() const {
return is<js::ReadableStreamDefaultController>() ||
is<js::ReadableByteStreamController>();
}
#endif // builtin_streams_ReadableStreamController_inl_h

Просмотреть файл

@ -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<ListObject>();
}
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<js::StreamController>() const;
#endif // builtin_streams_StreamController_h

Просмотреть файл

@ -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<ReadableStream*> unwrappedStream) {
Rooted<TeeState*> state(cx, NewBuiltinClassInstance<TeeState>(cx));
if (!state) {
return nullptr;
}
Rooted<PromiseObject*> cancelPromise(
cx, PromiseObject::createSkippingExecutor(cx));
if (!cancelPromise) {
return nullptr;
}
state->setFixedSlot(Slot_Flags, Int32Value(0));
state->setFixedSlot(Slot_CancelPromise, ObjectValue(*cancelPromise));
Rooted<JSObject*> 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)};

Просмотреть файл

@ -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 <stdint.h> // 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<PromiseObject>();
}
ReadableStreamDefaultController* branch1() {
ReadableStreamDefaultController* controller =
&getFixedSlot(Slot_Branch1)
.toObject()
.as<ReadableStreamDefaultController>();
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<ReadableStreamDefaultController>();
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<ReadableStream*> unwrappedStream);
};
} // namespace js
#endif // builtin_streams_TeeState_h

Просмотреть файл

@ -33,7 +33,7 @@ static const JSClass* getGlobalClass() {
static JSObject* jsfuzz_createGlobal(JSContext* cx, JSPrincipals* principals) { static JSObject* jsfuzz_createGlobal(JSContext* cx, JSPrincipals* principals) {
/* Create the global object. */ /* Create the global object. */
JS::RealmOptions options; JS::RealmOptions options;
options.creationOptions().setStreamsEnabled(true).setWeakRefsEnabled( options.creationOptions().setWeakRefsEnabled(
JS::WeakRefSpecifier::EnabledWithCleanupSome); JS::WeakRefSpecifier::EnabledWithCleanupSome);
return JS_NewGlobalObject(cx, getGlobalClass(), principals, return JS_NewGlobalObject(cx, getGlobalClass(), principals,
JS::FireOnNewGlobalHook, options); JS::FireOnNewGlobalHook, options);

Просмотреть файл

@ -162,11 +162,6 @@ if not CONFIG["JS_CODEGEN_NONE"]:
"testsJit.cpp", "testsJit.cpp",
] ]
if CONFIG["MOZ_JS_STREAMS"]:
UNIFIED_SOURCES += [
"testReadableStream.cpp",
]
if CONFIG["NIGHTLY_BUILD"]: if CONFIG["NIGHTLY_BUILD"]:
# The Error interceptor only exists on Nightly. # The Error interceptor only exists on Nightly.
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -116,7 +116,6 @@ JSObject* JSAPITest::createGlobal(JSPrincipals* principals) {
JS::RootedObject newGlobal(cx); JS::RootedObject newGlobal(cx);
JS::RealmOptions options; JS::RealmOptions options;
options.creationOptions() options.creationOptions()
.setStreamsEnabled(true)
.setWeakRefsEnabled(JS::WeakRefSpecifier::EnabledWithCleanupSome) .setWeakRefsEnabled(JS::WeakRefSpecifier::EnabledWithCleanupSome)
.setSharedMemoryAndAtomicsEnabled(true); .setSharedMemoryAndAtomicsEnabled(true);
newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals,

Просмотреть файл

@ -200,7 +200,6 @@ EXPORTS.js += [
"../public/SourceText.h", "../public/SourceText.h",
"../public/StableStringChars.h", "../public/StableStringChars.h",
"../public/Stack.h", "../public/Stack.h",
"../public/Stream.h",
"../public/StreamConsumer.h", "../public/StreamConsumer.h",
"../public/String.h", "../public/String.h",
"../public/StructuredClone.h", "../public/StructuredClone.h",

Просмотреть файл

@ -604,7 +604,6 @@ bool shell::enableTestWasmAwaitTier2 = false;
bool shell::enableSourcePragmas = true; bool shell::enableSourcePragmas = true;
bool shell::enableAsyncStacks = false; bool shell::enableAsyncStacks = false;
bool shell::enableAsyncStackCaptureDebuggeeOnly = false; bool shell::enableAsyncStackCaptureDebuggeeOnly = false;
bool shell::enableStreams = false;
bool shell::enableWeakRefs = false; bool shell::enableWeakRefs = false;
bool shell::enableToSource = false; bool shell::enableToSource = false;
bool shell::enablePropertyErrorMessageFix = false; bool shell::enablePropertyErrorMessageFix = false;
@ -3841,7 +3840,6 @@ static void SetStandardRealmOptions(JS::RealmOptions& options) {
options.creationOptions() options.creationOptions()
.setSharedMemoryAndAtomicsEnabled(enableSharedMemory) .setSharedMemoryAndAtomicsEnabled(enableSharedMemory)
.setCoopAndCoepEnabled(false) .setCoopAndCoepEnabled(false)
.setStreamsEnabled(enableStreams)
.setWeakRefsEnabled(enableWeakRefs .setWeakRefsEnabled(enableWeakRefs
? JS::WeakRefSpecifier::EnabledWithCleanupSome ? JS::WeakRefSpecifier::EnabledWithCleanupSome
: JS::WeakRefSpecifier::Disabled) : JS::WeakRefSpecifier::Disabled)
@ -10601,7 +10599,6 @@ static bool SetContextOptions(JSContext* cx, const OptionParser& op) {
enableAsyncStacks = !op.getBoolOption("no-async-stacks"); enableAsyncStacks = !op.getBoolOption("no-async-stacks");
enableAsyncStackCaptureDebuggeeOnly = enableAsyncStackCaptureDebuggeeOnly =
op.getBoolOption("async-stacks-capture-debuggee-only"); op.getBoolOption("async-stacks-capture-debuggee-only");
enableStreams = !op.getBoolOption("no-streams");
enableWeakRefs = !op.getBoolOption("disable-weak-refs"); enableWeakRefs = !op.getBoolOption("disable-weak-refs");
enableToSource = !op.getBoolOption("disable-tosource"); enableToSource = !op.getBoolOption("disable-tosource");
enablePropertyErrorMessageFix = enablePropertyErrorMessageFix =
@ -11575,9 +11572,6 @@ int main(int argc, char** argv) {
!op.addBoolOption('\0', "less-debug-code", !op.addBoolOption('\0', "less-debug-code",
"Emit less machine code for " "Emit less machine code for "
"checking assertions under DEBUG.") || "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-weak-refs", "Disable weak references") ||
!op.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") || !op.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") ||
!op.addBoolOption('\0', "disable-property-error-message-fix", !op.addBoolOption('\0', "disable-property-error-message-fix",

Просмотреть файл

@ -122,11 +122,6 @@ extern bool enableTestWasmAwaitTier2;
extern bool enableSourcePragmas; extern bool enableSourcePragmas;
extern bool enableAsyncStacks; extern bool enableAsyncStacks;
extern bool enableAsyncStackCaptureDebuggeeOnly; extern bool enableAsyncStackCaptureDebuggeeOnly;
extern bool enableStreams;
extern bool enableReadableByteStreams;
extern bool enableBYOBStreamReaders;
extern bool enableReadableStreamPipeTo;
extern bool enableWeakRefs; extern bool enableWeakRefs;
extern bool enableToSource; extern bool enableToSource;
extern bool enablePropertyErrorMessageFix; extern bool enablePropertyErrorMessageFix;

Просмотреть файл

@ -25,11 +25,6 @@
#include "builtin/FinalizationRegistryObject.h" #include "builtin/FinalizationRegistryObject.h"
#include "builtin/MapObject.h" #include "builtin/MapObject.h"
#include "builtin/ShadowRealm.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/Symbol.h"
#include "builtin/WeakMapObject.h" #include "builtin/WeakMapObject.h"
#include "builtin/WeakRefObject.h" #include "builtin/WeakRefObject.h"
@ -180,15 +175,6 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) {
case JSProto_RelativeTimeFormat: case JSProto_RelativeTimeFormat:
return false; return false;
#endif #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. // Return true if the given constructor has been disabled at run-time.
case JSProto_Atomics: case JSProto_Atomics:

Просмотреть файл

@ -53,7 +53,6 @@
#include "js/MemoryMetrics.h" #include "js/MemoryMetrics.h"
#include "js/Object.h" // JS::GetClass #include "js/Object.h" // JS::GetClass
#include "js/RealmIterators.h" #include "js/RealmIterators.h"
#include "js/Stream.h" // JS::AbortSignalIsAborted, JS::InitPipeToHandling
#include "js/SliceBudget.h" #include "js/SliceBudget.h"
#include "js/UbiNode.h" #include "js/UbiNode.h"
#include "js/UbiNodeUtils.h" #include "js/UbiNodeUtils.h"