Bug 1503324 - Implement ReadableStreamCreateReadResult. r=arai

This prevents author code from using Object.prototype.then to observe or tamper
with a stream that is locked by another consumer.

Differential Revision: https://phabricator.services.mozilla.com/D12081

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jason Orendorff 2018-11-20 00:18:07 +00:00
Родитель ec618b5047
Коммит daac4d31de
7 изменённых файлов: 223 добавлений и 92 удалений

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

@ -451,37 +451,38 @@ extern JS_PUBLIC_API bool
ReadableStreamError(JSContext* cx, HandleObject stream, HandleValue error);
/**
* Cancels the given ReadableStream reader's associated stream.
* C++ equivalent of `reader.cancel(reason)`
* (both <https://streams.spec.whatwg.org/#default-reader-cancel> and
* <https://streams.spec.whatwg.org/#byob-reader-cancel>).
*
* Throws a TypeError and returns false if the given reader isn't active.
*
* Asserts that |reader| is a ReadableStreamDefaultReader or
* ReadableStreamBYOBReader object or an unwrappable wrapper for one.
* `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);
/**
* Cancels the given ReadableStream reader's associated stream.
* 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>).
*
* Throws a TypeError and returns false if the given reader has pending
* read or readInto (for default or byob readers, respectively) requests.
*
* Asserts that |reader| is a ReadableStreamDefaultReader or
* ReadableStreamBYOBReader object or an unwrappable wrapper for one.
* `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);
/**
* Requests a read from the reader's associated ReadableStream and returns the
* resulting PromiseObject.
* C++ equivalent of the `reader.read()` method on default readers
* (<https://streams.spec.whatwg.org/#default-reader-read>).
*
* Returns a Promise that's resolved with the read result once available or
* rejected immediately if the stream is errored or the operation failed.
* The result is a new Promise object, or null on OOM.
*
* Asserts that |reader| is a ReadableStreamDefaultReader object or an
* unwrappable wrapper for one.
* `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);

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

@ -742,7 +742,9 @@ ReadableStream_cancel(JSContext* cx, unsigned argc, Value* vp)
}
static MOZ_MUST_USE ReadableStreamDefaultReader*
CreateReadableStreamDefaultReader(JSContext* cx, Handle<ReadableStream*> unwrappedStream);
CreateReadableStreamDefaultReader(JSContext* cx,
Handle<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode = ForAuthorCodeBool::No);
/**
* Streams spec, 3.2.5.3. getReader()
@ -772,7 +774,7 @@ ReadableStream_getReader(JSContext* cx, unsigned argc, Value* vp)
}
if (modeVal.isUndefined()) {
reader = CreateReadableStreamDefaultReader(cx, unwrappedStream);
reader = CreateReadableStreamDefaultReader(cx, unwrappedStream, ForAuthorCodeBool::Yes);
} else {
// Step 3: Set mode to ? ToString(mode) (implicit).
RootedString mode(cx, ToString<CanGC>(cx, modeVal));
@ -1292,8 +1294,12 @@ AppendToListAtSlot(JSContext* cx,
HandleObject obj);
/**
* Streams spec, 3.4.1. ReadableStreamAddReadIntoRequest ( stream )
* Streams spec, 3.4.2. ReadableStreamAddReadRequest ( stream )
* Streams spec, 3.4.1. ReadableStreamAddReadIntoRequest ( stream, forAuthorCode )
* Streams spec, 3.4.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()`.
*/
static MOZ_MUST_USE JSObject*
ReadableStreamAddReadOrReadIntoRequest(JSContext* cx, Handle<ReadableStream*> unwrappedStream)
@ -1314,10 +1320,13 @@ ReadableStreamAddReadOrReadIntoRequest(JSContext* cx, Handle<ReadableStream*> un
return nullptr;
}
// Step 4: Let read{Into}Request be Record {[[promise]]: promise}.
// 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 [[promise]] is the Record's only field, we store it directly.
// Since we don't need the [[forAuthorCode]] field (see the comment on
// `ReadableStreamReader::forAuthorCode()`), we elide the Record and store
// only the promise.
if (!AppendToListAtSlot(cx, unwrappedReader, ReadableStreamReader::Slot_Requests, promise)) {
return nullptr;
}
@ -1396,6 +1405,12 @@ ReadableStreamCancel(JSContext* cx, Handle<ReadableStream*> unwrappedStream, Han
return JS::CallOriginalPromiseThen(cx, sourceCancelPromise, returnUndefined, nullptr);
}
static MOZ_MUST_USE JSObject*
ReadableStreamCreateReadResult(JSContext* cx,
HandleValue value,
bool done,
ForAuthorCodeBool forAuthorCode);
/**
* Streams spec, 3.4.4. ReadableStreamClose ( stream )
*/
@ -1421,6 +1436,8 @@ ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
// 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]],
RootedNativeObject unwrappedReadRequests(cx, unwrappedReader->requests());
@ -1430,13 +1447,15 @@ ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
RootedValue resultVal(cx);
for (uint32_t i = 0; i < len; i++) {
// Step i: Resolve readRequest.[[promise]] with
// ! CreateIterResultObject(undefined, true).
// ! ReadableStreamCreateReadResult(undefined, true,
// readRequest.[[forAuthorCode]]).
readRequest = &unwrappedReadRequests->getDenseElement(i).toObject();
if (!cx->compartment()->wrap(cx, &readRequest)) {
return false;
}
resultObj = CreateIterResultObject(cx, UndefinedHandleValue, true);
resultObj = ReadableStreamCreateReadResult(cx, UndefinedHandleValue, true,
forAuthorCode);
if (!resultObj) {
return false;
}
@ -1476,6 +1495,40 @@ ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
return true;
}
/**
* Streams spec, 3.4.5. ReadableStreamCreateReadResult ( value, done, forAuthorCode )
*/
static MOZ_MUST_USE JSObject*
ReadableStreamCreateReadResult(JSContext* cx,
HandleValue value,
bool done,
ForAuthorCodeBool forAuthorCode)
{
// Step 1: Let prototype be null.
// Step 2: If forAuthorCode is true, set prototype to %ObjectPrototype%.
RootedObject templateObject(cx,
forAuthorCode == ForAuthorCodeBool::Yes
? cx->realm()->getOrCreateIterResultTemplateObject(cx)
: cx->realm()->getOrCreateIterResultWithoutPrototypeTemplateObject(cx));
// Step 3: Assert: Type(done) is Boolean (implicit).
// Step 4: Let obj be ObjectCreate(prototype).
NativeObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::createWithTemplate(cx, gc::DefaultHeap,
templateObject));
// Step 5: Perform CreateDataProperty(obj, "value", value).
obj->setSlot(Realm::IterResultObjectValueSlot, value);
// Step 6: Perform CreateDataProperty(obj, "done", done).
obj->setSlot(Realm::IterResultObjectDoneSlot,
done ? TrueHandleValue : FalseHandleValue);
// Step 7: Return obj.
return obj;
}
/**
* Streams spec, 3.4.6. ReadableStreamError ( stream, e )
*/
@ -1610,9 +1663,10 @@ ReadableStreamFulfillReadOrReadIntoRequest(JSContext* cx,
return false;
}
// Step 4: Resolve readIntoRequest.[[promise]] with
// ! CreateIterResultObject(chunk, done).
RootedObject iterResult(cx, CreateIterResultObject(cx, chunk, done));
// Step 4: Resolve read{Into}Request.[[promise]] with
// ! ReadableStreamCreateReadResult(chunk, done, readIntoRequest.[[forAuthorCode]]).
RootedObject iterResult(cx,
ReadableStreamCreateReadResult(cx, chunk, done, unwrappedReader->forAuthorCode()));
if (!iterResult) {
return false;
}
@ -1677,14 +1731,17 @@ ReadableStreamHasDefaultReader(JSContext* cx,
static MOZ_MUST_USE bool
ReadableStreamReaderGenericInitialize(JSContext* cx,
Handle<ReadableStreamReader*> reader,
Handle<ReadableStream*> unwrappedStream);
Handle<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode);
/**
* Stream spec, 3.5.3. new ReadableStreamDefaultReader ( stream )
* Steps 2-4.
*/
static MOZ_MUST_USE ReadableStreamDefaultReader*
CreateReadableStreamDefaultReader(JSContext* cx, Handle<ReadableStream*> unwrappedStream)
CreateReadableStreamDefaultReader(JSContext* cx,
Handle<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode)
{
Rooted<ReadableStreamDefaultReader*> reader(cx,
NewBuiltinClassInstance<ReadableStreamDefaultReader>(cx));
@ -1700,7 +1757,7 @@ CreateReadableStreamDefaultReader(JSContext* cx, Handle<ReadableStream*> unwrapp
}
// Step 3: Perform ! ReadableStreamReaderGenericInitialize(this, stream).
if (!ReadableStreamReaderGenericInitialize(cx, reader, unwrappedStream)) {
if (!ReadableStreamReaderGenericInitialize(cx, reader, unwrappedStream, forAuthorCode)) {
return nullptr;
}
@ -1831,7 +1888,7 @@ ReadableStreamDefaultReader_read(JSContext* cx, unsigned argc, Value* vp)
return ReturnPromiseRejectedWithPendingError(cx, args);
}
// Step 3: Return ! ReadableStreamDefaultReaderRead(this).
// Step 3: Return ! ReadableStreamDefaultReaderRead(this, true).
JSObject* readPromise = ::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
if (!readPromise) {
return false;
@ -1937,8 +1994,10 @@ ReadableStreamReaderGenericCancel(JSContext* cx,
* Streams spec, 3.7.4. ReadableStreamReaderGenericInitialize ( reader, stream )
*/
static MOZ_MUST_USE bool
ReadableStreamReaderGenericInitialize(JSContext* cx, Handle<ReadableStreamReader*> reader,
Handle<ReadableStream*> unwrappedStream)
ReadableStreamReaderGenericInitialize(JSContext* cx,
Handle<ReadableStreamReader*> reader,
Handle<ReadableStream*> unwrappedStream,
ForAuthorCodeBool forAuthorCode)
{
cx->check(reader);
@ -1991,6 +2050,11 @@ ReadableStreamReaderGenericInitialize(JSContext* cx, Handle<ReadableStreamReader
}
reader->setClosedPromise(promise);
// Extra step not in the standard. See the comment on
// `ReadableStreamReader::forAuthorCode()`.
reader->setForAuthorCode(forAuthorCode);
return true;
}
@ -2066,26 +2130,30 @@ ReadableStreamControllerPullSteps(JSContext* cx,
Handle<ReadableStreamController*> unwrappedController);
/**
* Streams spec, 3.7.7. ReadableStreamDefaultReaderRead ( reader )
* Streams spec, 3.7.7. ReadableStreamDefaultReaderRead ( reader [, forAuthorCode ] )
*/
static MOZ_MUST_USE JSObject*
ReadableStreamDefaultReaderRead(JSContext* cx,
Handle<ReadableStreamDefaultReader*> unwrappedReader)
{
// Step 1: Let stream be reader.[[ownerReadableStream]].
// Step 2: Assert: stream is not undefined.
// 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 3: Set stream.[[disturbed]] to true.
// Step 4: Set stream.[[disturbed]] to true.
unwrappedStream->setDisturbed();
// Step 4: If stream.[[state]] is "closed", return a new promise resolved with
// ! CreateIterResultObject(undefined, true).
// Step 5: If stream.[[state]] is "closed", return a new promise resolved with
// ! ReadableStreamCreateReadResult(undefined, true, forAuthorCode).
if (unwrappedStream->closed()) {
RootedObject iterResult(cx, CreateIterResultObject(cx, UndefinedHandleValue, true));
RootedObject iterResult(cx,
ReadableStreamCreateReadResult(cx, UndefinedHandleValue, true,
unwrappedReader->forAuthorCode()));
if (!iterResult) {
return nullptr;
}
@ -2093,7 +2161,7 @@ ReadableStreamDefaultReaderRead(JSContext* cx,
return PromiseObject::unforgeableResolve(cx, iterResultVal);
}
// Step 5: If stream.[[state]] is "errored", return a new promise rejected with
// Step 6: If stream.[[state]] is "errored", return a new promise rejected with
// stream.[[storedError]].
if (unwrappedStream->errored()) {
RootedValue storedError(cx, unwrappedStream->storedError());
@ -2103,10 +2171,10 @@ ReadableStreamDefaultReaderRead(JSContext* cx,
return PromiseObject::unforgeableReject(cx, storedError);
}
// Step 6: Assert: stream.[[state]] is "readable".
// Step 7: Assert: stream.[[state]] is "readable".
MOZ_ASSERT(unwrappedStream->readable());
// Step 7: Return ! stream.[[readableStreamController]].[[PullSteps]]().
// Step 8: Return ! stream.[[readableStreamController]].[[PullSteps]]().
Rooted<ReadableStreamController*> unwrappedController(cx, unwrappedStream->controller());
return ReadableStreamControllerPullSteps(cx, unwrappedController);
}
@ -2580,7 +2648,7 @@ DequeueValue(JSContext* cx,
MutableHandleValue chunk);
/**
* Streams spec, 3.8.5.2. ReadableStreamDefaultController [[PullSteps]]()
* Streams spec, 3.8.5.2. ReadableStreamDefaultController [[PullSteps]]( forAuthorCode )
*/
static JSObject*
ReadableStreamDefaultControllerPullSteps(JSContext* cx,
@ -2620,17 +2688,24 @@ ReadableStreamDefaultControllerPullSteps(JSContext* cx,
}
}
// Step d: Return a promise resolved with ! CreateIterResultObject(chunk, false).
// Step d: Return a promise resolved with
// ! ReadableStreamCreateReadResult(chunk, false, forAuthorCode).
cx->check(chunk);
RootedObject iterResultObj(cx, CreateIterResultObject(cx, chunk, false));
if (!iterResultObj) {
ReadableStreamReader* unwrappedReader = UnwrapReaderFromStream(cx, unwrappedStream);
if (!unwrappedReader) {
return nullptr;
}
RootedValue iterResult(cx, ObjectValue(*iterResultObj));
return PromiseObject::unforgeableResolve(cx, iterResult);
RootedObject readResultObj(cx,
ReadableStreamCreateReadResult(cx, chunk, false, unwrappedReader->forAuthorCode()));
if (!readResultObj) {
return nullptr;
}
RootedValue readResult(cx, ObjectValue(*readResultObj));
return PromiseObject::unforgeableResolve(cx, readResult);
}
// Step 3: Let pendingPromise be ! ReadableStreamAddReadRequest(stream).
// Step 3: Let pendingPromise be
// ! ReadableStreamAddReadRequest(stream, forAuthorCode).
RootedObject pendingPromise(cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
if (!pendingPromise) {
return nullptr;
@ -3297,7 +3372,7 @@ ReadableByteStreamControllerHandleQueueDrain(JSContext* cx,
Handle<ReadableStreamController*> unwrappedController);
/**
* Streams spec, 3.10.5.2. [[PullSteps]] ()
* Streams spec, 3.10.5.2. [[PullSteps]] ( forAuthorCode )
*/
static MOZ_MUST_USE JSObject*
ReadableByteStreamControllerPullSteps(JSContext* cx,
@ -3386,13 +3461,19 @@ ReadableByteStreamControllerPullSteps(JSContext* cx,
return nullptr;
}
// Step 3.g: Return a promise resolved with ! CreateIterResultObject(view, false).
// Step 3.g: Return a promise resolved with
// ! ReadableStreamCreateReadResult(view, false, forAuthorCode).
val.setObject(*view);
RootedObject iterResult(cx, CreateIterResultObject(cx, val, false));
if (!iterResult) {
ReadableStreamReader* unwrappedReader = UnwrapReaderFromStream(cx, unwrappedStream);
if (!unwrappedReader) {
return nullptr;
}
val.setObject(*iterResult);
RootedObject readResult(cx,
ReadableStreamCreateReadResult(cx, val, false, unwrappedReader->forAuthorCode()));
if (!readResult) {
return nullptr;
}
val.setObject(*readResult);
return PromiseObject::unforgeableResolve(cx, val);
}
@ -3440,7 +3521,7 @@ ReadableByteStreamControllerPullSteps(JSContext* cx,
}
}
// Step 6: Let promise be ! ReadableStreamAddReadRequest(stream).
// Step 6: Let promise be ! ReadableStreamAddReadRequest(stream, forAuthorCode).
RootedObject promise(cx, ReadableStreamAddReadOrReadIntoRequest(cx, unwrappedStream));
if (!promise) {
return nullptr;
@ -3458,9 +3539,9 @@ ReadableByteStreamControllerPullSteps(JSContext* cx,
/**
* Unified implementation of ReadableStream controllers' [[PullSteps]] internal
* methods.
* Streams spec, 3.8.5.2. [[PullSteps]] ()
* Streams spec, 3.8.5.2. [[PullSteps]] ( forAuthorCode )
* and
* Streams spec, 3.10.5.2. [[PullSteps]] ()
* Streams spec, 3.10.5.2. [[PullSteps]] ( forAuthorCode )
*/
static MOZ_MUST_USE JSObject*
ReadableStreamControllerPullSteps(JSContext* cx, Handle<ReadableStreamController*> unwrappedController)
@ -4517,6 +4598,8 @@ JS::ReadableStreamReaderCancel(JSContext* cx, HandleObject readerObj, HandleValu
if (!unwrappedReader) {
return false;
}
MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
"C++ code should not touch readers created by scripts");
return ReadableStreamReaderGenericCancel(cx, unwrappedReader, reason);
}
@ -4527,10 +4610,13 @@ JS::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject readerObj)
AssertHeapIsIdle();
CHECK_THREAD(cx);
Rooted<ReadableStreamReader*> unwrappedReader(cx, APIToUnwrapped<ReadableStreamReader>(cx, readerObj));
Rooted<ReadableStreamReader*> unwrappedReader(cx,
APIToUnwrapped<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));
@ -4554,6 +4640,8 @@ JS::ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject readerObj)
if (!unwrappedReader) {
return nullptr;
}
MOZ_ASSERT(unwrappedReader->forAuthorCode() == ForAuthorCodeBool::No,
"C++ code should not touch readers created by scripts");
return ::ReadableStreamDefaultReaderRead(cx, unwrappedReader);
}

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

@ -115,6 +115,13 @@ class ReadableStream : public NativeObject
static const Class protoClass_;
};
/**
* 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:
@ -143,6 +150,7 @@ class ReadableStreamReader : public NativeObject
Slot_Stream,
Slot_Requests,
Slot_ClosedPromise,
Slot_ForAuthorCode,
SlotCount,
};
@ -151,6 +159,28 @@ class ReadableStreamReader : public NativeObject
void clearStream() { setFixedSlot(Slot_Stream, 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, BooleanValue(value == ForAuthorCodeBool::Yes));
}
NativeObject* requests() const {
return &getFixedSlot(Slot_Requests).toObject().as<NativeObject>();
}

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

@ -1036,10 +1036,35 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
return iterResultTemplate_;
}
NativeObject* templateObj = createIterResultTemplateObject(cx, WithObjectPrototype::Yes);
iterResultTemplate_.set(templateObj);
return iterResultTemplate_;
}
NativeObject*
Realm::getOrCreateIterResultWithoutPrototypeTemplateObject(JSContext* cx)
{
MOZ_ASSERT(cx->realm() == this);
if (iterResultWithoutPrototypeTemplate_) {
return iterResultWithoutPrototypeTemplate_;
}
NativeObject* templateObj = createIterResultTemplateObject(cx, WithObjectPrototype::No);
iterResultWithoutPrototypeTemplate_.set(templateObj);
return iterResultWithoutPrototypeTemplate_;
}
NativeObject*
Realm::createIterResultTemplateObject(JSContext* cx, WithObjectPrototype withProto)
{
// Create template plain object
RootedNativeObject templateObject(cx, NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
RootedNativeObject templateObject(cx,
withProto == WithObjectPrototype::Yes
? NewBuiltinClassInstance<PlainObject>(cx, TenuredObject)
: NewObjectWithNullTaggedProto<PlainObject>(cx));
if (!templateObject) {
return iterResultTemplate_; // = nullptr
return nullptr;
}
// Create a new group for the template.
@ -1048,7 +1073,7 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
templateObject->getClass(),
proto));
if (!group) {
return iterResultTemplate_; // = nullptr
return nullptr;
}
templateObject->setGroup(group);
@ -1056,14 +1081,14 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
if (!NativeDefineDataProperty(cx, templateObject, cx->names().value, UndefinedHandleValue,
JSPROP_ENUMERATE))
{
return iterResultTemplate_; // = nullptr
return nullptr;
}
// Set dummy `done` property
if (!NativeDefineDataProperty(cx, templateObject, cx->names().done, TrueHandleValue,
JSPROP_ENUMERATE))
{
return iterResultTemplate_; // = nullptr
return nullptr;
}
AutoSweepObjectGroup sweep(group);
@ -1084,9 +1109,7 @@ Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
MOZ_ASSERT(shape->slot() == Realm::IterResultObjectDoneSlot &&
shape->propidRef() == NameToId(cx->names().done));
iterResultTemplate_.set(templateObject);
return iterResultTemplate_;
return templateObject;
}
/*** Iterator objects ****************************************************************************/

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

@ -530,6 +530,12 @@ Realm::sweepTemplateObjects()
if (iterResultTemplate_ && IsAboutToBeFinalized(&iterResultTemplate_)) {
iterResultTemplate_.set(nullptr);
}
if (iterResultWithoutPrototypeTemplate_ &&
IsAboutToBeFinalized(&iterResultWithoutPrototypeTemplate_))
{
iterResultWithoutPrototypeTemplate_.set(nullptr);
}
}
void

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

@ -365,6 +365,7 @@ class JS::Realm : public JS::shadow::Realm
js::ReadBarriered<js::ArgumentsObject*> mappedArgumentsTemplate_ { nullptr };
js::ReadBarriered<js::ArgumentsObject*> unmappedArgumentsTemplate_ { nullptr };
js::ReadBarriered<js::NativeObject*> iterResultTemplate_ { nullptr };
js::ReadBarriered<js::NativeObject*> iterResultWithoutPrototypeTemplate_ { nullptr };
// There are two ways to enter a realm:
//
@ -695,7 +696,13 @@ class JS::Realm : public JS::shadow::Realm
static const size_t IterResultObjectValueSlot = 0;
static const size_t IterResultObjectDoneSlot = 1;
js::NativeObject* getOrCreateIterResultTemplateObject(JSContext* cx);
js::NativeObject* getOrCreateIterResultWithoutPrototypeTemplateObject(JSContext* cx);
private:
enum class WithObjectPrototype { No, Yes };
js::NativeObject* createIterResultTemplateObject(JSContext* cx, WithObjectPrototype withProto);
public:
js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
js::ArgumentsObject* maybeArgumentsTemplateObject(bool mapped) const;

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

@ -1,33 +1,9 @@
[response-stream-with-broken-then.any.html]
[Attempt to inject undefined via Object.prototype.then.]
expected: FAIL
[Attempt to inject {done: false, value: bye} via Object.prototype.then.]
expected: FAIL
[intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
expected: FAIL
[Attempt to inject 8.2 via Object.prototype.then.]
expected: FAIL
[Attempt to inject value: undefined via Object.prototype.then.]
expected: FAIL
[response-stream-with-broken-then.any.worker.html]
[Attempt to inject undefined via Object.prototype.then.]
expected: FAIL
[Attempt to inject {done: false, value: bye} via Object.prototype.then.]
expected: FAIL
[intercepting arraybuffer to body readable stream conversion via Object.prototype.then should not be possible]
expected: FAIL
[Attempt to inject 8.2 via Object.prototype.then.]
expected: FAIL
[Attempt to inject value: undefined via Object.prototype.then.]
expected: FAIL