mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
ab644b087f
--HG-- extra : rebase_source : 075f0747c9256fee67925853b501b7a3549cebba
463 lines
16 KiB
C++
463 lines
16 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/. */
|
|
|
|
#ifndef js_TracingAPI_h
|
|
#define js_TracingAPI_h
|
|
|
|
#include "js/AllocPolicy.h"
|
|
#include "js/HashTable.h"
|
|
#include "js/HeapAPI.h"
|
|
#include "js/TraceKind.h"
|
|
|
|
class JS_PUBLIC_API(JSTracer);
|
|
|
|
namespace JS {
|
|
class JS_PUBLIC_API(CallbackTracer);
|
|
template <typename T> class Heap;
|
|
template <typename T> class TenuredHeap;
|
|
|
|
/** Returns a static string equivalent of |kind|. */
|
|
JS_FRIEND_API(const char*)
|
|
GCTraceKindToAscii(JS::TraceKind kind);
|
|
|
|
} // namespace JS
|
|
|
|
enum WeakMapTraceKind {
|
|
/**
|
|
* Do not trace into weak map keys or values during traversal. Users must
|
|
* handle weak maps manually.
|
|
*/
|
|
DoNotTraceWeakMaps,
|
|
|
|
/**
|
|
* Do true ephemeron marking with a weak key lookup marking phase. This is
|
|
* the default for GCMarker.
|
|
*/
|
|
ExpandWeakMaps,
|
|
|
|
/**
|
|
* Trace through to all values, irrespective of whether the keys are live
|
|
* or not. Used for non-marking tracers.
|
|
*/
|
|
TraceWeakMapValues,
|
|
|
|
/**
|
|
* Trace through to all keys and values, irrespective of whether the keys
|
|
* are live or not. Used for non-marking tracers.
|
|
*/
|
|
TraceWeakMapKeysValues
|
|
};
|
|
|
|
class JS_PUBLIC_API(JSTracer)
|
|
{
|
|
public:
|
|
// Return the runtime set on the tracer.
|
|
JSRuntime* runtime() const { return runtime_; }
|
|
|
|
// Return the weak map tracing behavior currently set on this tracer.
|
|
WeakMapTraceKind weakMapAction() const { return weakMapAction_; }
|
|
|
|
enum class TracerKindTag {
|
|
// Marking path: a tracer used only for marking liveness of cells, not
|
|
// for moving them. The kind will transition to WeakMarking after
|
|
// everything reachable by regular edges has been marked.
|
|
Marking,
|
|
|
|
// Same as Marking, except we have now moved on to the "weak marking
|
|
// phase", in which every marked obj/script is immediately looked up to
|
|
// see if it is a weak map key (and therefore might require marking its
|
|
// weak map value).
|
|
WeakMarking,
|
|
|
|
// A tracer that traverses the graph for the purposes of moving objects
|
|
// from the nursery to the tenured area.
|
|
Tenuring,
|
|
|
|
// General-purpose traversal that invokes a callback on each cell.
|
|
// Traversing children is the responsibility of the callback.
|
|
Callback
|
|
};
|
|
bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; }
|
|
bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; }
|
|
bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
|
|
bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
|
|
inline JS::CallbackTracer* asCallbackTracer();
|
|
bool traceWeakEdges() const { return traceWeakEdges_; }
|
|
bool canSkipJsids() const { return canSkipJsids_; }
|
|
#ifdef DEBUG
|
|
bool checkEdges() { return checkEdges_; }
|
|
#endif
|
|
|
|
// Get the current GC number. Only call this method if |isMarkingTracer()|
|
|
// is true.
|
|
uint32_t gcNumberForMarking() const;
|
|
|
|
protected:
|
|
JSTracer(JSRuntime* rt, TracerKindTag tag,
|
|
WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
|
|
: runtime_(rt)
|
|
, weakMapAction_(weakTraceKind)
|
|
#ifdef DEBUG
|
|
, checkEdges_(true)
|
|
#endif
|
|
, tag_(tag)
|
|
, traceWeakEdges_(true)
|
|
, canSkipJsids_(false)
|
|
{}
|
|
|
|
#ifdef DEBUG
|
|
// Set whether to check edges are valid in debug builds.
|
|
void setCheckEdges(bool check) {
|
|
checkEdges_ = check;
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
JSRuntime* runtime_;
|
|
WeakMapTraceKind weakMapAction_;
|
|
#ifdef DEBUG
|
|
bool checkEdges_;
|
|
#endif
|
|
|
|
protected:
|
|
TracerKindTag tag_;
|
|
bool traceWeakEdges_;
|
|
bool canSkipJsids_;
|
|
};
|
|
|
|
namespace JS {
|
|
|
|
class AutoTracingName;
|
|
class AutoTracingIndex;
|
|
class AutoTracingCallback;
|
|
|
|
class JS_PUBLIC_API(CallbackTracer) : public JSTracer
|
|
{
|
|
public:
|
|
CallbackTracer(JSRuntime* rt, WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
|
|
: JSTracer(rt, JSTracer::TracerKindTag::Callback, weakTraceKind),
|
|
contextName_(nullptr), contextIndex_(InvalidIndex), contextFunctor_(nullptr)
|
|
{}
|
|
CallbackTracer(JSContext* cx, WeakMapTraceKind weakTraceKind = TraceWeakMapValues);
|
|
|
|
// Override these methods to receive notification when an edge is visited
|
|
// with the type contained in the callback. The default implementation
|
|
// dispatches to the fully-generic onChild implementation, so for cases that
|
|
// do not care about boxing overhead and do not need the actual edges,
|
|
// just override the generic onChild.
|
|
virtual void onObjectEdge(JSObject** objp) { onChild(JS::GCCellPtr(*objp)); }
|
|
virtual void onStringEdge(JSString** strp) { onChild(JS::GCCellPtr(*strp)); }
|
|
virtual void onSymbolEdge(JS::Symbol** symp) { onChild(JS::GCCellPtr(*symp)); }
|
|
#ifdef ENABLE_BIGINT
|
|
virtual void onBigIntEdge(JS::BigInt** bip) { onChild(JS::GCCellPtr(*bip)); }
|
|
#endif
|
|
virtual void onScriptEdge(JSScript** scriptp) { onChild(JS::GCCellPtr(*scriptp)); }
|
|
virtual void onShapeEdge(js::Shape** shapep) {
|
|
onChild(JS::GCCellPtr(*shapep, JS::TraceKind::Shape));
|
|
}
|
|
virtual void onObjectGroupEdge(js::ObjectGroup** groupp) {
|
|
onChild(JS::GCCellPtr(*groupp, JS::TraceKind::ObjectGroup));
|
|
}
|
|
virtual void onBaseShapeEdge(js::BaseShape** basep) {
|
|
onChild(JS::GCCellPtr(*basep, JS::TraceKind::BaseShape));
|
|
}
|
|
virtual void onJitCodeEdge(js::jit::JitCode** codep) {
|
|
onChild(JS::GCCellPtr(*codep, JS::TraceKind::JitCode));
|
|
}
|
|
virtual void onLazyScriptEdge(js::LazyScript** lazyp) {
|
|
onChild(JS::GCCellPtr(*lazyp, JS::TraceKind::LazyScript));
|
|
}
|
|
virtual void onScopeEdge(js::Scope** scopep) {
|
|
onChild(JS::GCCellPtr(*scopep, JS::TraceKind::Scope));
|
|
}
|
|
virtual void onRegExpSharedEdge(js::RegExpShared** sharedp) {
|
|
onChild(JS::GCCellPtr(*sharedp, JS::TraceKind::RegExpShared));
|
|
}
|
|
|
|
// Override this method to receive notification when a node in the GC
|
|
// heap graph is visited.
|
|
virtual void onChild(const JS::GCCellPtr& thing) = 0;
|
|
|
|
// Access to the tracing context:
|
|
// When tracing with a JS::CallbackTracer, we invoke the callback with the
|
|
// edge location and the type of target. This is useful for operating on
|
|
// the edge in the abstract or on the target thing, satisfying most common
|
|
// use cases. However, some tracers need additional detail about the
|
|
// specific edge that is being traced in order to be useful. Unfortunately,
|
|
// the raw pointer to the edge that we provide is not enough information to
|
|
// infer much of anything useful about that edge.
|
|
//
|
|
// In order to better support use cases that care in particular about edges
|
|
// -- as opposed to the target thing -- tracing implementations are
|
|
// responsible for providing extra context information about each edge they
|
|
// trace, as it is traced. This contains, at a minimum, an edge name and,
|
|
// when tracing an array, the index. Further specialization can be achived
|
|
// (with some complexity), by associating a functor with the tracer so
|
|
// that, when requested, the user can generate totally custom edge
|
|
// descriptions.
|
|
|
|
// Returns the current edge's name. It is only valid to call this when
|
|
// inside the trace callback, however, the edge name will always be set.
|
|
const char* contextName() const { MOZ_ASSERT(contextName_); return contextName_; }
|
|
|
|
// Returns the current edge's index, if marked as part of an array of edges.
|
|
// This must be called only inside the trace callback. When not tracing an
|
|
// array, the value will be InvalidIndex.
|
|
const static size_t InvalidIndex = size_t(-1);
|
|
size_t contextIndex() const { return contextIndex_; }
|
|
|
|
// Build a description of this edge in the heap graph. This call may invoke
|
|
// the context functor, if set, which may inspect arbitrary areas of the
|
|
// heap. On the other hand, the description provided by this method may be
|
|
// substantially more accurate and useful than those provided by only the
|
|
// contextName and contextIndex.
|
|
void getTracingEdgeName(char* buffer, size_t bufferSize);
|
|
|
|
// The trace implementation may associate a callback with one or more edges
|
|
// using AutoTracingDetails. This functor is called by getTracingEdgeName
|
|
// and is responsible for providing a textual representation of the
|
|
// currently being traced edge. The callback has access to the full heap,
|
|
// including the currently set tracing context.
|
|
class ContextFunctor {
|
|
public:
|
|
virtual void operator()(CallbackTracer* trc, char* buf, size_t bufsize) = 0;
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
enum class TracerKind {
|
|
DoNotCare,
|
|
Moving,
|
|
GrayBuffering,
|
|
VerifyTraceProtoAndIface,
|
|
ClearEdges,
|
|
UnmarkGray
|
|
};
|
|
virtual TracerKind getTracerKind() const { return TracerKind::DoNotCare; }
|
|
#endif
|
|
|
|
// In C++, overriding a method hides all methods in the base class with
|
|
// that name, not just methods with that signature. Thus, the typed edge
|
|
// methods have to have distinct names to allow us to override them
|
|
// individually, which is freqently useful if, for example, we only want to
|
|
// process only one type of edge.
|
|
void dispatchToOnEdge(JSObject** objp) { onObjectEdge(objp); }
|
|
void dispatchToOnEdge(JSString** strp) { onStringEdge(strp); }
|
|
void dispatchToOnEdge(JS::Symbol** symp) { onSymbolEdge(symp); }
|
|
#ifdef ENABLE_BIGINT
|
|
void dispatchToOnEdge(JS::BigInt** bip) { onBigIntEdge(bip); }
|
|
#endif
|
|
void dispatchToOnEdge(JSScript** scriptp) { onScriptEdge(scriptp); }
|
|
void dispatchToOnEdge(js::Shape** shapep) { onShapeEdge(shapep); }
|
|
void dispatchToOnEdge(js::ObjectGroup** groupp) { onObjectGroupEdge(groupp); }
|
|
void dispatchToOnEdge(js::BaseShape** basep) { onBaseShapeEdge(basep); }
|
|
void dispatchToOnEdge(js::jit::JitCode** codep) { onJitCodeEdge(codep); }
|
|
void dispatchToOnEdge(js::LazyScript** lazyp) { onLazyScriptEdge(lazyp); }
|
|
void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); }
|
|
void dispatchToOnEdge(js::RegExpShared** sharedp) { onRegExpSharedEdge(sharedp); }
|
|
|
|
protected:
|
|
void setTraceWeakEdges(bool value) {
|
|
traceWeakEdges_ = value;
|
|
}
|
|
|
|
// If this is set to false, then the tracer will skip some jsids
|
|
// to improve performance. This is needed for the cycle collector.
|
|
void setCanSkipJsids(bool value) {
|
|
canSkipJsids_ = value;
|
|
}
|
|
|
|
private:
|
|
friend class AutoTracingName;
|
|
const char* contextName_;
|
|
|
|
friend class AutoTracingIndex;
|
|
size_t contextIndex_;
|
|
|
|
friend class AutoTracingDetails;
|
|
ContextFunctor* contextFunctor_;
|
|
};
|
|
|
|
// Set the name portion of the tracer's context for the current edge.
|
|
class MOZ_RAII AutoTracingName
|
|
{
|
|
CallbackTracer* trc_;
|
|
const char* prior_;
|
|
|
|
public:
|
|
AutoTracingName(CallbackTracer* trc, const char* name) : trc_(trc), prior_(trc->contextName_) {
|
|
MOZ_ASSERT(name);
|
|
trc->contextName_ = name;
|
|
}
|
|
~AutoTracingName() {
|
|
MOZ_ASSERT(trc_->contextName_);
|
|
trc_->contextName_ = prior_;
|
|
}
|
|
};
|
|
|
|
// Set the index portion of the tracer's context for the current range.
|
|
class MOZ_RAII AutoTracingIndex
|
|
{
|
|
CallbackTracer* trc_;
|
|
|
|
public:
|
|
explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(nullptr) {
|
|
if (trc->isCallbackTracer()) {
|
|
trc_ = trc->asCallbackTracer();
|
|
MOZ_ASSERT(trc_->contextIndex_ == CallbackTracer::InvalidIndex);
|
|
trc_->contextIndex_ = initial;
|
|
}
|
|
}
|
|
~AutoTracingIndex() {
|
|
if (trc_) {
|
|
MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex);
|
|
trc_->contextIndex_ = CallbackTracer::InvalidIndex;
|
|
}
|
|
}
|
|
|
|
void operator++() {
|
|
if (trc_) {
|
|
MOZ_ASSERT(trc_->contextIndex_ != CallbackTracer::InvalidIndex);
|
|
++trc_->contextIndex_;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Set a context callback for the trace callback to use, if it needs a detailed
|
|
// edge description.
|
|
class MOZ_RAII AutoTracingDetails
|
|
{
|
|
CallbackTracer* trc_;
|
|
|
|
public:
|
|
AutoTracingDetails(JSTracer* trc, CallbackTracer::ContextFunctor& func) : trc_(nullptr) {
|
|
if (trc->isCallbackTracer()) {
|
|
trc_ = trc->asCallbackTracer();
|
|
MOZ_ASSERT(trc_->contextFunctor_ == nullptr);
|
|
trc_->contextFunctor_ = &func;
|
|
}
|
|
}
|
|
~AutoTracingDetails() {
|
|
if (trc_) {
|
|
MOZ_ASSERT(trc_->contextFunctor_);
|
|
trc_->contextFunctor_ = nullptr;
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace JS
|
|
|
|
JS::CallbackTracer*
|
|
JSTracer::asCallbackTracer()
|
|
{
|
|
MOZ_ASSERT(isCallbackTracer());
|
|
return static_cast<JS::CallbackTracer*>(this);
|
|
}
|
|
|
|
namespace js {
|
|
namespace gc {
|
|
template <typename T>
|
|
JS_PUBLIC_API(void) TraceExternalEdge(JSTracer* trc, T* thingp, const char* name);
|
|
} // namespace gc
|
|
} // namespace js
|
|
|
|
namespace JS {
|
|
|
|
// The JS::TraceEdge family of functions traces the given GC thing reference.
|
|
// This performs the tracing action configured on the given JSTracer: typically
|
|
// calling the JSTracer::callback or marking the thing as live.
|
|
//
|
|
// The argument to JS::TraceEdge is an in-out param: when the function returns,
|
|
// the garbage collector might have moved the GC thing. In this case, the
|
|
// reference passed to JS::TraceEdge will be updated to the thing's new
|
|
// location. Callers of this method are responsible for updating any state that
|
|
// is dependent on the object's address. For example, if the object's address
|
|
// is used as a key in a hashtable, then the object must be removed and
|
|
// re-inserted with the correct hash.
|
|
//
|
|
// Note that while |edgep| must never be null, it is fine for |*edgep| to be
|
|
// nullptr.
|
|
|
|
template <typename T>
|
|
inline void
|
|
TraceEdge(JSTracer* trc, JS::Heap<T>* thingp, const char* name)
|
|
{
|
|
MOZ_ASSERT(thingp);
|
|
if (*thingp) {
|
|
js::gc::TraceExternalEdge(trc, thingp->unsafeGet(), name);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
inline void
|
|
TraceEdge(JSTracer* trc, JS::TenuredHeap<T>* thingp, const char* name)
|
|
{
|
|
MOZ_ASSERT(thingp);
|
|
if (T ptr = thingp->unbarrieredGetPtr()) {
|
|
js::gc::TraceExternalEdge(trc, &ptr, name);
|
|
thingp->setPtr(ptr);
|
|
}
|
|
}
|
|
|
|
// Edges that are always traced as part of root marking do not require
|
|
// incremental barriers. This function allows for marking non-barriered
|
|
// pointers, but asserts that this happens during root marking.
|
|
//
|
|
// Note that while |edgep| must never be null, it is fine for |*edgep| to be
|
|
// nullptr.
|
|
template <typename T>
|
|
extern JS_PUBLIC_API(void)
|
|
UnsafeTraceRoot(JSTracer* trc, T* edgep, const char* name);
|
|
|
|
extern JS_PUBLIC_API(void)
|
|
TraceChildren(JSTracer* trc, GCCellPtr thing);
|
|
|
|
using ZoneSet = js::HashSet<Zone*, js::DefaultHasher<Zone*>, js::SystemAllocPolicy>;
|
|
using CompartmentSet = js::HashSet<JS::Compartment*, js::DefaultHasher<JS::Compartment*>,
|
|
js::SystemAllocPolicy>;
|
|
|
|
/**
|
|
* Trace every value within |compartments| that is wrapped by a
|
|
* cross-compartment wrapper from a compartment that is not an element of
|
|
* |compartments|.
|
|
*/
|
|
extern JS_PUBLIC_API(void)
|
|
TraceIncomingCCWs(JSTracer* trc, const JS::CompartmentSet& compartments);
|
|
|
|
} // namespace JS
|
|
|
|
extern JS_PUBLIC_API(void)
|
|
JS_GetTraceThingInfo(char* buf, size_t bufsize, JSTracer* trc,
|
|
void* thing, JS::TraceKind kind, bool includeDetails);
|
|
|
|
namespace js {
|
|
|
|
// Trace an edge that is not a GC root and is not wrapped in a barriered
|
|
// wrapper for some reason.
|
|
//
|
|
// This method does not check if |*edgep| is non-null before tracing through
|
|
// it, so callers must check any nullable pointer before calling this method.
|
|
template <typename T>
|
|
extern JS_PUBLIC_API(void)
|
|
UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* edgep, const char* name);
|
|
|
|
namespace gc {
|
|
|
|
// Return true if the given edge is not live and is about to be swept.
|
|
template <typename T>
|
|
extern JS_PUBLIC_API(bool)
|
|
EdgeNeedsSweep(JS::Heap<T>* edgep);
|
|
|
|
// Not part of the public API, but declared here so we can use it in GCPolicy
|
|
// which is.
|
|
template <typename T>
|
|
bool
|
|
IsAboutToBeFinalizedUnbarriered(T* thingp);
|
|
|
|
} // namespace gc
|
|
} // namespace js
|
|
|
|
#endif /* js_TracingAPI_h */
|