зеркало из https://github.com/mozilla/gecko-dev.git
528 строки
18 KiB
C++
528 строки
18 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
|
* 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/GCTypeMacros.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_PUBLIC_API const char* GCTraceKindToAscii(JS::TraceKind kind);
|
|
|
|
/** Returns the base size in bytes of the GC thing of kind |kind|. */
|
|
JS_PUBLIC_API size_t GCTraceKindSize(JS::TraceKind kind);
|
|
|
|
// Kinds of JSTracer.
|
|
enum class TracerKind {
|
|
// Marking path: a tracer used only for marking liveness of cells, not
|
|
// for moving them.
|
|
Marking,
|
|
|
|
// Generic tracers: Internal tracers that have a different virtual method
|
|
// called for each edge kind.
|
|
//
|
|
// Order is important. All generic kinds must follow this one.
|
|
Generic,
|
|
|
|
// Specific kinds of generic tracer.
|
|
Tenuring,
|
|
Moving,
|
|
GrayBuffering,
|
|
ClearEdges,
|
|
Sweeping,
|
|
MinorSweeping,
|
|
Barrier,
|
|
|
|
// Callback tracers: General-purpose tracers that have a single virtual
|
|
// method called on every edge.
|
|
//
|
|
// Order is important. All callback kinds must follow this one.
|
|
Callback,
|
|
|
|
// Specific kinds of callback tracer.
|
|
UnmarkGray,
|
|
VerifyTraceProtoAndIface,
|
|
};
|
|
|
|
enum class WeakMapTraceAction {
|
|
/**
|
|
* Do not trace into weak map keys or values during traversal. Users must
|
|
* handle weak maps manually.
|
|
*/
|
|
Skip,
|
|
|
|
/**
|
|
* Do true ephemeron marking with a weak key lookup marking phase. This is
|
|
* the default for GCMarker.
|
|
*/
|
|
Expand,
|
|
|
|
/**
|
|
* Trace through to all values, irrespective of whether the keys are live
|
|
* or not. Used for non-marking tracers.
|
|
*/
|
|
TraceValues,
|
|
|
|
/**
|
|
* Trace through to all keys and values, irrespective of whether the keys
|
|
* are live or not. Used for non-marking tracers.
|
|
*/
|
|
TraceKeysAndValues
|
|
};
|
|
|
|
// Whether a tracer should trace weak edges. GCMarker sets this to Skip.
|
|
enum class WeakEdgeTraceAction { Skip, Trace };
|
|
|
|
// Whether a tracer can skip tracing JS::Ids. This is needed by the cycle
|
|
// collector to skip some Ids for performance reasons. Not all Ids are skipped.
|
|
enum class IdTraceAction { CanSkip, Trace };
|
|
|
|
struct TraceOptions {
|
|
JS::WeakMapTraceAction weakMapAction = WeakMapTraceAction::TraceValues;
|
|
JS::WeakEdgeTraceAction weakEdgeAction = WeakEdgeTraceAction::Trace;
|
|
JS::IdTraceAction idAction = IdTraceAction::Trace;
|
|
|
|
TraceOptions() = default;
|
|
TraceOptions(JS::WeakMapTraceAction weakMapActionArg,
|
|
JS::WeakEdgeTraceAction weakEdgeActionArg,
|
|
JS::IdTraceAction idActionArg = IdTraceAction::Trace)
|
|
: weakMapAction(weakMapActionArg),
|
|
weakEdgeAction(weakEdgeActionArg),
|
|
idAction(idActionArg) {}
|
|
MOZ_IMPLICIT TraceOptions(JS::WeakMapTraceAction weakMapActionArg)
|
|
: weakMapAction(weakMapActionArg) {}
|
|
MOZ_IMPLICIT TraceOptions(JS::WeakEdgeTraceAction weakEdgeActionArg)
|
|
: weakEdgeAction(weakEdgeActionArg) {}
|
|
MOZ_IMPLICIT TraceOptions(JS::IdTraceAction idActionArg)
|
|
: idAction(idActionArg) {}
|
|
};
|
|
|
|
class AutoTracingName;
|
|
class AutoTracingIndex;
|
|
|
|
// Optional context information that can be used to construct human readable
|
|
// descriptions of what is being traced.
|
|
class TracingContext {
|
|
public:
|
|
// 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 achieved (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* name() const {
|
|
MOZ_ASSERT(name_);
|
|
return name_;
|
|
}
|
|
|
|
// 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.
|
|
constexpr static size_t InvalidIndex = size_t(-1);
|
|
size_t index() const { return index_; }
|
|
|
|
// 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
|
|
// name and index.
|
|
void getEdgeName(char* buffer, size_t bufferSize);
|
|
|
|
// The trace implementation may associate a callback with one or more edges
|
|
// using AutoTracingDetails. This functor is called by getEdgeName and
|
|
// is responsible for providing a textual representation of the edge currently
|
|
// being traced. The callback has access to the full heap, including the
|
|
// currently set tracing context.
|
|
class Functor {
|
|
public:
|
|
virtual void operator()(TracingContext* tcx, char* buf, size_t bufsize) = 0;
|
|
};
|
|
|
|
private:
|
|
friend class AutoTracingName;
|
|
const char* name_ = nullptr;
|
|
|
|
friend class AutoTracingIndex;
|
|
size_t index_ = InvalidIndex;
|
|
|
|
friend class AutoTracingDetails;
|
|
Functor* functor_ = nullptr;
|
|
};
|
|
|
|
} // namespace JS
|
|
|
|
namespace js {
|
|
class GenericTracer;
|
|
} // namespace js
|
|
|
|
class JS_PUBLIC_API JSTracer {
|
|
public:
|
|
// Return the runtime set on the tracer.
|
|
JSRuntime* runtime() const { return runtime_; }
|
|
|
|
JS::TracerKind kind() const { return kind_; }
|
|
bool isMarkingTracer() const { return kind_ == JS::TracerKind::Marking; }
|
|
bool isTenuringTracer() const { return kind_ == JS::TracerKind::Tenuring; }
|
|
bool isGenericTracer() const { return kind_ >= JS::TracerKind::Generic; }
|
|
bool isCallbackTracer() const { return kind_ >= JS::TracerKind::Callback; }
|
|
|
|
inline js::GenericTracer* asGenericTracer();
|
|
inline JS::CallbackTracer* asCallbackTracer();
|
|
|
|
JS::WeakMapTraceAction weakMapAction() const {
|
|
return options_.weakMapAction;
|
|
}
|
|
bool traceWeakEdges() const {
|
|
return options_.weakEdgeAction == JS::WeakEdgeTraceAction::Trace;
|
|
}
|
|
bool canSkipJsids() const {
|
|
return options_.idAction == JS::IdTraceAction::CanSkip;
|
|
}
|
|
|
|
JS::TracingContext& context() { return context_; }
|
|
|
|
// Get the current GC number. Only call this method if |isMarkingTracer()|
|
|
// is true.
|
|
uint32_t gcNumberForMarking() const;
|
|
|
|
protected:
|
|
JSTracer(JSRuntime* rt, JS::TracerKind kind,
|
|
JS::TraceOptions options = JS::TraceOptions())
|
|
: runtime_(rt), kind_(kind), options_(options) {}
|
|
|
|
private:
|
|
JSRuntime* const runtime_;
|
|
const JS::TracerKind kind_;
|
|
const JS::TraceOptions options_;
|
|
JS::TracingContext context_;
|
|
};
|
|
|
|
namespace js {
|
|
|
|
class GenericTracer : public JSTracer {
|
|
public:
|
|
GenericTracer(JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Generic,
|
|
JS::TraceOptions options = JS::TraceOptions())
|
|
: JSTracer(rt, kind, options) {
|
|
MOZ_ASSERT(isGenericTracer());
|
|
}
|
|
|
|
// These methods are called when the tracer encounters an edge. Clients should
|
|
// override them to receive notifications when an edge of each type is
|
|
// visited.
|
|
//
|
|
// The caller updates the edge with the return value (if different).
|
|
//
|
|
// 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 one type
|
|
// of edge.
|
|
virtual JSObject* onObjectEdge(JSObject* obj) = 0;
|
|
virtual JSString* onStringEdge(JSString* str) = 0;
|
|
virtual JS::Symbol* onSymbolEdge(JS::Symbol* sym) = 0;
|
|
virtual JS::BigInt* onBigIntEdge(JS::BigInt* bi) = 0;
|
|
virtual js::BaseScript* onScriptEdge(js::BaseScript* script) = 0;
|
|
virtual js::Shape* onShapeEdge(js::Shape* shape) = 0;
|
|
virtual js::RegExpShared* onRegExpSharedEdge(js::RegExpShared* shared) = 0;
|
|
virtual js::GetterSetter* onGetterSetterEdge(js::GetterSetter* gs) = 0;
|
|
virtual js::PropMap* onPropMapEdge(js::PropMap* map) = 0;
|
|
virtual js::BaseShape* onBaseShapeEdge(js::BaseShape* base) = 0;
|
|
virtual js::jit::JitCode* onJitCodeEdge(js::jit::JitCode* code) = 0;
|
|
virtual js::Scope* onScopeEdge(js::Scope* scope) = 0;
|
|
};
|
|
|
|
// A helper class that implements a GenericTracer by calling template method
|
|
// on a derived type for each edge kind.
|
|
template <typename T>
|
|
class GenericTracerImpl : public GenericTracer {
|
|
public:
|
|
GenericTracerImpl(JSRuntime* rt, JS::TracerKind kind,
|
|
JS::TraceOptions options)
|
|
: GenericTracer(rt, kind, options) {}
|
|
|
|
private:
|
|
T* derived() { return static_cast<T*>(this); }
|
|
|
|
JSObject* onObjectEdge(JSObject* obj) override {
|
|
return derived()->onEdge(obj);
|
|
}
|
|
Shape* onShapeEdge(Shape* shape) override { return derived()->onEdge(shape); }
|
|
JSString* onStringEdge(JSString* string) override {
|
|
return derived()->onEdge(string);
|
|
}
|
|
BaseScript* onScriptEdge(BaseScript* script) override {
|
|
return derived()->onEdge(script);
|
|
}
|
|
BaseShape* onBaseShapeEdge(BaseShape* base) override {
|
|
return derived()->onEdge(base);
|
|
}
|
|
GetterSetter* onGetterSetterEdge(GetterSetter* gs) override {
|
|
return derived()->onEdge(gs);
|
|
}
|
|
PropMap* onPropMapEdge(PropMap* map) override {
|
|
return derived()->onEdge(map);
|
|
}
|
|
Scope* onScopeEdge(Scope* scope) override { return derived()->onEdge(scope); }
|
|
RegExpShared* onRegExpSharedEdge(RegExpShared* shared) override {
|
|
return derived()->onEdge(shared);
|
|
}
|
|
JS::BigInt* onBigIntEdge(JS::BigInt* bi) override {
|
|
return derived()->onEdge(bi);
|
|
}
|
|
JS::Symbol* onSymbolEdge(JS::Symbol* sym) override {
|
|
return derived()->onEdge(sym);
|
|
}
|
|
jit::JitCode* onJitCodeEdge(jit::JitCode* jit) override {
|
|
return derived()->onEdge(jit);
|
|
}
|
|
};
|
|
|
|
} // namespace js
|
|
|
|
namespace JS {
|
|
|
|
class JS_PUBLIC_API CallbackTracer
|
|
: public js::GenericTracerImpl<CallbackTracer> {
|
|
public:
|
|
CallbackTracer(JSRuntime* rt, JS::TracerKind kind = JS::TracerKind::Callback,
|
|
JS::TraceOptions options = JS::TraceOptions())
|
|
: GenericTracerImpl(rt, kind, options) {
|
|
MOZ_ASSERT(isCallbackTracer());
|
|
}
|
|
CallbackTracer(JSContext* cx, JS::TracerKind kind = JS::TracerKind::Callback,
|
|
JS::TraceOptions options = JS::TraceOptions());
|
|
|
|
// Override this method to receive notification when a node in the GC
|
|
// heap graph is visited.
|
|
virtual void onChild(const JS::GCCellPtr& thing) = 0;
|
|
|
|
private:
|
|
template <typename T>
|
|
T* onEdge(T* thing) {
|
|
onChild(JS::GCCellPtr(thing));
|
|
return thing;
|
|
}
|
|
friend class js::GenericTracerImpl<CallbackTracer>;
|
|
};
|
|
|
|
// Set the name portion of the tracer's context for the current edge.
|
|
class MOZ_RAII AutoTracingName {
|
|
JSTracer* trc_;
|
|
|
|
public:
|
|
AutoTracingName(JSTracer* trc, const char* name) : trc_(trc) {
|
|
MOZ_ASSERT(name);
|
|
MOZ_ASSERT(!trc_->context().name_);
|
|
trc_->context().name_ = name;
|
|
}
|
|
~AutoTracingName() {
|
|
MOZ_ASSERT(trc_->context().name_);
|
|
trc_->context().name_ = nullptr;
|
|
}
|
|
};
|
|
|
|
// Set the index portion of the tracer's context for the current range.
|
|
class MOZ_RAII AutoTracingIndex {
|
|
JSTracer* trc_;
|
|
|
|
public:
|
|
explicit AutoTracingIndex(JSTracer* trc, size_t initial = 0) : trc_(trc) {
|
|
MOZ_ASSERT(trc_->context().index_ == TracingContext::InvalidIndex);
|
|
trc_->context().index_ = initial;
|
|
}
|
|
~AutoTracingIndex() {
|
|
MOZ_ASSERT(trc_->context().index_ != TracingContext::InvalidIndex);
|
|
trc_->context().index_ = TracingContext::InvalidIndex;
|
|
}
|
|
|
|
void operator++() {
|
|
MOZ_ASSERT(trc_->context().index_ != TracingContext::InvalidIndex);
|
|
++trc_->context().index_;
|
|
}
|
|
};
|
|
|
|
// Set a context callback for the trace callback to use, if it needs a detailed
|
|
// edge description.
|
|
class MOZ_RAII AutoTracingDetails {
|
|
JSTracer* trc_;
|
|
|
|
public:
|
|
AutoTracingDetails(JSTracer* trc, TracingContext::Functor& func) : trc_(trc) {
|
|
MOZ_ASSERT(trc_->context().functor_ == nullptr);
|
|
trc_->context().functor_ = &func;
|
|
}
|
|
~AutoTracingDetails() {
|
|
MOZ_ASSERT(trc_->context().functor_);
|
|
trc_->context().functor_ = nullptr;
|
|
}
|
|
};
|
|
|
|
// Save and clear tracing context when performing nested tracing.
|
|
class MOZ_RAII AutoClearTracingContext {
|
|
JSTracer* trc_;
|
|
TracingContext prev_;
|
|
|
|
public:
|
|
explicit AutoClearTracingContext(JSTracer* trc)
|
|
: trc_(trc), prev_(trc->context()) {
|
|
trc_->context() = TracingContext();
|
|
}
|
|
|
|
~AutoClearTracingContext() { trc_->context() = prev_; }
|
|
};
|
|
|
|
} // namespace JS
|
|
|
|
js::GenericTracer* JSTracer::asGenericTracer() {
|
|
MOZ_ASSERT(isGenericTracer());
|
|
return static_cast<js::GenericTracer*>(this);
|
|
}
|
|
|
|
JS::CallbackTracer* JSTracer::asCallbackTracer() {
|
|
MOZ_ASSERT(isCallbackTracer());
|
|
return static_cast<JS::CallbackTracer*>(this);
|
|
}
|
|
|
|
namespace js {
|
|
|
|
class AbstractGeneratorObject;
|
|
class SavedFrame;
|
|
|
|
namespace gc {
|
|
|
|
#define JS_DECLARE_TRACE_EXTERNAL_EDGE(type) \
|
|
extern JS_PUBLIC_API void TraceExternalEdge(JSTracer* trc, type* thingp, \
|
|
const char* name);
|
|
|
|
// Declare edge-tracing function overloads for public GC pointer types.
|
|
JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE)
|
|
JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_TRACE_EXTERNAL_EDGE)
|
|
|
|
#undef JS_DECLARE_TRACE_EXTERNAL_EDGE
|
|
|
|
} // 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. |JS::UnsafeTraceRoot| overloads allow for marking
|
|
// non-barriered pointers but assert that this happens during root marking.
|
|
//
|
|
// Note that while |edgep| must never be null, it is fine for |*edgep| to be
|
|
// nullptr.
|
|
#define JS_DECLARE_UNSAFE_TRACE_ROOT(type) \
|
|
extern JS_PUBLIC_API void UnsafeTraceRoot(JSTracer* trc, type* edgep, \
|
|
const char* name);
|
|
|
|
// Declare edge-tracing function overloads for public GC pointer types.
|
|
JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_UNSAFE_TRACE_ROOT)
|
|
JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_UNSAFE_TRACE_ROOT)
|
|
|
|
// We also require overloads for these purely-internal types. These overloads
|
|
// ought not be in public headers, and they should use a different name in order
|
|
// to not be *actual* overloads, but for the moment we still declare them here.
|
|
JS_DECLARE_UNSAFE_TRACE_ROOT(js::AbstractGeneratorObject*)
|
|
JS_DECLARE_UNSAFE_TRACE_ROOT(js::SavedFrame*)
|
|
|
|
#undef JS_DECLARE_UNSAFE_TRACE_ROOT
|
|
|
|
extern JS_PUBLIC_API void TraceChildren(JSTracer* trc, GCCellPtr thing);
|
|
|
|
} // namespace JS
|
|
|
|
namespace js {
|
|
|
|
inline bool IsTracerKind(JSTracer* trc, JS::TracerKind kind) {
|
|
return trc->kind() == kind;
|
|
}
|
|
|
|
// 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.
|
|
extern JS_PUBLIC_API void UnsafeTraceManuallyBarrieredEdge(JSTracer* trc,
|
|
JSObject** 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);
|
|
|
|
} // namespace gc
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* Return whether the runtime is currently being destroyed, for use in
|
|
* assertions.
|
|
*/
|
|
extern JS_PUBLIC_API bool RuntimeIsBeingDestroyed();
|
|
#endif
|
|
|
|
} // namespace js
|
|
|
|
#endif /* js_TracingAPI_h */
|