/* 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 "gc/FinalizationObservers.h" #include "jsapi-tests/tests.h" #include "vm/JSContext.h" #include "vm/NativeObject.h" #include "vm/JSObject-inl.h" #include "vm/NativeObject-inl.h" using namespace js; using namespace js::gc; namespace { class TestObserverListElement : public ObserverListObject { public: static const JSClass class_; static TestObserverListElement* New(JSContext* cx) { return NewObjectWithGivenProto(cx, nullptr); } private: static const JSClassOps classOps_; static void Finalize(JS::GCContext* gcx, JSObject* obj) { obj->as().unlink(); } }; const JSClassOps TestObserverListElement::classOps_ = { .finalize = Finalize, }; const JSClass TestObserverListElement::class_ = { "TestObserverListElement", JSCLASS_HAS_RESERVED_SLOTS(ObserverListObject::SlotCount) | JSCLASS_FOREGROUND_FINALIZE, &classOps_, JS_NULL_CLASS_SPEC, &classExtension_}; } // namespace BEGIN_TEST(testObserverList_insertAndUnlink) { gc::AutoSuppressGC suppress(cx); ObserverList list; CHECK(list.isEmpty()); Rooted a(cx, TestObserverListElement::New(cx)); CHECK(a); CHECK(!a->isInList()); list.insertFront(a); CHECK(!list.isEmpty()); CHECK(a->isInList()); CHECK(list.getFirst() == a); Rooted b(cx, TestObserverListElement::New(cx)); CHECK(b); CHECK(!b->isInList()); list.insertFront(b); CHECK(!list.isEmpty()); CHECK(b->isInList()); CHECK(list.getFirst() == b); Rooted c(cx, TestObserverListElement::New(cx)); CHECK(c); list.insertFront(c); CHECK(list.getFirst() == c); // Unlink an element from the middle. b->unlink(); CHECK(!b->isInList()); CHECK(!list.isEmpty()); CHECK(list.getFirst() == c); // Unlink the front element. c->unlink(); CHECK(!c->isInList()); CHECK(!list.isEmpty()); CHECK(list.getFirst() == a); // Unlink the last remaining element. a->unlink(); CHECK(!a->isInList()); CHECK(list.isEmpty()); return true; } END_TEST(testObserverList_insertAndUnlink) BEGIN_TEST(testObserverList_move) { gc::AutoSuppressGC suppress(cx); Rooted a(cx, TestObserverListElement::New(cx)); Rooted b(cx, TestObserverListElement::New(cx)); CHECK(a && b); // Move construction from an empty list. { ObserverList src; ObserverList dst(std::move(src)); CHECK(src.isEmpty()); CHECK(dst.isEmpty()); } // Move construction from a non-empty list. { ObserverList src; src.insertFront(a); src.insertFront(b); CHECK(src.getFirst() == b); ObserverList dst(std::move(src)); CHECK(src.isEmpty()); CHECK(!dst.isEmpty()); CHECK(dst.getFirst() == b); b->unlink(); a->unlink(); CHECK(dst.isEmpty()); } // Move assignment from a non-empty list. { ObserverList src; src.insertFront(a); src.insertFront(b); ObserverList dst; dst = std::move(src); CHECK(src.isEmpty()); CHECK(dst.getFirst() == b); b->unlink(); a->unlink(); CHECK(dst.isEmpty()); } return true; } END_TEST(testObserverList_move) BEGIN_TEST(testObserverList_append) { gc::AutoSuppressGC suppress(cx); ObserverList list1; ObserverList list2; CHECK(list1.isEmpty()); CHECK(list2.isEmpty()); // Append empty to empty. list1.append(std::move(list2)); CHECK(list1.isEmpty()); CHECK(list2.isEmpty()); Rooted a(cx, TestObserverListElement::New(cx)); Rooted b(cx, TestObserverListElement::New(cx)); Rooted c(cx, TestObserverListElement::New(cx)); CHECK(a); CHECK(b); CHECK(c); // Append empty to non-empty. list1.insertFront(a); list1.insertFront(b); list1.append(std::move(list2)); CHECK(!list1.isEmpty()); CHECK(list2.isEmpty()); CHECK(list1.getFirst() == b); // Append non-empty to empty. list2.append(std::move(list1)); CHECK(list1.isEmpty()); CHECK(!list2.isEmpty()); CHECK(list2.getFirst() == b); // Build list1 = [c, b], list2 = [a]. a->unlink(); b->unlink(); CHECK(list2.isEmpty()); list1.insertFront(b); list1.insertFront(c); list2.insertFront(a); // Append non-empty to non-empty: list1 becomes [c, b, a]. list1.append(std::move(list2)); CHECK(list2.isEmpty()); CHECK(!list1.isEmpty()); CHECK(list1.getFirst() == c); c->unlink(); CHECK(list1.getFirst() == b); b->unlink(); CHECK(list1.getFirst() == a); a->unlink(); CHECK(list1.isEmpty()); return true; } END_TEST(testObserverList_append)