Issue #1442 - Part 3: Implement ReadableStream and associated classes in the JS engine. https://bugzilla.mozilla.org/show_bug.cgi?id=1272697

This commit is contained in:
Brian Smith 2023-09-27 16:00:10 -05:00 committed by roytam1
commit 54e84f0f1d
13 changed files with 5178 additions and 5 deletions

4957
js/src/builtin/Stream.cpp Normal file

File diff suppressed because it is too large Load diff

118
js/src/builtin/Stream.h Normal file
View file

@ -0,0 +1,118 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "vm/NativeObject.h"
namespace js {
class AutoSetNewObjectMetadata;
class ReadableStream : public NativeObject
{
public:
static ReadableStream* createDefaultStream(JSContext* cx, HandleValue underlyingSource,
HandleValue size, HandleValue highWaterMark);
static ReadableStream* createByteStream(JSContext* cx, HandleValue underlyingSource,
HandleValue highWaterMark);
inline bool readable() const;
inline bool closed() const;
inline bool errored() const;
inline bool disturbed() const;
enum State {
Readable = 1 << 0,
Closed = 1 << 1,
Errored = 1 << 2,
Disturbed = 1 << 3
};
private:
static ReadableStream* createStream(JSContext* cx);
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamDefaultReader : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamBYOBReader : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamDefaultController : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableByteStreamController : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamBYOBRequest : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ByteLengthQueuingStrategy : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class CountQueuingStrategy : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
} // namespace js
#endif /* builtin_Stream_h */

View file

@ -1441,6 +1441,14 @@ RejectPromise(JSContext* cx, unsigned argc, Value* vp)
return result;
}
static bool
StreamsAreEnabled(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(cx->options().streams());
return true;
}
static unsigned finalizeCount = 0;
static void
@ -4178,6 +4186,10 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
"rejectPromise(promise, reason)",
" Reject a Promise by calling the JSAPI function JS::RejectPromise."),
JS_FN_HELP("streamsAreEnabled", StreamsAreEnabled, 0, 0,
"streamsAreEnabled()",
" Returns a boolean indicating whether WHATWG Streams are enabled for the current compartment."),
JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
"makeFinalizeObserver()",
" Get a special object whose finalization increases the counter returned\n"

View file

