2011-04-20 05:30:10 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2016-11-04 17:32:36 +00:00
|
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
2012-05-21 11:12:37 +00:00
|
|
|
* 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/. */
|
2010-09-24 17:54:39 +00:00
|
|
|
|
2013-06-20 00:59:46 +00:00
|
|
|
#ifndef jsgcinlines_h
|
|
|
|
#define jsgcinlines_h
|
2010-09-24 17:54:39 +00:00
|
|
|
|
|
|
|
#include "jsgc.h"
|
2013-07-24 00:34:18 +00:00
|
|
|
|
2016-02-29 16:22:00 +00:00
|
|
|
#include "mozilla/DebugOnly.h"
|
2016-03-30 11:33:55 +00:00
|
|
|
#include "mozilla/Maybe.h"
|
2016-02-29 16:22:00 +00:00
|
|
|
|
2014-06-30 09:10:40 +00:00
|
|
|
#include "gc/GCTrace.h"
|
2013-08-08 23:07:22 +00:00
|
|
|
#include "gc/Zone.h"
|
2010-09-24 17:54:39 +00:00
|
|
|
|
2010-10-13 18:49:22 +00:00
|
|
|
namespace js {
|
|
|
|
namespace gc {
|
|
|
|
|
2016-05-19 18:27:57 +00:00
|
|
|
inline void
|
|
|
|
MakeAccessibleAfterMovingGC(void* anyp) {}
|
|
|
|
|
|
|
|
inline void
|
|
|
|
MakeAccessibleAfterMovingGC(JSObject* obj) {
|
|
|
|
if (obj->isNative())
|
|
|
|
obj->as<NativeObject>().updateShapeAfterMovingGC();
|
|
|
|
}
|
|
|
|
|
2011-10-11 00:14:38 +00:00
|
|
|
static inline AllocKind
|
2015-03-28 22:22:11 +00:00
|
|
|
GetGCObjectKind(const Class* clasp)
|
2011-10-11 00:14:38 +00:00
|
|
|
{
|
2013-06-18 06:53:49 +00:00
|
|
|
if (clasp == FunctionClassPtr)
|
2015-05-06 14:52:46 +00:00
|
|
|
return AllocKind::FUNCTION;
|
Bug 708735 - Use <stdint.h> types in JSAPI and throughout SpiderMonkey. Continue to provide the {u,}int{8,16,32,64} and JS{Uint,Int}{8,16,32,64} integer types through a single header, however, for a simpler backout strategy -- and also to ease the transition for embedders. r=timeless on switching the jsd API to use the <stdint.h> types, r=luke, r=dmandelin
2011-12-09 03:54:10 +00:00
|
|
|
uint32_t nslots = JSCLASS_RESERVED_SLOTS(clasp);
|
2011-10-11 00:14:38 +00:00
|
|
|
if (clasp->flags & JSCLASS_HAS_PRIVATE)
|
|
|
|
nslots++;
|
|
|
|
return GetGCObjectKind(nslots);
|
|
|
|
}
|
2013-03-11 17:47:33 +00:00
|
|
|
|
2014-06-07 09:34:57 +00:00
|
|
|
inline void
|
|
|
|
GCRuntime::poke()
|
2011-06-02 00:48:52 +00:00
|
|
|
{
|
2014-06-07 09:34:57 +00:00
|
|
|
poked = true;
|
2011-06-02 00:48:52 +00:00
|
|
|
|
|
|
|
#ifdef JS_GC_ZEAL
|
|
|
|
/* Schedule a GC to happen "soon" after a GC poke. */
|
2016-02-05 11:21:43 +00:00
|
|
|
if (hasZealMode(ZealMode::Poke))
|
2014-06-07 09:34:57 +00:00
|
|
|
nextScheduled = 1;
|
2011-06-02 00:48:52 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-08-09 18:16:33 +00:00
|
|
|
class ArenaIter
|
2011-08-26 19:39:31 +00:00
|
|
|
{
|
2016-02-29 16:24:00 +00:00
|
|
|
Arena* arena;
|
|
|
|
Arena* unsweptArena;
|
|
|
|
Arena* sweptArena;
|
2016-02-29 16:22:00 +00:00
|
|
|
mozilla::DebugOnly<bool> initialized;
|
2012-08-09 18:16:33 +00:00
|
|
|
|
|
|
|
public:
|
2016-02-29 16:22:00 +00:00
|
|
|
ArenaIter()
|
2016-02-29 16:24:00 +00:00
|
|
|
: arena(nullptr), unsweptArena(nullptr), sweptArena(nullptr), initialized(false) {}
|
2012-08-09 18:16:33 +00:00
|
|
|
|
2016-02-29 16:22:00 +00:00
|
|
|
ArenaIter(JS::Zone* zone, AllocKind kind) : initialized(false) { init(zone, kind); }
|
2012-08-09 18:16:33 +00:00
|
|
|
|
2015-03-28 22:22:11 +00:00
|
|
|
void init(JS::Zone* zone, AllocKind kind) {
|
2016-02-29 16:22:00 +00:00
|
|
|
MOZ_ASSERT(!initialized);
|
|
|
|
MOZ_ASSERT(zone);
|
|
|
|
initialized = true;
|
2016-02-29 16:24:00 +00:00
|
|
|
arena = zone->arenas.getFirstArena(kind);
|
|
|
|
unsweptArena = zone->arenas.getFirstArenaToSweep(kind);
|
|
|
|
sweptArena = zone->arenas.getFirstSweptArena(kind);
|
|
|
|
if (!unsweptArena) {
|
|
|
|
unsweptArena = sweptArena;
|
|
|
|
sweptArena = nullptr;
|
2014-07-16 15:10:00 +00:00
|
|
|
}
|
2016-02-29 16:24:00 +00:00
|
|
|
if (!arena) {
|
|
|
|
arena = unsweptArena;
|
|
|
|
unsweptArena = sweptArena;
|
|
|
|
sweptArena = nullptr;
|
2011-08-26 19:39:31 +00:00
|
|
|
}
|
|
|
|
}
|
2012-08-09 18:16:33 +00:00
|
|
|
|
2014-04-30 01:00:27 +00:00
|
|
|
bool done() const {
|
2016-02-29 16:22:00 +00:00
|
|
|
MOZ_ASSERT(initialized);
|
2016-02-29 16:24:00 +00:00
|
|
|
return !arena;
|
2012-08-09 18:16:33 +00:00
|
|
|
}
|
|
|
|
|
2016-02-29 16:24:00 +00:00
|
|
|
Arena* get() const {
|
2016-02-29 16:22:00 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2016-02-29 16:24:00 +00:00
|
|
|
return arena;
|
2012-08-09 18:16:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void next() {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2016-02-29 16:24:00 +00:00
|
|
|
arena = arena->next;
|
|
|
|
if (!arena) {
|
|
|
|
arena = unsweptArena;
|
|
|
|
unsweptArena = sweptArena;
|
|
|
|
sweptArena = nullptr;
|
2012-08-09 18:16:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2011-08-26 19:39:31 +00:00
|
|
|
|
2017-02-10 10:22:38 +00:00
|
|
|
enum CellIterNeedsBarrier : uint8_t
|
|
|
|
{
|
|
|
|
CellIterDoesntNeedBarrier = 0,
|
|
|
|
CellIterMayNeedBarrier = 1
|
|
|
|
};
|
|
|
|
|
2014-04-29 06:39:44 +00:00
|
|
|
class ArenaCellIterImpl
|
2011-08-09 08:51:59 +00:00
|
|
|
{
|
2011-08-18 07:16:08 +00:00
|
|
|
size_t firstThingOffset;
|
2011-08-09 08:51:59 +00:00
|
|
|
size_t thingSize;
|
2016-02-29 16:24:00 +00:00
|
|
|
Arena* arenaAddr;
|
2014-04-30 00:59:03 +00:00
|
|
|
FreeSpan span;
|
2016-02-29 11:50:00 +00:00
|
|
|
uint_fast16_t thing;
|
2017-02-10 10:22:38 +00:00
|
|
|
JS::TraceKind traceKind;
|
|
|
|
bool needsBarrier;
|
2016-02-29 16:22:00 +00:00
|
|
|
mozilla::DebugOnly<bool> initialized;
|
2011-08-09 08:51:59 +00:00
|
|
|
|
2014-04-30 00:59:03 +00:00
|
|
|
// Upon entry, |thing| points to any thing (free or used) and finds the
|
|
|
|
// first used thing, which may be |thing|.
|
|
|
|
void moveForwardIfFree() {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
|
|
|
MOZ_ASSERT(thing);
|
2014-05-01 04:30:53 +00:00
|
|
|
// Note: if |span| is empty, this test will fail, which is what we want
|
|
|
|
// -- |span| being empty means that we're past the end of the last free
|
|
|
|
// thing, all the remaining things in the arena are used, and we'll
|
|
|
|
// never need to move forward.
|
2014-04-30 00:59:03 +00:00
|
|
|
if (thing == span.first) {
|
2014-05-01 04:30:53 +00:00
|
|
|
thing = span.last + thingSize;
|
2016-02-29 11:50:00 +00:00
|
|
|
span = *span.nextSpan(arenaAddr);
|
2014-04-30 00:59:03 +00:00
|
|
|
}
|
2011-08-09 08:51:59 +00:00
|
|
|
}
|
|
|
|
|
2014-04-30 00:59:03 +00:00
|
|
|
public:
|
2014-06-10 05:04:14 +00:00
|
|
|
ArenaCellIterImpl()
|
2017-02-10 10:22:38 +00:00
|
|
|
: firstThingOffset(0),
|
|
|
|
thingSize(0),
|
|
|
|
arenaAddr(nullptr),
|
|
|
|
thing(0),
|
|
|
|
traceKind(JS::TraceKind::Null),
|
|
|
|
needsBarrier(false),
|
|
|
|
initialized(false)
|
|
|
|
{}
|
|
|
|
|
|
|
|
explicit ArenaCellIterImpl(Arena* arena, CellIterNeedsBarrier mayNeedBarrier)
|
|
|
|
: initialized(false)
|
|
|
|
{
|
|
|
|
init(arena, mayNeedBarrier);
|
|
|
|
}
|
2014-04-30 00:59:03 +00:00
|
|
|
|
2017-02-10 10:22:38 +00:00
|
|
|
void init(Arena* arena, CellIterNeedsBarrier mayNeedBarrier) {
|
2016-02-29 16:22:00 +00:00
|
|
|
MOZ_ASSERT(!initialized);
|
2016-02-29 16:24:00 +00:00
|
|
|
MOZ_ASSERT(arena);
|
2016-02-29 16:22:00 +00:00
|
|
|
initialized = true;
|
2016-02-29 16:24:00 +00:00
|
|
|
AllocKind kind = arena->getAllocKind();
|
2011-08-18 07:16:08 +00:00
|
|
|
firstThingOffset = Arena::firstThingOffset(kind);
|
|
|
|
thingSize = Arena::thingSize(kind);
|
2017-02-10 10:22:38 +00:00
|
|
|
traceKind = MapAllocToTraceKind(kind);
|
|
|
|
needsBarrier = mayNeedBarrier && !JS::CurrentThreadIsHeapCollecting();
|
2016-02-29 16:24:00 +00:00
|
|
|
reset(arena);
|
2012-02-17 22:35:20 +00:00
|
|
|
}
|
|
|
|
|
2014-04-30 00:59:03 +00:00
|
|
|
// Use this to move from an Arena of a particular kind to another Arena of
|
|
|
|
// the same kind.
|
2016-02-29 16:24:00 +00:00
|
|
|
void reset(Arena* arena) {
|
2016-02-29 16:22:00 +00:00
|
|
|
MOZ_ASSERT(initialized);
|
2016-02-29 16:24:00 +00:00
|
|
|
MOZ_ASSERT(arena);
|
|
|
|
arenaAddr = arena;
|
|
|
|
span = *arena->getFirstFreeSpan();
|
2016-02-29 11:50:00 +00:00
|
|
|
thing = firstThingOffset;
|
2014-04-30 00:59:03 +00:00
|
|
|
moveForwardIfFree();
|
2012-02-17 22:35:20 +00:00
|
|
|
}
|
|
|
|
|
2014-04-29 06:39:44 +00:00
|
|
|
bool done() const {
|
2016-02-29 16:22:00 +00:00
|
|
|
MOZ_ASSERT(initialized);
|
2016-02-29 11:50:00 +00:00
|
|
|
MOZ_ASSERT(thing <= ArenaSize);
|
|
|
|
return thing == ArenaSize;
|
2014-04-29 06:39:44 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 22:22:11 +00:00
|
|
|
TenuredCell* getCell() const {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2017-02-10 10:22:38 +00:00
|
|
|
TenuredCell* cell = reinterpret_cast<TenuredCell*>(uintptr_t(arenaAddr) + thing);
|
|
|
|
|
|
|
|
// This can result in a a new reference being created to an object that
|
|
|
|
// an ongoing incremental GC may find to be unreachable, so we may need
|
|
|
|
// a barrier here.
|
|
|
|
if (needsBarrier)
|
|
|
|
ExposeGCThingToActiveJS(JS::GCCellPtr(cell, traceKind));
|
|
|
|
|
|
|
|
return cell;
|
2014-04-29 06:39:44 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 22:22:11 +00:00
|
|
|
template<typename T> T* get() const {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2017-02-10 10:22:38 +00:00
|
|
|
MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
|
2015-03-28 22:22:11 +00:00
|
|
|
return static_cast<T*>(getCell());
|
2014-04-29 06:39:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void next() {
|
2014-04-30 00:59:03 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2014-04-29 06:39:44 +00:00
|
|
|
thing += thingSize;
|
2016-02-29 11:50:00 +00:00
|
|
|
if (thing < ArenaSize)
|
2014-04-30 00:59:03 +00:00
|
|
|
moveForwardIfFree();
|
2014-04-29 06:39:44 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-17 17:32:37 +00:00
|
|
|
template<>
|
2015-03-28 22:22:11 +00:00
|
|
|
JSObject*
|
2014-09-17 17:32:37 +00:00
|
|
|
ArenaCellIterImpl::get<JSObject>() const;
|
|
|
|
|
2016-11-04 17:32:36 +00:00
|
|
|
class ArenaCellIter : public ArenaCellIterImpl
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit ArenaCellIter(Arena* arena)
|
2017-02-10 10:22:38 +00:00
|
|
|
: ArenaCellIterImpl(arena, CellIterMayNeedBarrier)
|
2016-11-04 17:32:36 +00:00
|
|
|
{
|
2017-02-02 19:12:43 +00:00
|
|
|
MOZ_ASSERT(JS::CurrentThreadIsHeapTracing());
|
2016-11-04 17:32:36 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-04-29 06:39:44 +00:00
|
|
|
class ArenaCellIterUnderGC : public ArenaCellIterImpl
|
|
|
|
{
|
|
|
|
public:
|
2016-11-04 17:32:36 +00:00
|
|
|
explicit ArenaCellIterUnderGC(Arena* arena)
|
2017-02-10 10:22:38 +00:00
|
|
|
: ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier)
|
2016-11-04 17:32:36 +00:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(CurrentThreadIsPerformingGC());
|
2014-04-29 06:39:44 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-04-30 01:03:28 +00:00
|
|
|
class ArenaCellIterUnderFinalize : public ArenaCellIterImpl
|
|
|
|
{
|
|
|
|
public:
|
2016-11-04 17:32:36 +00:00
|
|
|
explicit ArenaCellIterUnderFinalize(Arena* arena)
|
2017-02-10 10:22:38 +00:00
|
|
|
: ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier)
|
2016-11-04 17:32:36 +00:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(CurrentThreadIsGCSweeping());
|
|
|
|
}
|
2014-04-30 01:03:28 +00:00
|
|
|
};
|
|
|
|
|
2017-02-23 16:26:14 +00:00
|
|
|
class ArenaCellIterUnbarriered : public ArenaCellIterImpl
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit ArenaCellIterUnbarriered(Arena* arena)
|
|
|
|
: ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier)
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
template <typename T>
|
|
|
|
class ZoneCellIter;
|
|
|
|
|
|
|
|
template <>
|
|
|
|
class ZoneCellIter<TenuredCell> {
|
2014-04-30 01:00:27 +00:00
|
|
|
ArenaIter arenaIter;
|
|
|
|
ArenaCellIterImpl cellIter;
|
2016-10-27 10:03:53 +00:00
|
|
|
mozilla::Maybe<JS::AutoAssertNoGC> nogc;
|
2014-04-29 06:39:44 +00:00
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
protected:
|
|
|
|
// For use when a subclass wants to insert some setup before init().
|
|
|
|
ZoneCellIter() {}
|
|
|
|
|
|
|
|
void init(JS::Zone* zone, AllocKind kind) {
|
2016-10-14 08:43:30 +00:00
|
|
|
MOZ_ASSERT_IF(IsNurseryAllocable(kind),
|
2017-02-02 19:12:43 +00:00
|
|
|
zone->isAtomsZone() || zone->group()->nursery().isEmpty());
|
2016-10-14 08:43:30 +00:00
|
|
|
initForTenuredIteration(zone, kind);
|
|
|
|
}
|
|
|
|
|
|
|
|
void initForTenuredIteration(JS::Zone* zone, AllocKind kind) {
|
2016-04-25 12:41:25 +00:00
|
|
|
JSRuntime* rt = zone->runtimeFromAnyThread();
|
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
// If called from outside a GC, ensure that the heap is in a state
|
|
|
|
// that allows us to iterate.
|
2017-02-02 19:12:43 +00:00
|
|
|
if (!JS::CurrentThreadIsHeapBusy()) {
|
2016-05-28 05:00:10 +00:00
|
|
|
// Assert that no GCs can occur while a ZoneCellIter is live.
|
2017-02-02 19:12:43 +00:00
|
|
|
nogc.emplace();
|
2016-05-28 05:00:10 +00:00
|
|
|
}
|
|
|
|
|
2016-04-25 12:41:25 +00:00
|
|
|
// We have a single-threaded runtime, so there's no need to protect
|
|
|
|
// against other threads iterating or allocating. However, we do have
|
|
|
|
// background finalization; we may have to wait for this to finish if
|
|
|
|
// it's currently active.
|
|
|
|
if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind))
|
|
|
|
rt->gc.waitBackgroundSweepEnd();
|
2014-04-30 01:00:27 +00:00
|
|
|
arenaIter.init(zone, kind);
|
|
|
|
if (!arenaIter.done())
|
2017-02-10 10:22:38 +00:00
|
|
|
cellIter.init(arenaIter.get(), CellIterMayNeedBarrier);
|
2011-08-09 08:51:59 +00:00
|
|
|
}
|
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
public:
|
|
|
|
ZoneCellIter(JS::Zone* zone, AllocKind kind) {
|
|
|
|
// If we are iterating a nursery-allocated kind then we need to
|
|
|
|
// evict first so that we can see all things.
|
2017-02-02 19:12:43 +00:00
|
|
|
if (IsNurseryAllocable(kind))
|
2017-02-21 11:25:22 +00:00
|
|
|
zone->runtimeFromActiveCooperatingThread()->gc.evictNursery();
|
2016-05-28 05:00:10 +00:00
|
|
|
|
|
|
|
init(zone, kind);
|
|
|
|
}
|
|
|
|
|
|
|
|
ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery&) {
|
|
|
|
// No need to evict the nursery. (This constructor is known statically
|
|
|
|
// to not GC.)
|
|
|
|
init(zone, kind);
|
|
|
|
}
|
|
|
|
|
2011-08-09 08:51:59 +00:00
|
|
|
bool done() const {
|
2014-04-30 01:00:27 +00:00
|
|
|
return arenaIter.done();
|
2011-08-09 08:51:59 +00:00
|
|
|
}
|
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
template<typename T>
|
|
|
|
T* get() const {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2014-04-30 01:00:27 +00:00
|
|
|
return cellIter.get<T>();
|
2011-08-09 08:51:59 +00:00
|
|
|
}
|
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
TenuredCell* getCell() const {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2014-04-30 01:00:27 +00:00
|
|
|
return cellIter.getCell();
|
2011-08-09 08:51:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void next() {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2014-04-30 01:00:27 +00:00
|
|
|
cellIter.next();
|
|
|
|
if (cellIter.done()) {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!arenaIter.done());
|
2014-04-30 01:00:27 +00:00
|
|
|
arenaIter.next();
|
|
|
|
if (!arenaIter.done())
|
|
|
|
cellIter.reset(arenaIter.get());
|
2011-08-09 08:51:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
// Iterator over the cells in a Zone, where the GC type (JSString, JSObject) is
|
|
|
|
// known, for a single AllocKind. Example usages:
|
|
|
|
//
|
|
|
|
// for (auto obj = zone->cellIter<JSObject>(AllocKind::OBJECT0); !obj.done(); obj.next())
|
|
|
|
// ...
|
|
|
|
//
|
|
|
|
// for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
|
|
|
|
// f(script->code());
|
|
|
|
//
|
|
|
|
// As this code demonstrates, you can use 'script' as if it were a JSScript*.
|
|
|
|
// Its actual type is ZoneCellIter<JSScript>, but for most purposes it will
|
|
|
|
// autoconvert to JSScript*.
|
|
|
|
//
|
|
|
|
// Note that in the JSScript case, ZoneCellIter is able to infer the AllocKind
|
|
|
|
// from the type 'JSScript', whereas in the JSObject case, the kind must be
|
|
|
|
// given (because there are multiple AllocKinds for objects).
|
|
|
|
//
|
|
|
|
// Also, the static rooting hazard analysis knows that the JSScript case will
|
|
|
|
// not GC during construction. The JSObject case needs to GC, or more precisely
|
|
|
|
// to empty the nursery and clear out the store buffer, so that it can see all
|
|
|
|
// objects to iterate over (the nursery is not iterable) and remove the
|
|
|
|
// possibility of having pointers from the store buffer to data hanging off
|
|
|
|
// stuff we're iterating over that we are going to delete. (The latter should
|
|
|
|
// not be a problem, since such instances should be using RelocatablePtr do
|
|
|
|
// remove themselves from the store buffer on deletion, but currently for
|
|
|
|
// subtle reasons that isn't good enough.)
|
|
|
|
//
|
|
|
|
// If the iterator is used within a GC, then there is no need to evict the
|
|
|
|
// nursery (again). You may select a variant that will skip the eviction either
|
|
|
|
// by specializing on a GCType that is never allocated in the nursery, or
|
|
|
|
// explicitly by passing in a trailing AutoAssertEmptyNursery argument.
|
|
|
|
//
|
|
|
|
template <typename GCType>
|
|
|
|
class ZoneCellIter : public ZoneCellIter<TenuredCell> {
|
2016-03-30 11:33:55 +00:00
|
|
|
public:
|
2016-05-28 05:00:10 +00:00
|
|
|
// Non-nursery allocated (equivalent to having an entry in
|
|
|
|
// MapTypeToFinalizeKind). The template declaration here is to discard this
|
|
|
|
// constructor overload if MapTypeToFinalizeKind<GCType>::kind does not
|
|
|
|
// exist. Note that there will be no remaining overloads that will work,
|
|
|
|
// which makes sense given that you haven't specified which of the
|
|
|
|
// AllocKinds to use for GCType.
|
|
|
|
//
|
|
|
|
// If we later add a nursery allocable GCType with a single AllocKind, we
|
|
|
|
// will want to add an overload of this constructor that does the right
|
|
|
|
// thing (ie, it empties the nursery before iterating.)
|
|
|
|
explicit ZoneCellIter(JS::Zone* zone) : ZoneCellIter<TenuredCell>() {
|
|
|
|
init(zone, MapTypeToFinalizeKind<GCType>::kind);
|
2016-03-30 11:33:55 +00:00
|
|
|
}
|
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
// Non-nursery allocated, nursery is known to be empty: same behavior as above.
|
|
|
|
ZoneCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery&) : ZoneCellIter(zone) {
|
|
|
|
}
|
2016-03-30 11:33:55 +00:00
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
// Arbitrary kind, which will be assumed to be nursery allocable (and
|
|
|
|
// therefore the nursery will be emptied before iterating.)
|
|
|
|
ZoneCellIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter<TenuredCell>(zone, kind) {
|
|
|
|
}
|
2016-05-31 18:15:41 +00:00
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
// Arbitrary kind, which will be assumed to be nursery allocable, but the
|
|
|
|
// nursery is known to be empty already: same behavior as non-nursery types.
|
|
|
|
ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery& empty)
|
|
|
|
: ZoneCellIter<TenuredCell>(zone, kind, empty)
|
|
|
|
{
|
2016-03-30 11:33:55 +00:00
|
|
|
}
|
|
|
|
|
2016-05-28 05:00:10 +00:00
|
|
|
GCType* get() const { return ZoneCellIter<TenuredCell>::get<GCType>(); }
|
|
|
|
operator GCType*() const { return get(); }
|
|
|
|
GCType* operator ->() const { return get(); }
|
2016-03-30 11:33:55 +00:00
|
|
|
};
|
|
|
|
|
2016-10-14 08:43:30 +00:00
|
|
|
class GrayObjectIter : public ZoneCellIter<TenuredCell> {
|
|
|
|
public:
|
|
|
|
explicit GrayObjectIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter<TenuredCell>() {
|
|
|
|
initForTenuredIteration(zone, kind);
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject* get() const { return ZoneCellIter<TenuredCell>::get<JSObject>(); }
|
|
|
|
operator JSObject*() const { return get(); }
|
|
|
|
JSObject* operator ->() const { return get(); }
|
|
|
|
};
|
|
|
|
|
2013-03-17 03:36:37 +00:00
|
|
|
class GCZonesIter
|
2012-08-09 18:16:33 +00:00
|
|
|
{
|
2012-04-03 00:02:25 +00:00
|
|
|
private:
|
2013-03-17 03:36:37 +00:00
|
|
|
ZonesIter zone;
|
2012-04-03 00:02:25 +00:00
|
|
|
|
|
|
|
public:
|
2016-02-29 16:22:00 +00:00
|
|
|
explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms) : zone(rt, selector) {
|
2017-02-02 19:12:43 +00:00
|
|
|
MOZ_ASSERT(JS::CurrentThreadIsHeapBusy());
|
2017-01-20 12:09:07 +00:00
|
|
|
if (!zone->isCollectingFromAnyThread())
|
2012-04-03 00:02:25 +00:00
|
|
|
next();
|
|
|
|
}
|
|
|
|
|
2013-03-17 03:36:37 +00:00
|
|
|
bool done() const { return zone.done(); }
|
2012-04-03 00:02:25 +00:00
|
|
|
|
|
|
|
void next() {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2012-04-03 00:02:25 +00:00
|
|
|
do {
|
2013-03-17 03:36:37 +00:00
|
|
|
zone.next();
|
2014-11-18 09:59:14 +00:00
|
|
|
} while (!zone.done() && !zone->isCollectingFromAnyThread());
|
2012-04-03 00:02:25 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 22:22:11 +00:00
|
|
|
JS::Zone* get() const {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2013-03-17 03:36:37 +00:00
|
|
|
return zone;
|
2012-04-03 00:02:25 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 22:22:11 +00:00
|
|
|
operator JS::Zone*() const { return get(); }
|
|
|
|
JS::Zone* operator->() const { return get(); }
|
2012-04-03 00:02:25 +00:00
|
|
|
};
|
|
|
|
|
2013-03-17 03:36:37 +00:00
|
|
|
typedef CompartmentsIterT<GCZonesIter> GCCompartmentsIter;
|
2013-01-27 21:51:40 +00:00
|
|
|
|
2017-03-22 17:30:50 +00:00
|
|
|
/* Iterates over all zones in the current sweep group. */
|
|
|
|
class GCSweepGroupIter {
|
2012-11-16 15:34:22 +00:00
|
|
|
private:
|
2015-03-28 22:22:11 +00:00
|
|
|
JS::Zone* current;
|
2012-11-16 15:34:22 +00:00
|
|
|
|
|
|
|
public:
|
2017-03-22 17:30:50 +00:00
|
|
|
explicit GCSweepGroupIter(JSRuntime* rt) {
|
2016-11-04 17:32:36 +00:00
|
|
|
MOZ_ASSERT(CurrentThreadIsPerformingGC());
|
2017-03-22 17:30:50 +00:00
|
|
|
current = rt->gc.getCurrentSweepGroup();
|
2012-11-16 15:34:22 +00:00
|
|
|
}
|
|
|
|
|
2012-12-04 19:31:41 +00:00
|
|
|
bool done() const { return !current; }
|
2012-11-16 15:34:22 +00:00
|
|
|
|
|
|
|
void next() {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2012-12-04 19:31:41 +00:00
|
|
|
current = current->nextNodeInGroup();
|
2012-11-16 15:34:22 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 22:22:11 +00:00
|
|
|
JS::Zone* get() const {
|
2014-10-01 17:17:51 +00:00
|
|
|
MOZ_ASSERT(!done());
|
2012-11-16 15:34:22 +00:00
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
2015-03-28 22:22:11 +00:00
|
|
|
operator JS::Zone*() const { return get(); }
|
|
|
|
JS::Zone* operator->() const { return get(); }
|
2012-11-16 15:34:22 +00:00
|
|
|
};
|
|
|
|
|
2017-03-22 17:30:50 +00:00
|
|
|
typedef CompartmentsIterT<GCSweepGroupIter> GCCompartmentGroupIter;
|
2013-01-27 21:51:40 +00:00
|
|
|
|
2016-03-30 11:33:55 +00:00
|
|
|
inline void
|
|
|
|
RelocationOverlay::forwardTo(Cell* cell)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!isForwarded());
|
|
|
|
// The location of magic_ is important because it must never be valid to see
|
|
|
|
// the value Relocated there in a GC thing that has not been moved.
|
|
|
|
static_assert(offsetof(RelocationOverlay, magic_) == offsetof(JSObject, group_) &&
|
|
|
|
offsetof(RelocationOverlay, magic_) == offsetof(js::Shape, base_) &&
|
|
|
|
offsetof(RelocationOverlay, magic_) == offsetof(JSString, d.u1.flags),
|
|
|
|
"RelocationOverlay::magic_ is in the wrong location");
|
|
|
|
magic_ = Relocated;
|
2016-04-06 21:44:10 +00:00
|
|
|
newLocation_ = cell;
|
2016-03-30 11:33:55 +00:00
|
|
|
}
|
|
|
|
|
2017-03-27 09:38:29 +00:00
|
|
|
template <typename T>
|
|
|
|
struct MightBeForwarded
|
|
|
|
{
|
|
|
|
static_assert(mozilla::IsBaseOf<Cell, T>::value,
|
|
|
|
"T must derive from Cell");
|
|
|
|
static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
|
|
|
|
"T must not be Cell or TenuredCell");
|
|
|
|
|
|
|
|
static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
|
|
|
|
mozilla::IsBaseOf<Shape, T>::value ||
|
|
|
|
mozilla::IsBaseOf<BaseShape, T>::value ||
|
|
|
|
mozilla::IsBaseOf<JSString, T>::value ||
|
|
|
|
mozilla::IsBaseOf<JSScript, T>::value ||
|
|
|
|
mozilla::IsBaseOf<js::LazyScript, T>::value ||
|
|
|
|
mozilla::IsBaseOf<js::Scope, T>::value ||
|
|
|
|
mozilla::IsBaseOf<js::RegExpShared, T>::value;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline bool
|
|
|
|
IsForwarded(T* t)
|
|
|
|
{
|
|
|
|
RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
|
|
|
|
if (!MightBeForwarded<T>::value) {
|
|
|
|
MOZ_ASSERT(!overlay->isForwarded());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return overlay->isForwarded();
|
|
|
|
}
|
|
|
|
|
|
|
|
struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
|
|
|
|
template <typename T> bool operator()(T* t) { return IsForwarded(t); }
|
|
|
|
};
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
IsForwarded(const JS::Value& value)
|
|
|
|
{
|
|
|
|
return DispatchTyped(IsForwardedFunctor(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline T*
|
|
|
|
Forwarded(T* t)
|
|
|
|
{
|
|
|
|
RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
|
|
|
|
MOZ_ASSERT(overlay->isForwarded());
|
|
|
|
return reinterpret_cast<T*>(overlay->forwardingAddress());
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
|
|
|
|
template <typename T> inline Value operator()(T* t) {
|
|
|
|
return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
inline Value
|
|
|
|
Forwarded(const JS::Value& value)
|
|
|
|
{
|
|
|
|
return DispatchTyped(ForwardedFunctor(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline T
|
|
|
|
MaybeForwarded(T t)
|
|
|
|
{
|
|
|
|
if (IsForwarded(t))
|
|
|
|
t = Forwarded(t);
|
|
|
|
MakeAccessibleAfterMovingGC(t);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef JSGC_HASH_TABLE_CHECKS
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline bool
|
|
|
|
IsGCThingValidAfterMovingGC(T* t)
|
|
|
|
{
|
|
|
|
return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline void
|
|
|
|
CheckGCThingAfterMovingGC(T* t)
|
|
|
|
{
|
|
|
|
if (t)
|
|
|
|
MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline void
|
|
|
|
CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
|
|
|
|
{
|
|
|
|
CheckGCThingAfterMovingGC(t.unbarrieredGet());
|
|
|
|
}
|
|
|
|
|
|
|
|
struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
|
|
|
|
template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
|
|
|
|
};
|
|
|
|
|
|
|
|
inline void
|
|
|
|
CheckValueAfterMovingGC(const JS::Value& value)
|
|
|
|
{
|
|
|
|
DispatchTyped(CheckValueAfterMovingGCFunctor(), value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // JSGC_HASH_TABLE_CHECKS
|
|
|
|
|
2011-11-18 22:40:14 +00:00
|
|
|
} /* namespace gc */
|
2015-02-27 15:08:15 +00:00
|
|
|
} /* namespace js */
|
|
|
|
|
2013-06-20 00:59:46 +00:00
|
|
|
#endif /* jsgcinlines_h */
|