mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-27 02:29:35 +00:00
387 lines
10 KiB
C++
387 lines
10 KiB
C++
/* -*- 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/. */
|
|
|
|
#include "ds/TraceableFifo.h"
|
|
#include "js/GCHashTable.h"
|
|
#include "js/RootingAPI.h"
|
|
#include "js/TraceableVector.h"
|
|
|
|
#include "jsapi-tests/tests.h"
|
|
|
|
using namespace js;
|
|
|
|
BEGIN_TEST(testGCExactRooting)
|
|
{
|
|
JS::RootedObject rootCx(cx, JS_NewPlainObject(cx));
|
|
JS::RootedObject rootRt(cx->runtime(), JS_NewPlainObject(cx));
|
|
|
|
JS_GC(cx->runtime());
|
|
|
|
/* Use the objects we just created to ensure that they are still alive. */
|
|
JS_DefineProperty(cx, rootCx, "foo", JS::UndefinedHandleValue, 0);
|
|
JS_DefineProperty(cx, rootRt, "foo", JS::UndefinedHandleValue, 0);
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testGCExactRooting)
|
|
|
|
BEGIN_TEST(testGCSuppressions)
|
|
{
|
|
JS::AutoAssertOnGC nogc;
|
|
JS::AutoCheckCannotGC checkgc;
|
|
JS::AutoSuppressGCAnalysis noanalysis;
|
|
|
|
JS::AutoAssertOnGC nogcRt(cx->runtime());
|
|
JS::AutoCheckCannotGC checkgcRt(cx->runtime());
|
|
JS::AutoSuppressGCAnalysis noanalysisRt(cx->runtime());
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testGCSuppressions)
|
|
|
|
struct MyContainer : public JS::Traceable
|
|
{
|
|
RelocatablePtrObject obj;
|
|
RelocatablePtrString str;
|
|
|
|
MyContainer() : obj(nullptr), str(nullptr) {}
|
|
static void trace(MyContainer* self, JSTracer* trc) {
|
|
if (self->obj)
|
|
js::TraceEdge(trc, &self->obj, "test container");
|
|
if (self->str)
|
|
js::TraceEdge(trc, &self->str, "test container");
|
|
}
|
|
};
|
|
|
|
namespace js {
|
|
template <>
|
|
struct RootedBase<MyContainer> {
|
|
RelocatablePtrObject& obj() { return static_cast<Rooted<MyContainer>*>(this)->get().obj; }
|
|
RelocatablePtrString& str() { return static_cast<Rooted<MyContainer>*>(this)->get().str; }
|
|
};
|
|
template <>
|
|
struct PersistentRootedBase<MyContainer> {
|
|
RelocatablePtrObject& obj() {
|
|
return static_cast<PersistentRooted<MyContainer>*>(this)->get().obj;
|
|
}
|
|
RelocatablePtrString& str() {
|
|
return static_cast<PersistentRooted<MyContainer>*>(this)->get().str;
|
|
}
|
|
};
|
|
} // namespace js
|
|
|
|
BEGIN_TEST(testGCRootedStaticStructInternalStackStorageAugmented)
|
|
{
|
|
JS::Rooted<MyContainer> container(cx);
|
|
container.obj() = JS_NewObject(cx, nullptr);
|
|
container.str() = JS_NewStringCopyZ(cx, "Hello");
|
|
|
|
JS_GC(cx->runtime());
|
|
JS_GC(cx->runtime());
|
|
|
|
JS::RootedObject obj(cx, container.obj());
|
|
JS::RootedValue val(cx, StringValue(container.str()));
|
|
CHECK(JS_SetProperty(cx, obj, "foo", val));
|
|
obj = nullptr;
|
|
val = UndefinedValue();
|
|
|
|
{
|
|
JS::RootedString actual(cx);
|
|
bool same;
|
|
|
|
// Automatic move from stack to heap.
|
|
JS::PersistentRooted<MyContainer> heap(cx, container);
|
|
|
|
// clear prior rooting.
|
|
container.obj() = nullptr;
|
|
container.str() = nullptr;
|
|
|
|
obj = heap.obj();
|
|
CHECK(JS_GetProperty(cx, obj, "foo", &val));
|
|
actual = val.toString();
|
|
CHECK(JS_StringEqualsAscii(cx, actual, "Hello", &same));
|
|
CHECK(same);
|
|
obj = nullptr;
|
|
actual = nullptr;
|
|
|
|
JS_GC(cx->runtime());
|
|
JS_GC(cx->runtime());
|
|
|
|
obj = heap.obj();
|
|
CHECK(JS_GetProperty(cx, obj, "foo", &val));
|
|
actual = val.toString();
|
|
CHECK(JS_StringEqualsAscii(cx, actual, "Hello", &same));
|
|
CHECK(same);
|
|
obj = nullptr;
|
|
actual = nullptr;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testGCRootedStaticStructInternalStackStorageAugmented)
|
|
|
|
using MyHashMap = js::GCHashMap<js::Shape*, JSObject*>;
|
|
|
|
BEGIN_TEST(testGCRootedHashMap)
|
|
{
|
|
JS::Rooted<MyHashMap> map(cx, MyHashMap(cx));
|
|
CHECK(map.init(15));
|
|
CHECK(map.initialized());
|
|
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
|
RootedValue val(cx, UndefinedValue());
|
|
// Construct a unique property name to ensure that the object creates a
|
|
// new shape.
|
|
char buffer[2];
|
|
buffer[0] = 'a' + i;
|
|
buffer[1] = '\0';
|
|
CHECK(JS_SetProperty(cx, obj, buffer, val));
|
|
CHECK(map.putNew(obj->as<NativeObject>().lastProperty(), obj));
|
|
}
|
|
|
|
JS_GC(rt);
|
|
JS_GC(rt);
|
|
|
|
for (auto r = map.all(); !r.empty(); r.popFront()) {
|
|
RootedObject obj(cx, r.front().value());
|
|
CHECK(obj->as<NativeObject>().lastProperty() == r.front().key());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testGCRootedHashMap)
|
|
|
|
static bool
|
|
FillMyHashMap(JSContext* cx, MutableHandle<MyHashMap> map)
|
|
{
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
|
RootedValue val(cx, UndefinedValue());
|
|
// Construct a unique property name to ensure that the object creates a
|
|
// new shape.
|
|
char buffer[2];
|
|
buffer[0] = 'a' + i;
|
|
buffer[1] = '\0';
|
|
if (!JS_SetProperty(cx, obj, buffer, val))
|
|
return false;
|
|
if (!map.putNew(obj->as<NativeObject>().lastProperty(), obj))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
CheckMyHashMap(JSContext* cx, Handle<MyHashMap> map)
|
|
{
|
|
for (auto r = map.all(); !r.empty(); r.popFront()) {
|
|
RootedObject obj(cx, r.front().value());
|
|
if (obj->as<NativeObject>().lastProperty() != r.front().key())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
BEGIN_TEST(testGCHandleHashMap)
|
|
{
|
|
JS::Rooted<MyHashMap> map(cx, MyHashMap(cx));
|
|
CHECK(map.init(15));
|
|
CHECK(map.initialized());
|
|
|
|
CHECK(FillMyHashMap(cx, &map));
|
|
|
|
JS_GC(rt);
|
|
JS_GC(rt);
|
|
|
|
CHECK(CheckMyHashMap(cx, map));
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testGCHandleHashMap)
|
|
|
|
using ShapeVec = TraceableVector<Shape*>;
|
|
|
|
BEGIN_TEST(testGCRootedVector)
|
|
{
|
|
JS::Rooted<ShapeVec> shapes(cx, ShapeVec(cx));
|
|
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
|
RootedValue val(cx, UndefinedValue());
|
|
// Construct a unique property name to ensure that the object creates a
|
|
// new shape.
|
|
char buffer[2];
|
|
buffer[0] = 'a' + i;
|
|
buffer[1] = '\0';
|
|
CHECK(JS_SetProperty(cx, obj, buffer, val));
|
|
CHECK(shapes.append(obj->as<NativeObject>().lastProperty()));
|
|
}
|
|
|
|
JS_GC(rt);
|
|
JS_GC(rt);
|
|
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
// Check the shape to ensure it did not get collected.
|
|
char buffer[2];
|
|
buffer[0] = 'a' + i;
|
|
buffer[1] = '\0';
|
|
bool match;
|
|
CHECK(JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes[i]->propid()), buffer, &match));
|
|
CHECK(match);
|
|
}
|
|
|
|
// Ensure iterator enumeration works through the rooted.
|
|
for (auto shape : shapes) {
|
|
CHECK(shape);
|
|
}
|
|
|
|
CHECK(receiveConstRefToShapeVector(shapes));
|
|
|
|
// Ensure rooted converts to handles.
|
|
CHECK(receiveHandleToShapeVector(shapes));
|
|
CHECK(receiveMutableHandleToShapeVector(&shapes));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
receiveConstRefToShapeVector(const JS::Rooted<TraceableVector<Shape*>>& rooted)
|
|
{
|
|
// Ensure range enumeration works through the reference.
|
|
for (auto shape : rooted) {
|
|
CHECK(shape);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
receiveHandleToShapeVector(JS::Handle<TraceableVector<Shape*>> handle)
|
|
{
|
|
// Ensure range enumeration works through the handle.
|
|
for (auto shape : handle) {
|
|
CHECK(shape);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
receiveMutableHandleToShapeVector(JS::MutableHandle<TraceableVector<Shape*>> handle)
|
|
{
|
|
// Ensure range enumeration works through the handle.
|
|
for (auto shape : handle) {
|
|
CHECK(shape);
|
|
}
|
|
return true;
|
|
}
|
|
END_TEST(testGCRootedVector)
|
|
|
|
BEGIN_TEST(testTraceableFifo)
|
|
{
|
|
using ShapeFifo = TraceableFifo<Shape*>;
|
|
JS::Rooted<ShapeFifo> shapes(cx, ShapeFifo(cx));
|
|
CHECK(shapes.empty());
|
|
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
|
RootedValue val(cx, UndefinedValue());
|
|
// Construct a unique property name to ensure that the object creates a
|
|
// new shape.
|
|
char buffer[2];
|
|
buffer[0] = 'a' + i;
|
|
buffer[1] = '\0';
|
|
CHECK(JS_SetProperty(cx, obj, buffer, val));
|
|
CHECK(shapes.pushBack(obj->as<NativeObject>().lastProperty()));
|
|
}
|
|
|
|
CHECK(shapes.length() == 10);
|
|
|
|
JS_GC(rt);
|
|
JS_GC(rt);
|
|
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
// Check the shape to ensure it did not get collected.
|
|
char buffer[2];
|
|
buffer[0] = 'a' + i;
|
|
buffer[1] = '\0';
|
|
bool match;
|
|
CHECK(JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes.front()->propid()), buffer, &match));
|
|
CHECK(match);
|
|
CHECK(shapes.popFront());
|
|
}
|
|
|
|
CHECK(shapes.empty());
|
|
return true;
|
|
}
|
|
END_TEST(testTraceableFifo)
|
|
|
|
using ShapeVec = TraceableVector<Shape*>;
|
|
|
|
static bool
|
|
FillVector(JSContext* cx, MutableHandle<ShapeVec> shapes)
|
|
{
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
|
RootedValue val(cx, UndefinedValue());
|
|
// Construct a unique property name to ensure that the object creates a
|
|
// new shape.
|
|
char buffer[2];
|
|
buffer[0] = 'a' + i;
|
|
buffer[1] = '\0';
|
|
if (!JS_SetProperty(cx, obj, buffer, val))
|
|
return false;
|
|
if (!shapes.append(obj->as<NativeObject>().lastProperty()))
|
|
return false;
|
|
}
|
|
|
|
// Ensure iterator enumeration works through the mutable handle.
|
|
for (auto shape : shapes) {
|
|
if (!shape)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
CheckVector(JSContext* cx, Handle<ShapeVec> shapes)
|
|
{
|
|
for (size_t i = 0; i < 10; ++i) {
|
|
// Check the shape to ensure it did not get collected.
|
|
char buffer[2];
|
|
buffer[0] = 'a' + i;
|
|
buffer[1] = '\0';
|
|
bool match;
|
|
if (!JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes[i]->propid()), buffer, &match))
|
|
return false;
|
|
if (!match)
|
|
return false;
|
|
}
|
|
|
|
// Ensure iterator enumeration works through the handle.
|
|
for (auto shape : shapes) {
|
|
if (!shape)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
BEGIN_TEST(testGCHandleVector)
|
|
{
|
|
JS::Rooted<ShapeVec> vec(cx, ShapeVec(cx));
|
|
|
|
CHECK(FillVector(cx, &vec));
|
|
|
|
JS_GC(rt);
|
|
JS_GC(rt);
|
|
|
|
CHECK(CheckVector(cx, vec));
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testGCHandleVector)
|