/* -*- 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 gc_Marking_inl_h #define gc_Marking_inl_h #include "gc/Marking.h" #include #include "gc/RelocationOverlay.h" #include "js/Id.h" #include "js/Value.h" #include "vm/StringType.h" #include "vm/TaggedProto.h" #include "gc/Nursery-inl.h" namespace js { namespace gc { // An abstraction to re-wrap any kind of typed pointer back to the tagged // pointer it came from with |TaggedPtr::wrap(sourcePtr)|. template struct TaggedPtr {}; template <> struct TaggedPtr { static JS::Value wrap(JSObject* obj) { if (!obj) { return JS::NullValue(); } #ifdef ENABLE_RECORD_TUPLE if (MaybeForwardedIsExtendedPrimitive(*obj)) { return JS::ExtendedPrimitiveValue(*obj); } #endif return JS::ObjectValue(*obj); } static JS::Value wrap(JSString* str) { return JS::StringValue(str); } static JS::Value wrap(JS::Symbol* sym) { return JS::SymbolValue(sym); } static JS::Value wrap(JS::BigInt* bi) { return JS::BigIntValue(bi); } template static JS::Value wrap(T* priv) { static_assert(std::is_base_of_v, "Type must be a GC thing derived from js::gc::Cell"); return JS::PrivateGCThingValue(priv); } static JS::Value empty() { return JS::UndefinedValue(); } }; template <> struct TaggedPtr { static jsid wrap(JSString* str) { return JS::PropertyKey::NonIntAtom(str); } static jsid wrap(JS::Symbol* sym) { return PropertyKey::Symbol(sym); } static jsid empty() { return JS::PropertyKey::Void(); } }; template <> struct TaggedPtr { static TaggedProto wrap(JSObject* obj) { return TaggedProto(obj); } static TaggedProto empty() { return TaggedProto(); } }; template struct MightBeForwarded { static_assert(std::is_base_of_v); static_assert(!std::is_same_v && !std::is_same_v); #define CAN_FORWARD_KIND_OR(_1, _2, Type, _3, _4, _5, canCompact) \ std::is_base_of_v ? canCompact: // FOR_EACH_ALLOCKIND doesn't cover every possible type: make sure // to default to `true` for unknown types. static constexpr bool value = FOR_EACH_ALLOCKIND(CAN_FORWARD_KIND_OR) true; #undef CAN_FORWARD_KIND_OR }; template inline bool IsForwarded(const T* t) { if (!MightBeForwarded::value) { MOZ_ASSERT(!t->isForwarded()); return false; } return t->isForwarded(); } template inline T* Forwarded(const T* t) { const RelocationOverlay* overlay = RelocationOverlay::fromCell(t); MOZ_ASSERT(overlay->isForwarded()); return reinterpret_cast(overlay->forwardingAddress()); } template inline T MaybeForwarded(T t) { if (IsForwarded(t)) { t = Forwarded(t); } MOZ_ASSERT(!IsForwarded(t)); return t; } inline const JSClass* MaybeForwardedObjectClass(const JSObject* obj) { Shape* shape = MaybeForwarded(obj->shapeMaybeForwarded()); BaseShape* baseShape = MaybeForwarded(shape->base()); return baseShape->clasp(); } template inline bool MaybeForwardedObjectIs(const JSObject* obj) { MOZ_ASSERT(!obj->isForwarded()); return MaybeForwardedObjectClass(obj) == &T::class_; } template inline T& MaybeForwardedObjectAs(JSObject* obj) { MOZ_ASSERT(MaybeForwardedObjectIs(obj)); return *static_cast(obj); } inline RelocationOverlay::RelocationOverlay(Cell* dst) { MOZ_ASSERT(dst->flags() == 0); uintptr_t ptr = uintptr_t(dst); header_.setForwardingAddress(ptr); } /* static */ inline RelocationOverlay* RelocationOverlay::forwardCell(Cell* src, Cell* dst) { MOZ_ASSERT(!src->isForwarded()); MOZ_ASSERT(!dst->isForwarded()); return new (src) RelocationOverlay(dst); } inline bool IsAboutToBeFinalizedDuringMinorSweep(Cell** cellp) { MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting()); if ((*cellp)->isTenured()) { return false; } return !Nursery::getForwardedPointer(cellp); } // Special case pre-write barrier for strings used during rope flattening. This // avoids eager marking of ropes which does not immediately mark the cells if we // hit OOM. This does not traverse ropes and is instead called on every node in // a rope during flattening. inline void PreWriteBarrierDuringFlattening(JSString* str) { MOZ_ASSERT(str); MOZ_ASSERT(!JS::RuntimeHeapIsMajorCollecting()); if (IsInsideNursery(str)) { return; } auto* cell = reinterpret_cast(str); JS::shadow::Zone* zone = cell->shadowZoneFromAnyThread(); if (!zone->needsIncrementalBarrier()) { return; } MOZ_ASSERT(!str->isPermanentAndMayBeShared()); MOZ_ASSERT(CurrentThreadCanAccessRuntime(zone->runtimeFromAnyThread())); PerformIncrementalBarrierDuringFlattening(str); } #ifdef JSGC_HASH_TABLE_CHECKS template inline bool IsGCThingValidAfterMovingGC(T* t) { return !IsInsideNursery(t) && !t->isForwarded(); } template inline void CheckGCThingAfterMovingGC(T* t) { if (t) { MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t)); } } template inline void CheckGCThingAfterMovingGC(const WeakHeapPtr& t) { CheckGCThingAfterMovingGC(t.unbarrieredGet()); } #endif // JSGC_HASH_TABLE_CHECKS } /* namespace gc */ } /* namespace js */ #endif // gc_Marking_inl_h