Bug 1508346 - Add class ListObject. r=jandem

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jason Orendorff 2018-11-23 03:00:37 +00:00
parent f5eda35a8d
commit de94cffe43
8 changed files with 156 additions and 88 deletions

View File

@ -173,7 +173,7 @@ inline static MOZ_MUST_USE bool
SetNewList(JSContext* cx, HandleNativeObject unwrappedContainer, uint32_t slot)
{
AutoRealm ar(cx, unwrappedContainer);
NativeObject* list = NewList(cx);
ListObject* list = ListObject::create(cx);
if (!list) {
return false;
}
@ -1488,8 +1488,8 @@ ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
// Step a: Repeat for each readRequest that is an element of
// reader.[[readRequests]],
RootedNativeObject unwrappedReadRequests(cx, unwrappedReader->requests());
uint32_t len = unwrappedReadRequests->getDenseInitializedLength();
Rooted<ListObject*> unwrappedReadRequests(cx, unwrappedReader->requests());
uint32_t len = unwrappedReadRequests->length();
RootedObject readRequest(cx);
RootedObject resultObj(cx);
RootedValue resultVal(cx);
@ -1497,7 +1497,7 @@ ReadableStreamCloseInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
// Step i: Resolve readRequest.[[promise]] with
// ! ReadableStreamCreateReadResult(undefined, true,
// readRequest.[[forAuthorCode]]).
readRequest = &unwrappedReadRequests->getDenseElement(i).toObject();
readRequest = &unwrappedReadRequests->getAs<JSObject>(i);
if (!cx->compartment()->wrap(cx, &readRequest)) {
return false;
}
@ -1615,13 +1615,13 @@ ReadableStreamErrorInternal(JSContext* cx, Handle<ReadableStream*> unwrappedStre
// Steps 7,8: (Identical in our implementation.)
// Step a: Repeat for each readRequest that is an element of
// reader.[[readRequests]],
RootedNativeObject unwrappedReadRequests(cx, unwrappedReader->requests());
Rooted<ListObject*> unwrappedReadRequests(cx, unwrappedReader->requests());
RootedObject readRequest(cx);
RootedValue val(cx);
uint32_t len = unwrappedReadRequests->getDenseInitializedLength();
uint32_t len = unwrappedReadRequests->length();
for (uint32_t i = 0; i < len; i++) {
// Step i: Reject readRequest.[[promise]] with e.
val = unwrappedReadRequests->getDenseElement(i);
val = unwrappedReadRequests->get(i);
readRequest = &val.toObject();
// Responses have to be created in the compartment from which the
@ -1706,8 +1706,8 @@ ReadableStreamFulfillReadOrReadIntoRequest(JSContext* cx,
// Step 3: Remove readIntoRequest from reader.[[readIntoRequests]], shifting
// all other elements downward (so that the second becomes the first,
// and so on).
RootedNativeObject unwrappedReadIntoRequests(cx, unwrappedReader->requests());
RootedObject readIntoRequest(cx, ShiftFromList<JSObject>(cx, unwrappedReadIntoRequests));
Rooted<ListObject*> unwrappedReadIntoRequests(cx, unwrappedReader->requests());
RootedObject readIntoRequest(cx, &unwrappedReadIntoRequests->popFirstAs<JSObject>(cx));
MOZ_ASSERT(readIntoRequest);
if (!cx->compartment()->wrap(cx, &readIntoRequest)) {
return false;
@ -1746,7 +1746,7 @@ ReadableStreamGetNumReadRequests(ReadableStream* stream)
return 0;
}
return reader->requests()->getDenseInitializedLength();
return reader->requests()->length();
}
/**
@ -1983,9 +1983,8 @@ ReadableStreamDefaultReader_releaseLock(JSContext* cx, unsigned argc, Value* vp)
// Step 3: If this.[[readRequests]] is not empty, throw a TypeError exception.
Value val = reader->getFixedSlot(ReadableStreamReader::Slot_Requests);
if (!val.isUndefined()) {
NativeObject* readRequests = &val.toObject().as<NativeObject>();
uint32_t len = readRequests->getDenseInitializedLength();
if (len != 0) {
ListObject* readRequests = &val.toObject().as<ListObject>();
if (readRequests->length() != 0) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLESTREAMREADER_NOT_EMPTY,
"releaseLock");
@ -2539,15 +2538,15 @@ ReadableStreamControllerCancelSteps(JSContext* cx,
// Step 1 of 3.10.5.1: If this.[[pendingPullIntos]] is not empty,
if (!unwrappedController->is<ReadableStreamDefaultController>()) {
RootedNativeObject unwrappedPendingPullIntos(cx,
Rooted<ListObject*> unwrappedPendingPullIntos(cx,
unwrappedController->as<ReadableByteStreamController>().pendingPullIntos());
if (unwrappedPendingPullIntos->getDenseInitializedLength() != 0) {
if (unwrappedPendingPullIntos->length() != 0) {
// Step a: Let firstDescriptor be the first element of
// this.[[pendingPullIntos]].
PullIntoDescriptor* unwrappedDescriptor =
UnwrapAndDowncastObject<PullIntoDescriptor>(
cx, PeekList<JSObject>(unwrappedPendingPullIntos));
cx, &unwrappedPendingPullIntos->get(0).toObject());
if (!unwrappedDescriptor) {
return nullptr;
}
@ -2631,13 +2630,13 @@ ReadableStreamDefaultControllerPullSteps(JSContext* cx,
Rooted<ReadableStream*> unwrappedStream(cx, unwrappedController->stream());
// Step 2: If this.[[queue]] is not empty,
RootedNativeObject unwrappedQueue(cx);
Rooted<ListObject*> unwrappedQueue(cx);
RootedValue val(cx, unwrappedController->getFixedSlot(StreamController::Slot_Queue));
if (val.isObject()) {
unwrappedQueue = &val.toObject().as<NativeObject>();
unwrappedQueue = &val.toObject().as<ListObject>();
}
if (unwrappedQueue && unwrappedQueue->getDenseInitializedLength() != 0) {
if (unwrappedQueue && unwrappedQueue->length() != 0) {
// Step a: Let chunk be ! DequeueValue(this.[[queue]]).
RootedValue chunk(cx);
if (!DequeueValue(cx, unwrappedController, &chunk)) {
@ -2646,9 +2645,7 @@ ReadableStreamDefaultControllerPullSteps(JSContext* cx,
// Step b: If this.[[closeRequested]] is true and this.[[queue]] is empty,
// perform ! ReadableStreamClose(stream).
if (unwrappedController->closeRequested() &&
unwrappedQueue->getDenseInitializedLength() == 0)
{
if (unwrappedController->closeRequested() && unwrappedQueue->length() == 0) {
if (!ReadableStreamCloseInternal(cx, unwrappedStream)) {
return nullptr;
}
@ -2926,8 +2923,8 @@ ReadableStreamDefaultControllerClose(JSContext* cx,
// Step 5: If controller.[[queue]] is empty, perform
// ! ReadableStreamClose(stream).
RootedNativeObject unwrappedQueue(cx, unwrappedController->queue());
if (unwrappedQueue->getDenseInitializedLength() == 0) {
Rooted<ListObject*> unwrappedQueue(cx, unwrappedController->queue());
if (unwrappedQueue->length() == 0) {
return ReadableStreamCloseInternal(cx, unwrappedStream);
}
@ -3520,10 +3517,10 @@ ReadableByteStreamControllerPullSteps(JSContext* cx,
// Step 3.c: Remove entry from this.[[queue]], shifting all other
// elements downward (so that the second becomes the
// first, and so on).
RootedNativeObject unwrappedQueue(cx, unwrappedController->queue());
Rooted<ListObject*> unwrappedQueue(cx, unwrappedController->queue());
Rooted<ByteStreamChunk*> unwrappedEntry(cx,
UnwrapAndDowncastObject<ByteStreamChunk>(
cx, ShiftFromList<JSObject>(cx, unwrappedQueue)));
cx, &unwrappedQueue->popFirstAs<JSObject>(cx)));
if (!unwrappedEntry) {
return nullptr;
}
@ -3718,13 +3715,13 @@ ReadableByteStreamControllerClose(JSContext* cx,
}
// Step 5: If controller.[[pendingPullIntos]] is not empty,
RootedNativeObject unwrappedPendingPullIntos(cx, unwrappedController->pendingPullIntos());
if (unwrappedPendingPullIntos->getDenseInitializedLength() != 0) {
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, PeekList<JSObject>(unwrappedPendingPullIntos)));
cx, &unwrappedPendingPullIntos->get(0).toObject()));
if (!unwrappedFirstPendingPullInto) {
return false;
}
@ -3990,13 +3987,13 @@ DequeueValue(JSContext* cx, Handle<ReadableStreamController*> unwrappedContainer
// Step 1: Assert: container has [[queue]] and [[queueTotalSize]] internal
// slots (implicit).
// Step 2: Assert: queue is not empty.
RootedNativeObject unwrappedQueue(cx, unwrappedContainer->queue());
MOZ_ASSERT(unwrappedQueue->getDenseInitializedLength() > 0);
Rooted<ListObject*> unwrappedQueue(cx, unwrappedContainer->queue());
MOZ_ASSERT(unwrappedQueue->length() > 0);
// Step 3. Let pair be the first element of queue.
// Step 4. Remove pair from queue, shifting all other elements downward
// (so that the second becomes the first, and so on).
Rooted<QueueEntry*> unwrappedPair(cx, ShiftFromList<QueueEntry>(cx, unwrappedQueue));
Rooted<QueueEntry*> unwrappedPair(cx, &unwrappedQueue->popFirstAs<QueueEntry>(cx));
MOZ_ASSERT(unwrappedPair);
// Step 5: Set container.[[queueTotalSize]] to
@ -4053,7 +4050,7 @@ EnqueueValueWithSize(JSContext* cx,
// element of container.[[queue]].
{
AutoRealm ar(cx, unwrappedContainer);
RootedNativeObject queue(cx, unwrappedContainer->queue());
Rooted<ListObject*> queue(cx, unwrappedContainer->queue());
RootedValue wrappedVal(cx, value);
if (!cx->compartment()->wrap(cx, &wrappedVal)) {
return false;
@ -4064,7 +4061,7 @@ EnqueueValueWithSize(JSContext* cx,
return false;
}
RootedValue val(cx, ObjectValue(*entry));
if (!AppendToList(cx, queue, val)) {
if (!queue->append(cx, val)) {
return false;
}
}
@ -4110,15 +4107,15 @@ AppendToListAtSlot(JSContext* cx,
uint32_t slot,
HandleObject obj)
{
RootedNativeObject list(cx,
&unwrappedContainer->getFixedSlot(slot).toObject().as<NativeObject>());
Rooted<ListObject*> list(cx,
&unwrappedContainer->getFixedSlot(slot).toObject().as<ListObject>());
AutoRealm ar(cx, list);
RootedValue val(cx, ObjectValue(*obj));
if (!cx->compartment()->wrap(cx, &val)) {
return false;
}
return AppendToList(cx, list, val);
return list->append(cx, val);
}

View File

@ -9,9 +9,9 @@
#include "js/Stream.h"
#include "builtin/Promise.h"
#include "vm/List.h"
#include "vm/NativeObject.h"
namespace js {
class ReadableStreamReader;
@ -175,8 +175,8 @@ class ReadableStreamReader : public NativeObject
setFixedSlot(Slot_ForAuthorCode, BooleanValue(value == ForAuthorCodeBool::Yes));
}
NativeObject* requests() const {
return &getFixedSlot(Slot_Requests).toObject().as<NativeObject>();
ListObject* requests() const {
return &getFixedSlot(Slot_Requests).toObject().as<ListObject>();
}
void clearRequests() { setFixedSlot(Slot_Requests, UndefinedValue()); }
@ -219,7 +219,7 @@ class StreamController : public NativeObject
SlotCount
};
NativeObject* queue() const { return &getFixedSlot(Slot_Queue).toObject().as<NativeObject>(); }
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, NumberValue(size)); }
};
@ -349,8 +349,8 @@ class ReadableByteStreamController : public ReadableStreamController
Value byobRequest() const { return getFixedSlot(Slot_BYOBRequest); }
void clearBYOBRequest() { setFixedSlot(Slot_BYOBRequest, JS::UndefinedValue()); }
NativeObject* pendingPullIntos() const {
return &getFixedSlot(Slot_PendingPullIntos).toObject().as<NativeObject>();
ListObject* pendingPullIntos() const {
return &getFixedSlot(Slot_PendingPullIntos).toObject().as<ListObject>();
}
Value autoAllocateChunkSize() const { return getFixedSlot(Slot_AutoAllocateSize); }
void setAutoAllocateChunkSize(const Value & size) {

View File

@ -275,6 +275,7 @@ UNIFIED_SOURCES += [
'vm/JSONParser.cpp',
'vm/JSONPrinter.cpp',
'vm/JSScript.cpp',
'vm/List.cpp',
'vm/MemoryMetrics.cpp',
'vm/NativeObject.cpp',
'vm/ObjectGroup.cpp',

View File

@ -18,7 +18,6 @@
#include "vm/JSContext-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/List-inl.h"
#include "vm/NativeObject-inl.h"
using namespace js;
@ -339,17 +338,17 @@ AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*
return true;
}
RootedNativeObject queue(cx, NewList(cx));
Rooted<ListObject*> queue(cx, ListObject::create(cx));
if (!queue) {
return false;
}
RootedValue requestVal(cx, ObjectValue(*asyncGenObj->singleQueueRequest()));
if (!AppendToList(cx, queue, requestVal)) {
if (!queue->append(cx, requestVal)) {
return false;
}
requestVal = ObjectValue(*request);
if (!AppendToList(cx, queue, requestVal)) {
if (!queue->append(cx, requestVal)) {
return false;
}
@ -357,9 +356,9 @@ AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*
return true;
}
RootedNativeObject queue(cx, asyncGenObj->queue());
Rooted<ListObject*> queue(cx, asyncGenObj->queue());
RootedValue requestVal(cx, ObjectValue(*request));
return AppendToList(cx, queue, requestVal);
return queue->append(cx, requestVal);
}
/* static */ AsyncGeneratorRequest*
@ -371,8 +370,8 @@ AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*
return request;
}
RootedNativeObject queue(cx, asyncGenObj->queue());
return ShiftFromList<AsyncGeneratorRequest>(cx, queue);
Rooted<ListObject*> queue(cx, asyncGenObj->queue());
return &queue->popFirstAs<AsyncGeneratorRequest>(cx);
}
/* static */ AsyncGeneratorRequest*
@ -382,7 +381,7 @@ AsyncGeneratorObject::peekRequest(Handle<AsyncGeneratorObject*> asyncGenObj)
return asyncGenObj->singleQueueRequest();
}
return PeekList<AsyncGeneratorRequest>(asyncGenObj->queue());
return &asyncGenObj->queue()->getAs<AsyncGeneratorRequest>(0);
}
const Class AsyncGeneratorRequest::class_ = {

View File

@ -11,6 +11,7 @@
#include "vm/GeneratorObject.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/List.h"
namespace js {
@ -214,10 +215,10 @@ class AsyncGeneratorObject : public NativeObject
return &getFixedSlot(Slot_QueueOrRequest).toObject().as<AsyncGeneratorRequest>();
}
NativeObject* queue() const {
return &getFixedSlot(Slot_QueueOrRequest).toObject().as<NativeObject>();
ListObject* queue() const {
return &getFixedSlot(Slot_QueueOrRequest).toObject().as<ListObject>();
}
void setQueue(JSObject* queue_) {
void setQueue(ListObject* queue_) {
setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_));
}

View File

@ -7,6 +7,8 @@
#ifndef vm_List_inl_h
#define vm_List_inl_h
#include "vm/List.h"
#include "gc/Rooting.h"
#include "vm/JSContext.h"
#include "vm/NativeObject.h"
@ -14,55 +16,47 @@
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
namespace js {
inline MOZ_MUST_USE NativeObject*
NewList(JSContext* cx)
inline /* static */ js::ListObject*
js::ListObject::create(JSContext* cx)
{
return NewObjectWithNullTaggedProto<PlainObject>(cx);
return NewObjectWithNullTaggedProto<ListObject>(cx);
}
inline MOZ_MUST_USE bool
AppendToList(JSContext* cx, HandleNativeObject list, HandleValue value)
inline bool
js::ListObject::append(JSContext* cx, HandleValue value)
{
uint32_t length = list->getDenseInitializedLength();
uint32_t len = length();
if (!list->ensureElements(cx, length + 1)) {
if (!ensureElements(cx, len + 1)) {
return false;
}
list->ensureDenseInitializedLength(cx, length, 1);
list->setDenseElementWithType(cx, length, value);
ensureDenseInitializedLength(cx, len, 1);
setDenseElementWithType(cx, len, value);
return true;
}
template<class T>
inline MOZ_MUST_USE T*
PeekList(NativeObject* list)
inline JS::Value
js::ListObject::popFirst(JSContext* cx)
{
MOZ_ASSERT(list->getDenseInitializedLength() > 0);
return &list->getDenseElement(0).toObject().as<T>();
}
uint32_t len = length();
MOZ_ASSERT(len > 0);
template<class T>
inline MOZ_MUST_USE T*
ShiftFromList(JSContext* cx, HandleNativeObject list)
{
uint32_t length = list->getDenseInitializedLength();
MOZ_ASSERT(length > 0);
Rooted<T*> entry(cx, &list->getDenseElement(0).toObject().as<T>());
if (!list->tryShiftDenseElements(1)) {
list->moveDenseElements(0, 1, length - 1);
list->setDenseInitializedLength(length - 1);
list->shrinkElements(cx, length - 1);
Value entry = get(0);
if (!tryShiftDenseElements(1)) {
moveDenseElements(0, 1, len - 1);
setDenseInitializedLength(len - 1);
shrinkElements(cx, len - 1);
}
MOZ_ASSERT(list->getDenseInitializedLength() == length - 1);
MOZ_ASSERT(length() == len - 1);
return entry;
}
} /* namespace js */
template <class T>
inline T&
js::ListObject::popFirstAs(JSContext* cx) {
return popFirst(cx).toObject().as<T>();
}
#endif /* vm_List_inl_h */
#endif // vm_List_inl_h

7
js/src/vm/List.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "vm/List-inl.h"
using namespace js;
const Class ListObject::class_ = {
"List"
};

69
js/src/vm/List.h Normal file
View File

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 vm_List_h
#define vm_List_h
#include "NamespaceImports.h"
#include "js/Value.h"
#include "vm/NativeObject.h"
namespace js {
/**
* The List specification type, ECMA-262 6.2.1.
* <https://tc39.github.io/ecma262/#sec-list-and-record-specification-type>
*
* Lists are simple mutable sequences of values. Many standards use them.
* Abstractly, they're not objects; they don't have properties or prototypes;
* they're for internal specification use only. ListObject is our most direct
* implementation of a List: store the values in the slots of a JSObject.
*
* We often implement Lists in other ways. For example, builtin/Utilities.js
* contains a completely unrelated List constructor that's used in self-hosted
* code. And AsyncGeneratorObject optimizes away the ListObject in the common
* case where its internal queue never holds more than one element.
*
* ListObjects must not be exposed to content scripts.
*/
class ListObject : public NativeObject {
public:
static const Class class_;
inline static MOZ_MUST_USE ListObject* create(JSContext* cx);
uint32_t length() const { return getDenseInitializedLength(); }
const Value& get(uint32_t index) const { return getDenseElement(index); }
template <class T>
T& getAs(uint32_t index) const { return get(index).toObject().as<T>(); }
/**
* Add an element to the end of the list. Returns false on OOM.
*/
inline MOZ_MUST_USE bool append(JSContext* cx, HandleValue value);
/**
* Remove and return the first element of the list.
*
* Precondition: This list is not empty.
*/
inline JS::Value popFirst(JSContext* cx);
/**
* Remove and return the first element of the list.
*
* Precondition: This list is not empty, and the first element
* is an object of class T.
*/
template <class T>
inline T& popFirstAs(JSContext* cx);
};
} // namespace js
#endif // vm_List_h