Dactyloidae/js/src/jsweakmap.cpp
Moonchild 24b1a0f166 Avoid TLS lookups when checking if zones need to be marked for GC.
This converts the fromPointer() calls to an object cast to (hopefully)
avoid the GC crashes. Logic is reversed from MustSkipMarking to
ShouldMark with appropriate renames.
2023-07-03 14:59:02 +08:00

220 lines
5.2 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "jsweakmap.h"
#include <string.h>
#include "jsapi.h"
#include "jscntxt.h"
#include "jsfriendapi.h"
#include "jsobj.h"
#include "jswrapper.h"
#include "js/GCAPI.h"
#include "vm/GlobalObject.h"
#include "jsobjinlines.h"
using namespace js;
using namespace js::gc;
WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
: memberOf(memOf),
zone(zone),
marked(false)
{
MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
}
WeakMapBase::~WeakMapBase()
{
MOZ_ASSERT(CurrentThreadIsGCSweeping());
}
void
WeakMapBase::unmarkZone(JS::Zone* zone)
{
for (WeakMapBase* m : zone->gcWeakMapList)
m->marked = false;
}
void
WeakMapBase::markAll(JS::Zone* zone, JSTracer* tracer)
{
MOZ_ASSERT(tracer->weakMapAction() != DoNotTraceWeakMaps);
for (WeakMapBase* m : zone->gcWeakMapList) {
m->trace(tracer);
TraceNullableEdge(tracer, &m->memberOf, "memberOf");
}
}
bool
WeakMapBase::markZoneIteratively(JS::Zone* zone, JSTracer* tracer)
{
bool markedAny = false;
for (WeakMapBase* m : zone->gcWeakMapList) {
if (m->marked && m->traceEntries(tracer))
markedAny = true;
}
return markedAny;
}
bool
WeakMapBase::findInterZoneEdges(JS::Zone* zone)
{
for (WeakMapBase* m : zone->gcWeakMapList) {
if (!m->findZoneEdges())
return false;
}
return true;
}
void
WeakMapBase::sweepZone(JS::Zone* zone)
{
for (WeakMapBase* m = zone->gcWeakMapList.getFirst(); m; ) {
WeakMapBase* next = m->getNext();
if (m->marked) {
m->sweep();
} else {
/* Destroy the hash map now to catch any use after this point. */
m->finish();
m->removeFrom(zone->gcWeakMapList);
}
m = next;
}
#ifdef DEBUG
for (WeakMapBase* m : zone->gcWeakMapList)
MOZ_ASSERT(m->isInList() && m->marked);
#endif
}
void
WeakMapBase::traceAllMappings(WeakMapTracer* tracer)
{
JSRuntime* rt = tracer->context;
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
for (WeakMapBase* m : zone->gcWeakMapList) {
// The WeakMapTracer callback is not allowed to GC.
JS::AutoSuppressGCAnalysis nogc;
m->traceMappings(tracer);
}
}
}
bool
WeakMapBase::saveZoneMarkedWeakMaps(JS::Zone* zone, WeakMapSet& markedWeakMaps)
{
for (WeakMapBase* m : zone->gcWeakMapList) {
if (m->marked && !markedWeakMaps.put(m))
return false;
}
return true;
}
void
WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps)
{
for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
WeakMapBase* map = r.front();
MOZ_ASSERT(map->zone->shouldMarkInZone());
MOZ_ASSERT(!map->marked);
map->marked = true;
}
}
bool
ObjectValueMap::findZoneEdges()
{
/*
* For unmarked weakmap keys with delegates in a different zone, add a zone
* edge to ensure that the delegate zone finishes marking before the key
* zone.
*/
JS::AutoSuppressGCAnalysis nogc;
for (Range r = all(); !r.empty(); r.popFront()) {
JSObject* key = r.front().key();
if (key->asTenured().isMarked(BLACK) && !key->asTenured().isMarked(GRAY))
continue;
JSObject* delegate = getDelegate(key);
if (!delegate)
continue;
Zone* delegateZone = delegate->zone();
if (delegateZone == zone || !delegateZone->shouldMarkInZone())
continue;
if (!delegateZone->gcZoneGroupEdges.put(key->zone()))
return false;
}
return true;
}
ObjectWeakMap::ObjectWeakMap(JSContext* cx)
: map(cx, nullptr)
{}
bool
ObjectWeakMap::init()
{
return map.init();
}
JSObject*
ObjectWeakMap::lookup(const JSObject* obj)
{
MOZ_ASSERT(map.initialized());
if (ObjectValueMap::Ptr p = map.lookup(const_cast<JSObject*>(obj)))
return &p->value().toObject();
return nullptr;
}
bool
ObjectWeakMap::add(JSContext* cx, JSObject* obj, JSObject* target)
{
MOZ_ASSERT(obj && target);
MOZ_ASSERT(map.initialized());
MOZ_ASSERT(!map.has(obj));
if (!map.put(obj, ObjectValue(*target))) {
ReportOutOfMemory(cx);
return false;
}
return true;
}
void
ObjectWeakMap::clear()
{
MOZ_ASSERT(map.initialized());
map.clear();
}
void
ObjectWeakMap::trace(JSTracer* trc)
{
MOZ_ASSERT(map.initialized());
map.trace(trc);
}
size_t
ObjectWeakMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
MOZ_ASSERT(map.initialized());
return map.sizeOfExcludingThis(mallocSizeOf);
}
#ifdef JSGC_HASH_TABLE_CHECKS
void
ObjectWeakMap::checkAfterMovingGC()
{
MOZ_ASSERT(map.initialized());
for (ObjectValueMap::Range r = map.all(); !r.empty(); r.popFront()) {
CheckGCThingAfterMovingGC(r.front().key().get());
CheckGCThingAfterMovingGC(&r.front().value().toObject());
}
}
#endif // JSGC_HASH_TABLE_CHECKS