mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-27 02:29:35 +00:00
255 lines
6.6 KiB
C++
255 lines
6.6 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 "jscompartment.h"
|
|
|
|
#include "gc/Zone.h"
|
|
|
|
#include "jsapi-tests/tests.h"
|
|
|
|
JSObject* keyDelegate = nullptr;
|
|
|
|
BEGIN_TEST(testWeakMap_basicOperations)
|
|
{
|
|
JS::RootedObject map(cx, JS::NewWeakMapObject(cx));
|
|
CHECK(IsWeakMapObject(map));
|
|
|
|
JS::RootedObject key(cx, newKey());
|
|
CHECK(key);
|
|
CHECK(!IsWeakMapObject(key));
|
|
|
|
JS::RootedValue r(cx);
|
|
CHECK(GetWeakMapEntry(cx, map, key, &r));
|
|
CHECK(r.isUndefined());
|
|
|
|
CHECK(checkSize(map, 0));
|
|
|
|
JS::RootedValue val(cx, JS::Int32Value(1));
|
|
CHECK(SetWeakMapEntry(cx, map, key, val));
|
|
|
|
CHECK(GetWeakMapEntry(cx, map, key, &r));
|
|
CHECK(r == val);
|
|
CHECK(checkSize(map, 1));
|
|
|
|
JS_GC(rt);
|
|
|
|
CHECK(GetWeakMapEntry(cx, map, key, &r));
|
|
CHECK(r == val);
|
|
CHECK(checkSize(map, 1));
|
|
|
|
key = nullptr;
|
|
JS_GC(rt);
|
|
|
|
CHECK(checkSize(map, 0));
|
|
|
|
return true;
|
|
}
|
|
|
|
JSObject* newKey()
|
|
{
|
|
return JS_NewPlainObject(cx);
|
|
}
|
|
|
|
bool
|
|
checkSize(JS::HandleObject map, uint32_t expected)
|
|
{
|
|
JS::RootedObject keys(cx);
|
|
CHECK(JS_NondeterministicGetWeakMapKeys(cx, map, &keys));
|
|
|
|
uint32_t length;
|
|
CHECK(JS_GetArrayLength(cx, keys, &length));
|
|
CHECK(length == expected);
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testWeakMap_basicOperations)
|
|
|
|
BEGIN_TEST(testWeakMap_keyDelegates)
|
|
{
|
|
JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
|
|
JS_GC(rt);
|
|
JS::RootedObject map(cx, JS::NewWeakMapObject(cx));
|
|
CHECK(map);
|
|
|
|
JS::RootedObject key(cx, newKey());
|
|
CHECK(key);
|
|
|
|
JS::RootedObject delegate(cx, newDelegate());
|
|
CHECK(delegate);
|
|
keyDelegate = delegate;
|
|
|
|
JS::RootedObject delegateRoot(cx);
|
|
{
|
|
JSAutoCompartment ac(cx, delegate);
|
|
delegateRoot = JS_NewPlainObject(cx);
|
|
CHECK(delegateRoot);
|
|
JS::RootedValue delegateValue(cx, JS::ObjectValue(*delegate));
|
|
CHECK(JS_DefineProperty(cx, delegateRoot, "delegate", delegateValue, 0));
|
|
}
|
|
delegate = nullptr;
|
|
|
|
/*
|
|
* Perform an incremental GC, introducing an unmarked CCW to force the map
|
|
* zone to finish marking before the delegate zone.
|
|
*/
|
|
CHECK(newCCW(map, delegateRoot));
|
|
js::SliceBudget budget(js::WorkBudget(1000000));
|
|
rt->gc.startDebugGC(GC_NORMAL, budget);
|
|
CHECK(!JS::IsIncrementalGCInProgress(rt));
|
|
#ifdef DEBUG
|
|
CHECK(map->zone()->lastZoneGroupIndex() < delegateRoot->zone()->lastZoneGroupIndex());
|
|
#endif
|
|
|
|
/* Add our entry to the weakmap. */
|
|
JS::RootedValue val(cx, JS::Int32Value(1));
|
|
CHECK(SetWeakMapEntry(cx, map, key, val));
|
|
CHECK(checkSize(map, 1));
|
|
|
|
/* Check the delegate keeps the entry alive even if the key is not reachable. */
|
|
key = nullptr;
|
|
CHECK(newCCW(map, delegateRoot));
|
|
budget = js::SliceBudget(js::WorkBudget(100000));
|
|
rt->gc.startDebugGC(GC_NORMAL, budget);
|
|
CHECK(!JS::IsIncrementalGCInProgress(rt));
|
|
CHECK(checkSize(map, 1));
|
|
|
|
/*
|
|
* Check that the zones finished marking at the same time, which is
|
|
* necessary because of the presence of the delegate and the CCW.
|
|
*/
|
|
#ifdef DEBUG
|
|
CHECK(map->zone()->lastZoneGroupIndex() == delegateRoot->zone()->lastZoneGroupIndex());
|
|
#endif
|
|
|
|
/* Check that when the delegate becomes unreachable the entry is removed. */
|
|
delegateRoot = nullptr;
|
|
keyDelegate = nullptr;
|
|
JS_GC(rt);
|
|
CHECK(checkSize(map, 0));
|
|
|
|
return true;
|
|
}
|
|
|
|
static void DelegateObjectMoved(JSObject* obj, const JSObject* old)
|
|
{
|
|
if (!keyDelegate)
|
|
return; // Object got moved before we set keyDelegate to point to it.
|
|
|
|
MOZ_RELEASE_ASSERT(keyDelegate == old);
|
|
keyDelegate = obj;
|
|
}
|
|
|
|
static JSObject* GetKeyDelegate(JSObject* obj)
|
|
{
|
|
return keyDelegate;
|
|
}
|
|
|
|
JSObject* newKey()
|
|
{
|
|
static const js::Class keyClass = {
|
|
"keyWithDelgate",
|
|
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1),
|
|
nullptr, /* addProperty */
|
|
nullptr, /* delProperty */
|
|
nullptr, /* getProperty */
|
|
nullptr, /* setProperty */
|
|
nullptr, /* enumerate */
|
|
nullptr, /* resolve */
|
|
nullptr, /* mayResolve */
|
|
nullptr, /* finalize */
|
|
nullptr, /* call */
|
|
nullptr, /* hasInstance */
|
|
nullptr, /* construct */
|
|
nullptr, /* trace */
|
|
JS_NULL_CLASS_SPEC,
|
|
{
|
|
false,
|
|
GetKeyDelegate
|
|
},
|
|
JS_NULL_OBJECT_OPS
|
|
};
|
|
|
|
JS::RootedObject key(cx, JS_NewObject(cx, Jsvalify(&keyClass)));
|
|
if (!key)
|
|
return nullptr;
|
|
|
|
return key;
|
|
}
|
|
|
|
JSObject* newCCW(JS::HandleObject sourceZone, JS::HandleObject destZone)
|
|
{
|
|
/*
|
|
* Now ensure that this zone will be swept first by adding a cross
|
|
* compartment wrapper to a new objct in the same zone as the
|
|
* delegate obejct.
|
|
*/
|
|
JS::RootedObject object(cx);
|
|
{
|
|
JSAutoCompartment ac(cx, destZone);
|
|
object = JS_NewPlainObject(cx);
|
|
if (!object)
|
|
return nullptr;
|
|
}
|
|
{
|
|
JSAutoCompartment ac(cx, sourceZone);
|
|
if (!JS_WrapObject(cx, &object))
|
|
return nullptr;
|
|
}
|
|
return object;
|
|
}
|
|
|
|
JSObject* newDelegate()
|
|
{
|
|
static const js::Class delegateClass = {
|
|
"delegate",
|
|
JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_RESERVED_SLOTS(1),
|
|
nullptr, /* addProperty */
|
|
nullptr, /* delProperty */
|
|
nullptr, /* getProperty */
|
|
nullptr, /* setProperty */
|
|
nullptr, /* enumerate */
|
|
nullptr, /* resolve */
|
|
nullptr, /* mayResolve */
|
|
nullptr, /* finalize */
|
|
nullptr, /* call */
|
|
nullptr, /* hasInstance */
|
|
nullptr, /* construct */
|
|
JS_GlobalObjectTraceHook,
|
|
JS_NULL_CLASS_SPEC,
|
|
{
|
|
false,
|
|
nullptr,
|
|
DelegateObjectMoved
|
|
},
|
|
JS_NULL_OBJECT_OPS
|
|
};
|
|
|
|
/* Create the global object. */
|
|
JS::CompartmentOptions options;
|
|
options.setVersion(JSVERSION_LATEST);
|
|
JS::RootedObject global(cx);
|
|
global = JS_NewGlobalObject(cx, Jsvalify(&delegateClass), nullptr, JS::FireOnNewGlobalHook,
|
|
options);
|
|
JS_SetReservedSlot(global, 0, JS::Int32Value(42));
|
|
|
|
return global;
|
|
}
|
|
|
|
bool
|
|
checkSize(JS::HandleObject map, uint32_t expected)
|
|
{
|
|
JS::RootedObject keys(cx);
|
|
CHECK(JS_NondeterministicGetWeakMapKeys(cx, map, &keys));
|
|
|
|
uint32_t length;
|
|
CHECK(JS_GetArrayLength(cx, keys, &length));
|
|
CHECK(length == expected);
|
|
|
|
return true;
|
|
}
|
|
END_TEST(testWeakMap_keyDelegates)
|