UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
VVMCell.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#if WITH_VERSE_VM || defined(__INTELLISENSE__)
6
10#include "CoreTypes.h"
12#include "Misc/Optional.h"
13#include "VVMContext.h"
14#include "VVMCppClassInfo.h"
15#include "VVMHeap.h"
16
17#include <atomic>
18
19class FJsonObject;
20class FJsonValue;
21
22namespace Verse
23{
24enum class ECompares : uint8;
25enum class EValueStringFormat;
26enum class EValueJSONFormat;
27enum class EVisitState;
28struct FAbstractVisitor;
31struct VEmergentType;
32struct FOpResult;
34
35namespace Private
36{
38{
39 static constexpr uint8 IsLockedFlag = 1;
40 static constexpr uint8 MayHaveWaitingLockFlag = 2;
41};
42} // namespace Private
43
45struct VCell
46{
47 // If set in GCData, means that this object has an object in the libpas verse_heap client_data for the verse_heap_page_header
48 // corresponding to this object. This just means that during marking, we want to process that data structure.
49 static constexpr uint8 GCDataIsWeakKeyBit = 1;
50
52
54 // (one reserved for GC)
56 std::atomic<uint8> GCData{0};
57 // The first two bits of this are used by TIntrusiveMutexes in VCell subclasses.
58 mutable std::atomic<uint8> Mutex{0};
59 // The first bit of Misc2 being set indicates this cell is deeply mutable.
60 static constexpr uint8 DeeplyMutableTag = 1;
61 // The second bit of Misc2 being set indicates this cell is a subobject of a CDO.
62 // TODO(SOL-7928): Remove this bit. It is a hack for BPVM compatibility.
63 static constexpr uint8 ArchetypeTag = 2;
64 union
65 {
66 struct
67 {
68 uint8_t Misc2;
70 };
72 };
73
74 VCell(const VCell&) = delete;
75 VCell& operator=(const VCell&) = delete;
76
77 COREUOBJECT_API VCell(FAllocationContext Context, const VEmergentType* EmergentType);
78
80 const VCppClassInfo* GetCppClassInfo() const;
81
82 // FIXME: In the future maybe these will take a FRunningContext or FAccessContext rather than
83 // the MarkStack or nothing. That's because:
84 // 1) Nothing wrong with saying that the GC threads allocate or run arbitrary code if the GC is
85 // concurrent by design. Like, maybe we'll want GC-time hash-consing.
86 // 2) In a parallel GC, we'll probably want to just reuse the fact that each context has a
87 // MarkStack.
92
93 void AddRef(FAccessContext Context); // Temporarily prevent this cell from being GC'ed
94 void ReleaseRef();
95
96 COREUOBJECT_API ECompares Equal(FAllocationContext Context, VCell* Other, const TFunction<void(::Verse::VValue, ::Verse::VValue)>& HandlePlaceholder);
97 COREUOBJECT_API VValue Melt(FAllocationContext Context);
98 COREUOBJECT_API FOpResult Freeze(FAllocationContext Context, VTask*, FOp* AwaitPC);
99 COREUOBJECT_API bool Subsumes(FAllocationContext Context, VValue);
100 COREUOBJECT_API void VisitMembers(FAllocationContext Context, FDebuggerVisitor& Visitor);
101 COREUOBJECT_API void AppendToString(FAllocationContext Context, FUtf8StringBuilderBase& Builder, EValueStringFormat Format, TSet<const void*>& VisitedObjects, uint32 RecursionDepth = 0);
102 COREUOBJECT_API FUtf8String ToString(FAllocationContext Context, EValueStringFormat Format, uint32 RecursionDepth = 0);
103 COREUOBJECT_API TSharedPtr<FJsonValue> ToJSON(FRunningContext Context, EValueJSONFormat Format, TMap<const void*, EVisitState>& VisitedObjects, const VerseVMToJsonCallback& Callback, uint32 RecursionDepth = 0, FJsonObject* Defs = nullptr);
105 COREUOBJECT_API void Serialize(FAllocationContext Context, FStructuredArchiveVisitor& Visitor);
106 COREUOBJECT_API void Serialize(FAllocationContext Context, FArchive& Ar);
107
108 bool IsDeeplyMutable() const { return Misc2 & DeeplyMutableTag; }
109 bool SetIsDeeplyMutable() { return Misc2 |= DeeplyMutableTag; }
110
111 COREUOBJECT_API static void InitializeGlobals(FAllocationContext Context);
112
113private:
114 // Use this if your cell subtype has any outgoing strong references. It is used by both the
115 // GC system and the abstract visitor to collect strong references.
116 //
117 // Note that this function will run concurrently to the mutator (this function is being called in
118 // some collector thread while the VM's other threads are calling other methods on your object)
119 // and in parallel (the collector will run multiple threads calling this function). However,
120 // you're guaranteed that this function will only be called once per object per collection cycle;
121 // i.e. the GC will never call this function simultaneously for the same object.
122 //
123 // It is defined implicitly by DECLARE_DERIVED_VCPPCLASSINFO, and so you must either implement it
124 // or use the DEFINE_TRIVIAL_VISIT_REFERENCES macro to explicitly define a trivial implementation.
125 //
126 // Visit outgoing references for self. Don't call visit on your super class, or visit references
127 // defined by your super class.
128 template <typename TVisitor>
130
132
133protected:
134 // Override this if your cell subtype has any outgoing weak references. Call ClearWeakDuringCensus
135 // on those pointers in this function.
136 //
137 // Note that this function may run concurrently to the mutator, in parallel, or from within the
138 // mutator's allocation slow paths. This means that this function cannot allocate, since it may be
139 // called while internal allocator locks are held. It may also be called while mutator locks are
140 // held, if those locks are held across allocations. So, this function cannot acquire locks,
141 // unless those locks are never held while either handshaking or allocating.
142 //
143 // This function is called exactly once per collection cycle for any marked objects that belong to
144 // the CensusSpace or the DestructorAndCensusSpace. This function is guaranteed to be called only
145 // after all marking is finished, so you can use FHeap::IsMarked and that will tell you if the
146 // object survived the collection or not. Because census runs before destruction and sweep, you're
147 // also guaranteed that any pointed-to dead objects are still fully intact and usable.
148 //
149 // It's not meaningful to override this function unless the cell is allocated from the CensusSpace
150 // or the DestructorAndCensusSpace.
152
153 // Override this if your cell requries deep comparison (simple comparisons should be inlined in VValue::Equal).
154 COREUOBJECT_API ECompares EqualImpl(FAllocationContext Context, VCell* Other, const TFunction<void(::Verse::VValue, ::Verse::VValue)>& HandlePlaceholder);
155
156 // Override this if your cell subtype requires a deep hash.
158
159 // Override this if your cell requires deep copying.
160 //
161 // Note: This also acts as "Clone" for mutable types which is defined as 'Freeze->Melt'.
162 // We can ignore the possibility that the skipped 'freeze' could error on placeholders
163 // as the mutable data we are operating on requires all values be concrete on creation
164 // or we would've suspended.
165 COREUOBJECT_API VValue MeltImpl(FAllocationContext Context);
166
167 // Override this if your cell is a mutable representation and requires deep copying.
168 COREUOBJECT_API FOpResult FreezeImpl(FAllocationContext Context, VTask*, FOp* AwaitPC);
169
170 // Override this if your cell represents a type that can be used in casts.
171 COREUOBJECT_API bool SubsumesImpl(FAllocationContext, VValue);
172
173 // Override this if your cell has members that should be visible in the debugger.
174 COREUOBJECT_API void VisitMembersImpl(FAllocationContext, FDebuggerVisitor&);
175
176 // Override this to customize how your cell is displayed, in the debugger and elsewhere.
177 COREUOBJECT_API void AppendToStringImpl(FAllocationContext Context, FUtf8StringBuilderBase& Builder, EValueStringFormat Format, TSet<const void*>& VisitedObjects, uint32 RecursionDepth);
178
179 // Override this to customize how your cell is printed for JSON formats [Analytics, Persistence, Persona]
180 // Note: This runs in the closed! So any allocations / most Ref accesses will require an AutoRTFM::Open
181 COREUOBJECT_API TSharedPtr<FJsonValue> ToJSONImpl(FRunningContext Context, EValueJSONFormat Format, TMap<const void*, EVisitState>& VisitedObjects, const VerseVMToJsonCallback& Callback, uint32 RecursionDepth = 0, FJsonObject* Defs = nullptr);
182
183 // Override this if you are a supported Persistence/Persona type
184 // TODO: Make a new vtable for VType which inherits our VCell vtable and appends this to it
186
187 // Override these if your cell is serializable.
188 //
189 // SerializeIdentity means that a cell's identity must be preserved through serialization,
190 // particularly when imported by another package. When it is false, the cell may be duplicated
191 // into multiple packages (and potentially deduplicated by SerializeLayout on load.)
192 //
193 // SerializeLayout should serialize just enough to allocate a deserialized cell - for example,
194 // the length of a trailing array. SerializeImpl should then serialize the remaining fields.
195
196 static constexpr bool SerializeIdentity = true;
197
198 template <typename CellType>
199 static void SerializeLayout(FAllocationContext, CellType*&, FStructuredArchiveVisitor&)
200 {
201 V_DIE("VCell subtype without `SerializeLayout` override called!");
202 }
203
205
206 // VCell() and SetEmergentType(..) are used during setup when creating some cyclic dependencies.
207 VCell()
209 {
210 checkSlow(FHeap::OwnsAddress(this));
211 }
212
213public:
215
216 // Override this if your cell subtype has a destructor.
217 //
218 // Note that this function may run concurrently to the mutator, in parallel, or from within the
219 // mutator's allocation slow paths. This means that this function cannot allocate, since it may be
220 // called while internal allocator locks are held. It may also be called while mutator locks are
221 // held, if those locks are held across allocations. So, this function cannot acquire locks,
222 // unless those locks are never held while either handshaking or allocating.
223 //
224 // This function is called exactly once per collection cycle for any unmarked objects that belong
225 // to the DestructorSpace or the DestructorAndCensusSpace. This function is guaranteed to be called
226 // only after all marking and census are finished. Because census doesn't cover unmarked objects,
227 // you will see nonnull weak references to dead objects. Because destruction runs before sweep,
228 // you're also guaranteed that any pointed-to dead objects are still fully intact and usable.
229 //
230 // It's not meaningful to override this function unless the cell is allocated from the
231 // DestructorSpace or the DestructorAndCensusSpace.
232 //
233 // Note: You must override this if your cell has external memory to report swept external
234 // bytes during destruction.
235 ~VCell() = default;
236
237 template <typename CastType>
238 bool IsA() const;
239
240 template <typename CastType>
241 const CastType& StaticCast() const;
242
243 template <typename CastType>
245
246 template <typename CastType>
248
249 template <typename CastType>
250 CastType* DynamicCast() const;
251
252 COREUOBJECT_API FString DebugName() const;
253
254 // Inform the GC that this cell is now a key in the following weak map and it keeps the given value alive.
255 // For sound concurrent GC handling of weak maps, both the map and the key must know about the mapping.
256 // Hence, every cell in the VVM has the secret ability to become a weak key. This costs just one bit per
257 // object if unused. It costs horrors and nightmares if used (but at least the terror is O(1)ish).
260
261 // This function is a test-only function because it has a very limited kind of meaning. Requesting the size (or
262 // checking the emptiness) of a weap map (including a "transposed" weak map, like the weak key map) gives a kind
263 // of upper bound: it means that the map has at most this many entries. But we cannot tell you which of those
264 // entries are real. When you query them, you are likely to find fewer entries.
265 bool HasWeakMappings();
266};
267
268static_assert(sizeof(VCell) <= 8);
269
272
274// To be or not to be ...
275// Keep it here for now.
276struct VHeapValue : VCell
277{
279
280 using VCell::VCell;
281};
282
283} // namespace Verse
284#endif // WITH_VERSE_VM
#define checkSlow(expr)
Definition AssertionMacros.h:332
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
void AppendToString(const FStringFormatArg &Arg, StringType &StringToAppendTo)
Definition StringFormatter.cpp:64
UE_REWRITE T StaticCast(ArgType &&Arg)
Definition UnrealTemplate.h:638
uint8_t uint8
Definition binka_ue_file_header.h:8
uint16_t uint16
Definition binka_ue_file_header.h:7
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Archive.h:1208
Definition JsonObject.h:23
Definition JsonValue.h:22
Definition AndroidPlatformMisc.h:14
Definition UnrealString.h.inl:34
Definition SharedPointer.h:692
Definition StringBuilder.h:79
Definition IntrusiveUniqueLock.h:18
FString ToString(uint16 Value)
Definition PathFollowingComponent.cpp:82
Definition Array.h:3955
UE::FRecursiveMutex Mutex
Definition MeshPaintVirtualTexture.cpp:164
bool SerializeImpl(const UScriptStruct *InSourceEventType, const void *InSourceEventData, FLiveLinkSerializedFrameData &OutSerializedData)
Definition LiveLinkCompression.cpp:126
Definition OverriddenPropertySet.cpp:45
To DynamicCast(From *Arg)
Definition Casts.h:541
FORCEINLINE_DEBUGGABLE void VisitMembers(DispatcherType &Dispatcher, FSchemaView Schema, ObjectType *Instance)
Definition FastReferenceCollector.h:685
bool IsA(const UStruct *)
Definition MassEntityElementTypes.h:49
Definition Archive.h:36
EValueJSONFormat
Definition VVMJson.h:47
EValueStringFormat
Definition VVMValuePrinting.h:17
EVisitState
Definition VVMJson.h:54
bool FromJSON(const JSONValue &JSON, bool *Value)
Definition JSON.h:71
bool ToJSON(bool Value, JSONValue *JSON, JSONMemoryPoolAllocator &)
Definition JSON.h:211