Skip to content

Commit 4d1b17e

Browse files
committed
update developer notes and reformat file
1 parent 8648636 commit 4d1b17e

1 file changed

Lines changed: 11 additions & 34 deletions

File tree

natvis/godot-cpp.natvis

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,23 @@
99
</Type>
1010

1111
<Type Name="godot::StringName">
12-
<Intrinsic Name="get_data_ptr" Expression="(*reinterpret_cast&lt;const char32_t***&gt;(opaque))[1]" />
12+
<Intrinsic Name="get_data_ptr" Expression="(*reinterpret_cast&lt;const char32_t***&gt;(opaque))[1]"/>
1313
<DisplayString Condition="get_data_ptr()">{get_data_ptr(),s32}</DisplayString>
1414
<Expand>
1515
<Item Name="opaque_ptr">*reinterpret_cast&lt;void**&gt;(opaque)</Item>
1616
<Item Name="cname">get_data_ptr(),s32</Item>
1717
</Expand>
1818
</Type>
1919

20-
<!-- Development notes: At the current state no AI can directly generate a natvis entry for each `godot::` type.
21-
The testdata used for evaluation https://github.com/JetBrains/godot-support/wiki/Developing-godot%E2%80%90cpp.natvis
22-
The change of the memory layout would require adjustments of the natvis
23-
24-
Approach:
25-
1. For each type, inspect `opaque` bytes in LLDB using `memory read -size 8 -count N -format address` on
26-
the address stored in opaque (e.g. `*(void**)demo_type.opaque`) to map the actual field layout.
27-
2. Verify specific field values match known test data (e.g. array size=1, int value=42, string content).
28-
3. Write natvis expressions only after the field offsets are confirmed.
29-
30-
Evaluator constraints (JetBrains natvis engine, I haven't checked anything else):
31-
- Supports: reinterpret_cast, pointer arithmetic on built-in types, array indexing ([n]), Intrinsics
32-
- Does NOT support: calling member functions (e.g. size())
33-
- Nested type visualizers work: `{*(godot::String*)(opaque+8)}` delegates to the String natvis entry
34-
35-
Patterns used:
36-
- Opaque wrapper types (String, StringName, Array, Dictionary, ...): cast opaque slice to the
37-
godot-cpp type pointer — `*(godot::TypeName*)(opaque+N)` — and let that type's natvis handle display
38-
- Engine heap-allocated types stored as pointer (Transform2D, AABB, Basis, ...): double-deref —
39-
`**(godot::TypeName**)(opaque+N)`
40-
- Engine internals without a godot-cpp wrapper (ArrayPrivate, CowData, HashMap): use raw
41-
reinterpret_cast + index arithmetic, document offsets with a comment verified by LLDB
42-
43-
Future upcomming improvements:
44-
- JetBrains natvis support is going to become x-plat https://youtrack.jetbrains.com/issue/CPP-35297
45-
-->
20+
<!-- Developer notes: https://jb.gg/godot-cpp-natvis -->
4621

4722
<!-- opaque[0..7] holds ArrayPrivate*. Dereference as unsigned long long* to read ArrayPrivate
4823
fields as 8-byte slots: [0]=refcount, [1]=unknown, [2]=_cowdata._ptr, [3]=read_only, ...
4924
Index [2] reaches _cowdata._ptr at offset +16 (verified by LLDB memory dump).
5025
Cast _ptr to unsigned long long* so [-1] reads the CowData element count 8 bytes before data. -->
5126
<Type Name="godot::Array">
52-
<Intrinsic Name="_cow" Expression="(unsigned long long*)(*reinterpret_cast&lt;unsigned long long**&gt;(opaque))[2]" />
27+
<Intrinsic Name="_cow"
28+
Expression="(unsigned long long*)(*reinterpret_cast&lt;unsigned long long**&gt;(opaque))[2]"/>
5329
<DisplayString Condition="!*reinterpret_cast&lt;void**&gt;(opaque) || !_cow()">{{empty}}</DisplayString>
5430
<DisplayString>{{size={_cow()[-1]}}}</DisplayString>
5531
<Expand>
@@ -66,16 +42,16 @@
6642
HashMap: [_elements(8)][unknown(8)][_head(8)][_last(8)][_capacity_index(4)][_num_elements(4)]
6743
HashMapElement: [next(8)][prev(8)][key:Variant(24)][value:Variant(24)] -->
6844
<Type Name="godot::Dictionary">
69-
<Intrinsic Name="_p" Expression="*reinterpret_cast&lt;unsigned char**&gt;(opaque)" />
70-
<Intrinsic Name="_size" Expression="*(unsigned int*)(_p() + 52)" />
71-
<Intrinsic Name="_head" Expression="*reinterpret_cast&lt;unsigned char**&gt;(_p() + 32)" />
45+
<Intrinsic Name="_p" Expression="*reinterpret_cast&lt;unsigned char**&gt;(opaque)"/>
46+
<Intrinsic Name="_size" Expression="*(unsigned int*)(_p() + 52)"/>
47+
<Intrinsic Name="_head" Expression="*reinterpret_cast&lt;unsigned char**&gt;(_p() + 32)"/>
7248
<DisplayString Condition="!_p()">{{empty}}</DisplayString>
7349
<DisplayString>{{size={_size()}}}</DisplayString>
7450
<Expand>
7551
<Item Name="[size]">_size()</Item>
7652
<CustomListItems MaxItemsPerView="5000">
7753
<Variable Name="elem" InitialValue="(unsigned char*)_head()"/>
78-
<Variable Name="idx" InitialValue="0"/>
54+
<Variable Name="idx" InitialValue="0"/>
7955
<Loop Condition="elem != 0">
8056
<Item Name="[{idx}]">(godot::Variant*)(elem + 16),[2]</Item>
8157
<Exec>elem = *(unsigned char**)elem</Exec>
@@ -93,7 +69,7 @@
9369
DICTIONARY=27 ARRAY=28
9470
Layout: opaque[0..3]=type(int32), opaque[4..7]=pad, opaque[8..23]=_data union -->
9571
<Type Name="godot::Variant">
96-
<Intrinsic Name="_type" Expression="*reinterpret_cast&lt;int*&gt;(opaque)" />
72+
<Intrinsic Name="_type" Expression="*reinterpret_cast&lt;int*&gt;(opaque)"/>
9773
<DisplayString Condition="_type() == 0">nil</DisplayString>
9874
<DisplayString Condition="_type() == 1">{*reinterpret_cast&lt;bool*&gt;(opaque+8)}</DisplayString>
9975
<DisplayString Condition="_type() == 2">{*reinterpret_cast&lt;long long*&gt;(opaque+8)}</DisplayString>
@@ -125,7 +101,8 @@
125101
<DisplayString Condition="_type() == 18">{**(godot::Transform3D**)(opaque+8)}</DisplayString>
126102
<DisplayString Condition="_type() == 19">{**(godot::Projection**)(opaque+8)}</DisplayString>
127103
<!-- OBJECT: opaque+8=ObjectID, opaque+16=Object* -->
128-
<DisplayString Condition="_type() == 24">Object(id={*reinterpret_cast&lt;unsigned long long*&gt;(opaque+8)})</DisplayString>
104+
<DisplayString Condition="_type() == 24">Object(id={*reinterpret_cast&lt;unsigned long long*&gt;(opaque+8)})
105+
</DisplayString>
129106
<DisplayString>{{type={_type()}}}</DisplayString>
130107
<Expand>
131108
<Item Name="[type]">_type()</Item>

0 commit comments

Comments
 (0)