@ -613,11 +613,38 @@ MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of i
MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
// Async Iteration
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'")
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_SYNTAXERR, "'for await' loop should be used with 'of'")
MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator")
MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator")
MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
// ReadableStream
MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSOURCE_TYPE_WRONG,0, JSEXN_RANGEERR,"'underlyingSource.type' must be \"bytes\" or undefined.")
MSG_DEF(JSMSG_READABLESTREAM_INVALID_READER_MODE, 0, JSEXN_RANGEERR,"'mode' must be \"byob\" or undefined.")
MSG_DEF(JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, 1, JSEXN_RANGEERR, "'{0}' must be a finite, non-negative number.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_BYTESWRITTEN, 0, JSEXN_RANGEERR, "'bytesWritten' exceeds remaining length.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_SIZE, 0, JSEXN_RANGEERR, "view size does not match requested data.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_OFFSET, 0, JSEXN_RANGEERR, "view offset does not match requested position.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_LOCKED, 1, JSEXN_TYPEERR, "The ReadableStream method '{0}' may only be called on a locked stream.")
MSG_DEF(JSMSG_READABLESTREAM_LOCKED, 0, JSEXN_TYPEERR, "A Reader may only be created for an unlocked ReadableStream.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, 0, JSEXN_TYPEERR, "ReadableStream.getReader('byob') requires a ReadableByteStreamController.")
MSG_DEF(JSMSG_READABLESTREAM_CONTROLLER_SET, 0, JSEXN_TYPEERR, "The ReadableStream already has a controller defined.")
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_OWNED, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may only be called on a reader owned by a stream.")
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_EMPTY, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may not be called on a reader with read requests.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREADER_READ_EMPTY_VIEW, 0, JSEXN_TYPEERR, "ReadableStreamBYOBReader.read() was passed an empty TypedArrayBuffer view.")
MSG_DEF(JSMSG_READABLESTREAMREADER_RELEASED, 0, JSEXN_TYPEERR, "The ReadableStream reader was released.")
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_CLOSED, 1, JSEXN_TYPEERR, "The ReadableStream controller method '{0}' called on a stream already closing.")
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, 1, JSEXN_TYPEERR, "The ReadableStream controller method '{0}' may only be called on a stream in the 'readable' state.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE,0, JSEXN_RANGEERR, "ReadableByteStreamController requires a positive integer or undefined for 'autoAllocateChunkSize'.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, 0, JSEXN_TYPEERR, "ReadableByteStreamController passed a bad chunk.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL, 0, JSEXN_TYPEERR, "The ReadableByteStreamController cannot be closed while the buffer is being filled.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_NO_CONTROLLER, 1, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method '{0}' called on a request with no controller.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_RESPOND_CLOSED, 0, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method 'respond' called with non-zero number of bytes with a closed controller.")
MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "ReadableStream method {0} not yet implemented")
// Other Stream-related
MSG_DEF(JSMSG_STREAM_INVALID_HIGHWATERMARK, 0, JSEXN_RANGEERR, "'highWaterMark' must be a non-negative, non-NaN number.")
// BigInt
MSG_DEF(JSMSG_BIGINT_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert BigInt to number")
MSG_DEF(JSMSG_NUMBER_TO_BIGINT, 0, JSEXN_RANGEERR, "can't convert non-finite number to BigInt")

View file

@ -2005,6 +2005,12 @@ JS_IsArrayBufferViewObject(JSObject* obj);
extern JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteLength(JSObject* obj);
/**
* More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well
*/
extern JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteOffset(JSObject* obj);
/*
* Return a pointer to the start of the data referenced by a typed array. The
* data is still owned by the typed array, and should not be modified on

View file

@ -1003,6 +1003,13 @@ static const JSFunctionSpec number_methods[] = {
JS_FS_END
};
bool
js::IsInteger(const Value& val)
{
return val.isInt32() ||
(mozilla::IsFinite(val.toDouble()) && JS::ToInteger(val.toDouble()) == val.toDouble());
}
// ES6 draft ES6 15.7.3.12
static bool
Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
@ -1013,9 +1020,7 @@ Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
return true;
}
Value val = args[0];
args.rval().setBoolean(val.isInt32() ||
(mozilla::IsFinite(val.toDouble()) &&
JS::ToInteger(val.toDouble()) == val.toDouble()));
args.rval().setBoolean(js::IsInteger(val));
return true;
}

View file

@ -72,6 +72,10 @@ Int32ToString(ExclusiveContext* cx, int32_t i);
extern JSAtom*
Int32ToAtom(ExclusiveContext* cx, int32_t si);
// ES6 15.7.3.12
extern bool
IsInteger(const Value& val);
/*
* Convert an integer or double (contained in the given value) to a string and
* append to the given buffer.

View file

@ -108,6 +108,17 @@ IF_SAB(real,imaginary)(Atomics, InitAtomicsClass, OCLASP(Atomics)) \
imaginary(WasmMemory, dummy, dummy) \
imaginary(WasmTable, dummy, dummy) \
real(Promise, InitViaClassSpec, OCLASP(Promise)) \
real(ReadableStream, InitViaClassSpec, &js::ReadableStream::class_) \
real(ReadableStreamDefaultReader, InitViaClassSpec, &js::ReadableStreamDefaultReader::class_) \
real(ReadableStreamBYOBReader, InitViaClassSpec, &js::ReadableStreamBYOBReader::class_) \
real(ReadableStreamDefaultController, InitViaClassSpec, &js::ReadableStreamDefaultController::class_) \
real(ReadableByteStreamController, InitViaClassSpec, &js::ReadableByteStreamController::class_) \
real(ReadableStreamBYOBRequest, InitViaClassSpec, &js::ReadableStreamBYOBRequest::class_) \
imaginary(WritableStream, dummy, dummy) \
imaginary(WritableStreamDefaultWriter, dummy, dummy) \
imaginary(WritableStreamDefaultController,dummy, dummy) \
real(ByteLengthQueuingStrategy, InitViaClassSpec, &js::ByteLengthQueuingStrategy::class_) \
real(CountQueuingStrategy, InitViaClassSpec, &js::CountQueuingStrategy::class_) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)

View file

@ -384,6 +384,7 @@ main_deunified_sources = [
SOURCES += [
'builtin/BigInt.cpp',
'builtin/RegExp.cpp',
'builtin/Stream.cpp',
'frontend/Parser.cpp',
'gc/StoreBuffer.cpp',
'jsarray.cpp',

View file

@ -1891,6 +1891,17 @@ JS_GetArrayBufferViewByteLength(JSObject* obj)
: obj->as<TypedArrayObject>().byteLength();
}
JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteOffset(JSObject* obj)
{
obj = CheckedUnwrap(obj);
if (!obj)
return 0;
return obj->is<DataViewObject>()
? obj->as<DataViewObject>().byteOffset()
: obj->as<TypedArrayObject>().byteOffset();
}
JS_FRIEND_API(JSObject*)
JS_GetObjectAsArrayBufferView(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data)
{

View file

@ -35,6 +35,7 @@
macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \
macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
macro(async, async, "async") \
macro(autoAllocateChunkSize, autoAllocateChunkSize, "autoAllocateChunkSize") \
macro(await, await, "await") \
macro(bigint64, bigint64, "bigint64") \
macro(biguint64, biguint64, "biguint64") \
@ -48,6 +49,7 @@
macro(buffer, buffer, "buffer") \
macro(builder, builder, "builder") \
macro(by, by, "by") \
macro(byob, byob, "byob") \
macro(byteAlignment, byteAlignment, "byteAlignment") \
macro(byteLength, byteLength, "byteLength") \
macro(byteOffset, byteOffset, "byteOffset") \
@ -59,6 +61,7 @@
macro(callee, callee, "callee") \
macro(caller, caller, "caller") \
macro(callFunction, callFunction, "callFunction") \
macro(cancel, cancel, "cancel") \
macro(case, case_, "case") \
macro(caseFirst, caseFirst, "caseFirst") \
macro(catch, catch_, "catch") \
@ -180,6 +183,7 @@
macro(has, has, "has") \
macro(hasOwn, hasOwn, "hasOwn") \
macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
macro(highWaterMark, highWaterMark, "highWaterMark") \
macro(hour, hour, "hour") \
macro(hourCycle, hourCycle, "hourCycle") \
macro(if, if_, "if") \
@ -250,6 +254,7 @@
macro(minusSign, minusSign, "minusSign") \
macro(minute, minute, "minute") \
macro(missingArguments, missingArguments, "missingArguments") \
macro(mode, mode, "mode") \
macro(module, module, "module") \
macro(Module, Module, "Module") \
macro(ModuleInstantiate, ModuleInstantiate, "ModuleInstantiate") \
@ -312,6 +317,7 @@
macro(prototype, prototype, "prototype") \
macro(proxy, proxy, "proxy") \
macro(public, public_, "public") \
macro(pull, pull, "pull") \
macro(raw, raw, "raw") \
macro(reason, reason, "reason") \
macro(RegExpFlagsGetter, RegExpFlagsGetter, "RegExpFlagsGetter") \
@ -347,6 +353,7 @@
macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \
macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \
macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \
macro(start, start, "start") \
macro(startTimestamp, startTimestamp, "startTimestamp") \
macro(state, state, "state") \
macro(static, static_, "static") \

View file

@ -23,6 +23,7 @@
#include "builtin/Promise.h"
#include "builtin/RegExp.h"
#include "builtin/SelfHostingDefines.h"
#include "builtin/Stream.h"
#include "builtin/SymbolObject.h"
#include "builtin/TypedObject.h"
#include "builtin/WeakMapObject.h"
@ -98,6 +99,16 @@ GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key)
case JSProto_WebAssembly:
return !wasm::HasSupport(cx);
case JSProto_ReadableStream:
case JSProto_ReadableStreamDefaultReader:
case JSProto_ReadableStreamBYOBReader:
case JSProto_ReadableStreamDefaultController:
case JSProto_ReadableByteStreamController:
case JSProto_ReadableStreamBYOBRequest:
case JSProto_ByteLengthQueuingStrategy:
case JSProto_CountQueuingStrategy:
return !cx->options().streams();
#ifdef ENABLE_SHARED_ARRAY_BUFFER
case JSProto_Atomics:
case JSProto_SharedArrayBuffer:

View file

@ -36,6 +36,7 @@
#include "builtin/Promise.h"
#include "builtin/Reflect.h"
#include "builtin/SelfHostingDefines.h"
#include "builtin/Stream.h"
#include "builtin/TypedObject.h"
#include "builtin/WeakSetObject.h"
#include "gc/Marking.h"
@ -71,7 +72,6 @@ using JS::AutoCheckCannotGC;
using mozilla::IsInRange;
using mozilla::Maybe;
using mozilla::PodMove;
using mozilla::Maybe;
static void
selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report)
@ -2490,6 +2490,9 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("CallWeakSetMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
JS_FN("IsReadableStreamBYOBRequest",
intrinsic_IsInstanceOfBuiltin<ReadableStreamBYOBRequest>, 1, 0),
// See builtin/TypedObject.h for descriptors of the typedobj functions.
JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0),
JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0),