/* -*- 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/. */ // GC Policy Mechanism // GCPolicy controls how the GC interacts with a given type for functionality // that is used by generic code. It is implemented for: // // - direct pointers to GC things, e.g. JSObject* or JSString* // // - tagged and/or optional pointers to GC things, e.g. JS::Value or jsid // // - structures containing GC pointers, e.g. JS::PropertyDescriptor // // - C++ container types, e.g. GCHashMap // // The GCPolicy for type |T| provides at a minimum: // // static void trace(JSTracer, T* tp, const char* name) // // Trace the edge |*tp|, calling the edge |name|. Generic containers // like GCHashMap and GCHashSet use this method to trace their children. // // static bool traceWeak(T* tp) // // Update any GC edges if their target has been moved. Remove or clear any // edges to GC things that are going to be collected by an incremental // GC. Return false if this edge itself should be removed. // // For GC thing pointers, this will clear the edge and return false if the // target is going to be collected. In general, for structures this should // call |traceWeak| on internal GC edges and return whether the result was // true for all of them. // // Containers can use this to remove entries containing GC things that are // going to be collected (e.g. GCVector). // // static bool isValid(const T& t) // // Check that |t| is valid and is not corrupt in some way. The built-in GC // types do some memory layout checks. This is for assertions only; it is ok // to always return true. // // The GCPolicy may also provide: // // static bool needsSweep(const T* tp) // // Return whether this edge should be removed, like a version of |traceWeak| // with the sense of the return value reversed. // // The argument is const and this does not update any moved GC pointers, so // should not be called when this is a possibility. // // This is used internally for incremental barriers on WeakCache hash // tables. // // The default GCPolicy assumes that T has a default constructor and |trace| // and |traceWeak| methods, and forwards to them. GCPolicy has appropriate // specializations for pointers to GC things and pointer-like types like // JS::Heap and mozilla::UniquePtr. // // There are some stock structs your specializations can inherit from. // IgnoreGCPolicy does nothing. StructGCPolicy forwards the methods to the // referent type T. #ifndef GCPolicyAPI_h #define GCPolicyAPI_h #include "mozilla/Maybe.h" #include "mozilla/UniquePtr.h" #include #include "js/GCTypeMacros.h" // JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE #include "js/TraceKind.h" #include "js/TracingAPI.h" #include "js/TypeDecls.h" namespace JS { // Defines a policy for container types with non-GC, i.e. C storage. This // policy dispatches to the underlying struct for GC interactions. Note that // currently a type can define only the subset of the methods (trace and/or // traceWeak) if it is never used in a context that requires the other. template struct StructGCPolicy { static_assert(!std::is_pointer_v, "Pointer type not allowed for StructGCPolicy"); static void trace(JSTracer* trc, T* tp, const char* name) { tp->trace(trc); } static bool traceWeak(JSTracer* trc, T* tp) { return tp->traceWeak(trc); } static bool needsSweep(JSTracer* trc, const T* tp) { return tp->needsSweep(trc); } static bool isValid(const T& tp) { return true; } }; // The default GC policy attempts to defer to methods on the underlying type. // Most C++ structures that contain a default constructor, a trace function and // a sweep function will work out of the box with Rooted, Handle, GCVector, // and GCHash{Set,Map}. template struct GCPolicy : public StructGCPolicy {}; // This policy ignores any GC interaction, e.g. for non-GC types. template struct IgnoreGCPolicy { static void trace(JSTracer* trc, T* t, const char* name) {} static bool traceWeak(JSTracer*, T* v) { return true; } static bool needsSweep(JSTracer* trc, const T* v) { return false; } static bool isValid(const T& v) { return true; } }; template <> struct GCPolicy : public IgnoreGCPolicy {}; template <> struct GCPolicy : public IgnoreGCPolicy {}; template <> struct GCPolicy : public IgnoreGCPolicy {}; template struct GCPointerPolicy { static_assert(std::is_pointer_v, "Non-pointer type not allowed for GCPointerPolicy"); static void trace(JSTracer* trc, T* vp, const char* name) { // This should only be called as part of root marking since that's the only // time we should trace unbarriered GC thing pointers. This will assert if // called at other times. TraceRoot(trc, vp, name); } static bool isTenured(T v) { return !v || !js::gc::IsInsideNursery(v); } static bool isValid(T v) { return js::gc::IsCellPointerValidOrNull(v); } }; #define EXPAND_SPECIALIZE_GCPOLICY(Type) \ template <> \ struct GCPolicy : public GCPointerPolicy {}; \ template <> \ struct GCPolicy : public GCPointerPolicy {}; JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(EXPAND_SPECIALIZE_GCPOLICY) #undef EXPAND_SPECIALIZE_GCPOLICY template struct NonGCPointerPolicy { static void trace(JSTracer* trc, T* vp, const char* name) { if (*vp) { (*vp)->trace(trc); } } static bool traceWeak(JSTracer* trc, T* vp) { return !*vp || (*vp)->traceWeak(trc); } static bool isValid(T v) { return true; } }; template struct GCPolicy> { static void trace(JSTracer* trc, JS::Heap* thingp, const char* name) { TraceEdge(trc, thingp, name); } static bool traceWeak(JSTracer* trc, JS::Heap* thingp) { return !*thingp || js::gc::TraceWeakEdge(trc, thingp); } static bool needsSweep(JSTracer* trc, const JS::Heap* thingp) { T* thing = const_cast(thingp->unsafeAddress()); return thing && js::gc::EdgeNeedsSweepUnbarrieredSlow(thing); } }; // GCPolicy> forwards the contained pointer to GCPolicy. template struct GCPolicy> { static void trace(JSTracer* trc, mozilla::UniquePtr* tp, const char* name) { if (tp->get()) { GCPolicy::trace(trc, tp->get(), name); } } static bool traceWeak(JSTracer* trc, mozilla::UniquePtr* tp) { return !tp->get() || GCPolicy::traceWeak(trc, tp->get()); } static bool needsSweep(JSTracer* trc, const mozilla::UniquePtr* tp) { return tp->get() && GCPolicy::needsSweep(trc, tp->get()); } static bool isValid(const mozilla::UniquePtr& t) { return !t.get() || GCPolicy::isValid(*t.get()); } }; template <> struct GCPolicy : public IgnoreGCPolicy {}; // GCPolicy> forwards tracing/sweeping to GCPolicy if // the Maybe is filled and T* can be traced via GCPolicy. template struct GCPolicy> { static void trace(JSTracer* trc, mozilla::Maybe* tp, const char* name) { if (tp->isSome()) { GCPolicy::trace(trc, tp->ptr(), name); } } static bool traceWeak(JSTracer* trc, mozilla::Maybe* tp) { return tp->isNothing() || GCPolicy::traceWeak(trc, tp->ptr()); } static bool needsSweep(JSTracer* trc, const mozilla::Maybe* tp) { return tp->isSome() && GCPolicy::needsSweep(trc, tp->ptr()); } static bool isValid(const mozilla::Maybe& t) { return t.isNothing() || GCPolicy::isValid(t.ref()); } }; template struct GCPolicy> { static void trace(JSTracer* trc, std::pair* tp, const char* name) { GCPolicy::trace(trc, &tp->first, name); GCPolicy::trace(trc, &tp->second, name); } static bool traceWeak(JSTracer* trc, std::pair* tp) { return GCPolicy::traceWeak(trc, &tp->first) && GCPolicy::traceWeak(trc, &tp->second); } static bool needsSweep(JSTracer* trc, const std::pair* tp) { return GCPolicy::needsSweep(trc, &tp->first) || GCPolicy::needsSweep(trc, &tp->second); } static bool isValid(const std::pair& t) { return GCPolicy::isValid(t.first) && GCPolicy::isValid(t.second); } }; template <> struct GCPolicy; // see Realm.h template <> struct GCPolicy : public IgnoreGCPolicy {}; template struct GCPolicy> { static void trace(JSTracer* trc, mozilla::Result* tp, const char* name) { if (tp->isOk()) { V tmp = tp->unwrap(); JS::GCPolicy::trace(trc, &tmp, "Result value"); tp->updateAfterTracing(std::move(tmp)); } if (tp->isErr()) { E tmp = tp->unwrapErr(); JS::GCPolicy::trace(trc, &tmp, "Result error"); tp->updateErrorAfterTracing(std::move(tmp)); } } static bool isValid(const mozilla::Result& t) { return true; } }; template struct GCPolicy> { using T = std::tuple; static void trace(JSTracer* trc, T* tp, const char* name) { traceFieldsFrom<0>(trc, *tp, name); } static bool isValid(const T& t) { return areFieldsValidFrom<0>(t); } private: template static void traceFieldsFrom(JSTracer* trc, T& tuple, const char* name) { if constexpr (N != std::tuple_size_v) { using F = std::tuple_element_t; GCPolicy::trace(trc, &std::get(tuple), name); traceFieldsFrom(trc, tuple, name); } } template static bool areFieldsValidFrom(const T& tuple) { if constexpr (N != std::tuple_size_v) { using F = std::tuple_element_t; return GCPolicy::isValid(std::get(tuple)) && areFieldsValidFrom(tuple); } return true; } }; } // namespace JS #endif // GCPolicyAPI_h