1 | /**************************************************************************/ |
2 | /* extension_api_dump.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "extension_api_dump.h" |
32 | |
33 | #include "core/config/engine.h" |
34 | #include "core/core_constants.h" |
35 | #include "core/io/file_access.h" |
36 | #include "core/io/json.h" |
37 | #include "core/templates/pair.h" |
38 | #include "core/version.h" |
39 | |
40 | #ifdef TOOLS_ENABLED |
41 | |
42 | static String get_builtin_or_variant_type_name(const Variant::Type p_type) { |
43 | if (p_type == Variant::NIL) { |
44 | return "Variant" ; |
45 | } else { |
46 | return Variant::get_type_name(p_type); |
47 | } |
48 | } |
49 | |
50 | static String get_property_info_type_name(const PropertyInfo &p_info) { |
51 | if (p_info.type == Variant::INT && (p_info.hint == PROPERTY_HINT_INT_IS_POINTER)) { |
52 | if (p_info.hint_string.is_empty()) { |
53 | return "void*" ; |
54 | } else { |
55 | return p_info.hint_string + "*" ; |
56 | } |
57 | } |
58 | if (p_info.type == Variant::ARRAY && (p_info.hint == PROPERTY_HINT_ARRAY_TYPE)) { |
59 | return String("typedarray::" ) + p_info.hint_string; |
60 | } |
61 | if (p_info.type == Variant::INT && (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM))) { |
62 | return String("enum::" ) + String(p_info.class_name); |
63 | } |
64 | if (p_info.type == Variant::INT && (p_info.usage & (PROPERTY_USAGE_CLASS_IS_BITFIELD))) { |
65 | return String("bitfield::" ) + String(p_info.class_name); |
66 | } |
67 | if (p_info.type == Variant::INT && (p_info.usage & PROPERTY_USAGE_ARRAY)) { |
68 | return "int" ; |
69 | } |
70 | if (p_info.class_name != StringName()) { |
71 | return p_info.class_name; |
72 | } |
73 | if (p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { |
74 | return p_info.hint_string; |
75 | } |
76 | if (p_info.type == Variant::NIL && (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) { |
77 | return "Variant" ; |
78 | } |
79 | if (p_info.type == Variant::NIL) { |
80 | return "void" ; |
81 | } |
82 | return get_builtin_or_variant_type_name(p_info.type); |
83 | } |
84 | |
85 | static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) { |
86 | static const char *argmeta[11] = { "none" , "int8" , "int16" , "int32" , "int64" , "uint8" , "uint16" , "uint32" , "uint64" , "float" , "double" }; |
87 | return argmeta[metadata]; |
88 | } |
89 | |
90 | Dictionary GDExtensionAPIDump::generate_extension_api() { |
91 | Dictionary api_dump; |
92 | |
93 | { |
94 | //header |
95 | Dictionary ; |
96 | header["version_major" ] = VERSION_MAJOR; |
97 | header["version_minor" ] = VERSION_MINOR; |
98 | #if VERSION_PATCH |
99 | header["version_patch" ] = VERSION_PATCH; |
100 | #else |
101 | header["version_patch" ] = 0; |
102 | #endif |
103 | header["version_status" ] = VERSION_STATUS; |
104 | header["version_build" ] = VERSION_BUILD; |
105 | header["version_full_name" ] = VERSION_FULL_NAME; |
106 | |
107 | api_dump["header" ] = header; |
108 | } |
109 | |
110 | const uint32_t vec3_elems = 3; |
111 | const uint32_t vec4_elems = 4; |
112 | const uint32_t ptrsize_32 = 4; |
113 | const uint32_t ptrsize_64 = 8; |
114 | static const char *build_config_name[4] = { "float_32" , "float_64" , "double_32" , "double_64" }; |
115 | |
116 | { |
117 | //type sizes |
118 | constexpr struct { |
119 | Variant::Type type; |
120 | uint32_t size_32_bits_real_float; |
121 | uint32_t size_64_bits_real_float; |
122 | uint32_t size_32_bits_real_double; |
123 | uint32_t size_64_bits_real_double; |
124 | |
125 | // For compile-time size check. |
126 | constexpr uint32_t operator[](int index) const { |
127 | switch (index) { |
128 | #ifndef REAL_T_IS_DOUBLE |
129 | case sizeof(uint32_t): |
130 | return size_32_bits_real_float; |
131 | case sizeof(uint64_t): |
132 | return size_64_bits_real_float; |
133 | #else // REAL_T_IS_DOUBLE |
134 | case sizeof(uint32_t): |
135 | return size_32_bits_real_double; |
136 | case sizeof(uint64_t): |
137 | return size_64_bits_real_double; |
138 | #endif |
139 | } |
140 | return -1; |
141 | } |
142 | } type_size_array[Variant::VARIANT_MAX + 1] = { |
143 | { Variant::NIL, 0, 0, 0, 0 }, |
144 | { Variant::BOOL, sizeof(uint8_t), sizeof(uint8_t), sizeof(uint8_t), sizeof(uint8_t) }, |
145 | { Variant::INT, sizeof(int64_t), sizeof(int64_t), sizeof(int64_t), sizeof(int64_t) }, |
146 | { Variant::FLOAT, sizeof(double), sizeof(double), sizeof(double), sizeof(double) }, |
147 | { Variant::STRING, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, |
148 | { Variant::VECTOR2, 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) }, |
149 | { Variant::VECTOR2I, 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) }, |
150 | { Variant::RECT2, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, |
151 | { Variant::RECT2I, 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t) }, |
152 | { Variant::VECTOR3, vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, |
153 | { Variant::VECTOR3I, 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t) }, |
154 | { Variant::TRANSFORM2D, 6 * sizeof(float), 6 * sizeof(float), 6 * sizeof(double), 6 * sizeof(double) }, |
155 | { Variant::VECTOR4, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, |
156 | { Variant::VECTOR4I, 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t) }, |
157 | { Variant::PLANE, (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(double), (vec3_elems + 1) * sizeof(double) }, |
158 | { Variant::QUATERNION, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) }, |
159 | { Variant::AABB, (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(double), (vec3_elems * 2) * sizeof(double) }, |
160 | { Variant::BASIS, (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) }, |
161 | { Variant::TRANSFORM3D, (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(double), (vec3_elems * 4) * sizeof(double) }, |
162 | { Variant::PROJECTION, (vec4_elems * 4) * sizeof(float), (vec4_elems * 4) * sizeof(float), (vec4_elems * 4) * sizeof(double), (vec4_elems * 4) * sizeof(double) }, |
163 | { Variant::COLOR, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float) }, |
164 | { Variant::STRING_NAME, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, |
165 | { Variant::NODE_PATH, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, |
166 | { Variant::RID, sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t) }, |
167 | { Variant::OBJECT, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, |
168 | { Variant::CALLABLE, sizeof(Callable), sizeof(Callable), sizeof(Callable), sizeof(Callable) }, // Hardcoded align. |
169 | { Variant::SIGNAL, sizeof(Signal), sizeof(Signal), sizeof(Signal), sizeof(Signal) }, // Hardcoded align. |
170 | { Variant::DICTIONARY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, |
171 | { Variant::ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, |
172 | { Variant::PACKED_BYTE_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
173 | { Variant::PACKED_INT32_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
174 | { Variant::PACKED_INT64_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
175 | { Variant::PACKED_FLOAT32_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
176 | { Variant::PACKED_FLOAT64_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
177 | { Variant::PACKED_STRING_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
178 | { Variant::PACKED_VECTOR2_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
179 | { Variant::PACKED_VECTOR3_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
180 | { Variant::PACKED_COLOR_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, |
181 | { Variant::VARIANT_MAX, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(double) * 4, sizeof(uint64_t) + sizeof(double) * 4 }, |
182 | }; |
183 | |
184 | // Validate sizes at compile time for the current build configuration. |
185 | static_assert(type_size_array[Variant::BOOL][sizeof(void *)] == sizeof(GDExtensionBool), "Size of bool mismatch" ); |
186 | static_assert(type_size_array[Variant::INT][sizeof(void *)] == sizeof(GDExtensionInt), "Size of int mismatch" ); |
187 | static_assert(type_size_array[Variant::FLOAT][sizeof(void *)] == sizeof(double), "Size of float mismatch" ); |
188 | static_assert(type_size_array[Variant::STRING][sizeof(void *)] == sizeof(String), "Size of String mismatch" ); |
189 | static_assert(type_size_array[Variant::VECTOR2][sizeof(void *)] == sizeof(Vector2), "Size of Vector2 mismatch" ); |
190 | static_assert(type_size_array[Variant::VECTOR2I][sizeof(void *)] == sizeof(Vector2i), "Size of Vector2i mismatch" ); |
191 | static_assert(type_size_array[Variant::RECT2][sizeof(void *)] == sizeof(Rect2), "Size of Rect2 mismatch" ); |
192 | static_assert(type_size_array[Variant::RECT2I][sizeof(void *)] == sizeof(Rect2i), "Size of Rect2i mismatch" ); |
193 | static_assert(type_size_array[Variant::VECTOR3][sizeof(void *)] == sizeof(Vector3), "Size of Vector3 mismatch" ); |
194 | static_assert(type_size_array[Variant::VECTOR3I][sizeof(void *)] == sizeof(Vector3i), "Size of Vector3i mismatch" ); |
195 | static_assert(type_size_array[Variant::TRANSFORM2D][sizeof(void *)] == sizeof(Transform2D), "Size of Transform2D mismatch" ); |
196 | static_assert(type_size_array[Variant::VECTOR4][sizeof(void *)] == sizeof(Vector4), "Size of Vector4 mismatch" ); |
197 | static_assert(type_size_array[Variant::VECTOR4I][sizeof(void *)] == sizeof(Vector4i), "Size of Vector4i mismatch" ); |
198 | static_assert(type_size_array[Variant::PLANE][sizeof(void *)] == sizeof(Plane), "Size of Plane mismatch" ); |
199 | static_assert(type_size_array[Variant::QUATERNION][sizeof(void *)] == sizeof(Quaternion), "Size of Quaternion mismatch" ); |
200 | static_assert(type_size_array[Variant::AABB][sizeof(void *)] == sizeof(AABB), "Size of AABB mismatch" ); |
201 | static_assert(type_size_array[Variant::BASIS][sizeof(void *)] == sizeof(Basis), "Size of Basis mismatch" ); |
202 | static_assert(type_size_array[Variant::TRANSFORM3D][sizeof(void *)] == sizeof(Transform3D), "Size of Transform3D mismatch" ); |
203 | static_assert(type_size_array[Variant::PROJECTION][sizeof(void *)] == sizeof(Projection), "Size of Projection mismatch" ); |
204 | static_assert(type_size_array[Variant::COLOR][sizeof(void *)] == sizeof(Color), "Size of Color mismatch" ); |
205 | static_assert(type_size_array[Variant::STRING_NAME][sizeof(void *)] == sizeof(StringName), "Size of StringName mismatch" ); |
206 | static_assert(type_size_array[Variant::NODE_PATH][sizeof(void *)] == sizeof(NodePath), "Size of NodePath mismatch" ); |
207 | static_assert(type_size_array[Variant::RID][sizeof(void *)] == sizeof(RID), "Size of RID mismatch" ); |
208 | static_assert(type_size_array[Variant::OBJECT][sizeof(void *)] == sizeof(Object *), "Size of Object mismatch" ); |
209 | static_assert(type_size_array[Variant::CALLABLE][sizeof(void *)] == sizeof(Callable), "Size of Callable mismatch" ); |
210 | static_assert(type_size_array[Variant::SIGNAL][sizeof(void *)] == sizeof(Signal), "Size of Signal mismatch" ); |
211 | static_assert(type_size_array[Variant::DICTIONARY][sizeof(void *)] == sizeof(Dictionary), "Size of Dictionary mismatch" ); |
212 | static_assert(type_size_array[Variant::ARRAY][sizeof(void *)] == sizeof(Array), "Size of Array mismatch" ); |
213 | static_assert(type_size_array[Variant::PACKED_BYTE_ARRAY][sizeof(void *)] == sizeof(PackedByteArray), "Size of PackedByteArray mismatch" ); |
214 | static_assert(type_size_array[Variant::PACKED_INT32_ARRAY][sizeof(void *)] == sizeof(PackedInt32Array), "Size of PackedInt32Array mismatch" ); |
215 | static_assert(type_size_array[Variant::PACKED_INT64_ARRAY][sizeof(void *)] == sizeof(PackedInt64Array), "Size of PackedInt64Array mismatch" ); |
216 | static_assert(type_size_array[Variant::PACKED_FLOAT32_ARRAY][sizeof(void *)] == sizeof(PackedFloat32Array), "Size of PackedFloat32Array mismatch" ); |
217 | static_assert(type_size_array[Variant::PACKED_FLOAT64_ARRAY][sizeof(void *)] == sizeof(PackedFloat64Array), "Size of PackedFloat64Array mismatch" ); |
218 | static_assert(type_size_array[Variant::PACKED_STRING_ARRAY][sizeof(void *)] == sizeof(PackedStringArray), "Size of PackedStringArray mismatch" ); |
219 | static_assert(type_size_array[Variant::PACKED_VECTOR2_ARRAY][sizeof(void *)] == sizeof(PackedVector2Array), "Size of PackedVector2Array mismatch" ); |
220 | static_assert(type_size_array[Variant::PACKED_VECTOR3_ARRAY][sizeof(void *)] == sizeof(PackedVector3Array), "Size of PackedVector3Array mismatch" ); |
221 | static_assert(type_size_array[Variant::PACKED_COLOR_ARRAY][sizeof(void *)] == sizeof(PackedColorArray), "Size of PackedColorArray mismatch" ); |
222 | static_assert(type_size_array[Variant::VARIANT_MAX][sizeof(void *)] == sizeof(Variant), "Size of Variant mismatch" ); |
223 | |
224 | Array core_type_sizes; |
225 | |
226 | for (int i = 0; i < 4; i++) { |
227 | Dictionary d; |
228 | d["build_configuration" ] = build_config_name[i]; |
229 | Array sizes; |
230 | for (int j = 0; j <= Variant::VARIANT_MAX; j++) { |
231 | Variant::Type t = type_size_array[j].type; |
232 | String name = t == Variant::VARIANT_MAX ? String("Variant" ) : Variant::get_type_name(t); |
233 | Dictionary d2; |
234 | d2["name" ] = name; |
235 | uint32_t size = 0; |
236 | switch (i) { |
237 | case 0: |
238 | size = type_size_array[j].size_32_bits_real_float; |
239 | break; |
240 | case 1: |
241 | size = type_size_array[j].size_64_bits_real_float; |
242 | break; |
243 | case 2: |
244 | size = type_size_array[j].size_32_bits_real_double; |
245 | break; |
246 | case 3: |
247 | size = type_size_array[j].size_64_bits_real_double; |
248 | break; |
249 | } |
250 | d2["size" ] = size; |
251 | sizes.push_back(d2); |
252 | } |
253 | d["sizes" ] = sizes; |
254 | core_type_sizes.push_back(d); |
255 | } |
256 | api_dump["builtin_class_sizes" ] = core_type_sizes; |
257 | } |
258 | |
259 | { |
260 | // Member offsets, meta types and sizes. |
261 | |
262 | #define REAL_MEMBER_OFFSET(type, member) \ |
263 | { \ |
264 | type, \ |
265 | member, \ |
266 | "float", \ |
267 | sizeof(float), \ |
268 | "float", \ |
269 | sizeof(float), \ |
270 | "double", \ |
271 | sizeof(double), \ |
272 | "double", \ |
273 | sizeof(double), \ |
274 | } |
275 | |
276 | #define INT32_MEMBER_OFFSET(type, member) \ |
277 | { \ |
278 | type, \ |
279 | member, \ |
280 | "int32", \ |
281 | sizeof(int32_t), \ |
282 | "int32", \ |
283 | sizeof(int32_t), \ |
284 | "int32", \ |
285 | sizeof(int32_t), \ |
286 | "int32", \ |
287 | sizeof(int32_t), \ |
288 | } |
289 | |
290 | #define INT32_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \ |
291 | { \ |
292 | type, \ |
293 | member, \ |
294 | member_type, \ |
295 | sizeof(int32_t) * member_elems, \ |
296 | member_type, \ |
297 | sizeof(int32_t) * member_elems, \ |
298 | member_type, \ |
299 | sizeof(int32_t) * member_elems, \ |
300 | member_type, \ |
301 | sizeof(int32_t) * member_elems, \ |
302 | } |
303 | |
304 | #define REAL_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \ |
305 | { \ |
306 | type, \ |
307 | member, \ |
308 | member_type, \ |
309 | sizeof(float) * member_elems, \ |
310 | member_type, \ |
311 | sizeof(float) * member_elems, \ |
312 | member_type, \ |
313 | sizeof(double) * member_elems, \ |
314 | member_type, \ |
315 | sizeof(double) * member_elems, \ |
316 | } |
317 | |
318 | struct { |
319 | Variant::Type type; |
320 | const char *member; |
321 | const char *member_meta_32_bits_real_float; |
322 | const uint32_t member_size_32_bits_real_float; |
323 | const char *member_meta_64_bits_real_float; |
324 | const uint32_t member_size_64_bits_real_float; |
325 | const char *member_meta_32_bits_real_double; |
326 | const uint32_t member_size_32_bits_real_double; |
327 | const char *member_meta_64_bits_real_double; |
328 | const uint32_t member_size_64_bits_real_double; |
329 | } member_offset_array[] = { |
330 | // Vector2 |
331 | REAL_MEMBER_OFFSET(Variant::VECTOR2, "x" ), |
332 | REAL_MEMBER_OFFSET(Variant::VECTOR2, "y" ), |
333 | // Vector2i |
334 | INT32_MEMBER_OFFSET(Variant::VECTOR2I, "x" ), |
335 | INT32_MEMBER_OFFSET(Variant::VECTOR2I, "y" ), |
336 | // Rect2 |
337 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2, "position" , "Vector2" , 2), |
338 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2, "size" , "Vector2" , 2), |
339 | // Rect2i |
340 | INT32_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2I, "position" , "Vector2i" , 2), |
341 | INT32_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2I, "size" , "Vector2i" , 2), |
342 | // Vector3 |
343 | REAL_MEMBER_OFFSET(Variant::VECTOR3, "x" ), |
344 | REAL_MEMBER_OFFSET(Variant::VECTOR3, "y" ), |
345 | REAL_MEMBER_OFFSET(Variant::VECTOR3, "z" ), |
346 | // Vector3i |
347 | INT32_MEMBER_OFFSET(Variant::VECTOR3I, "x" ), |
348 | INT32_MEMBER_OFFSET(Variant::VECTOR3I, "y" ), |
349 | INT32_MEMBER_OFFSET(Variant::VECTOR3I, "z" ), |
350 | // Transform2D |
351 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "x" , "Vector2" , 2), |
352 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "y" , "Vector2" , 2), |
353 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "origin" , "Vector2" , 2), |
354 | // Vector4 |
355 | REAL_MEMBER_OFFSET(Variant::VECTOR4, "x" ), |
356 | REAL_MEMBER_OFFSET(Variant::VECTOR4, "y" ), |
357 | REAL_MEMBER_OFFSET(Variant::VECTOR4, "z" ), |
358 | REAL_MEMBER_OFFSET(Variant::VECTOR4, "w" ), |
359 | // Vector4i |
360 | INT32_MEMBER_OFFSET(Variant::VECTOR4I, "x" ), |
361 | INT32_MEMBER_OFFSET(Variant::VECTOR4I, "y" ), |
362 | INT32_MEMBER_OFFSET(Variant::VECTOR4I, "z" ), |
363 | INT32_MEMBER_OFFSET(Variant::VECTOR4I, "w" ), |
364 | // Plane |
365 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PLANE, "normal" , "Vector3" , vec3_elems), |
366 | REAL_MEMBER_OFFSET(Variant::PLANE, "d" ), |
367 | // Quaternion |
368 | REAL_MEMBER_OFFSET(Variant::QUATERNION, "x" ), |
369 | REAL_MEMBER_OFFSET(Variant::QUATERNION, "y" ), |
370 | REAL_MEMBER_OFFSET(Variant::QUATERNION, "z" ), |
371 | REAL_MEMBER_OFFSET(Variant::QUATERNION, "w" ), |
372 | // AABB |
373 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::AABB, "position" , "Vector3" , vec3_elems), |
374 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::AABB, "size" , "Vector3" , vec3_elems), |
375 | // Basis (remember that basis vectors are flipped!) |
376 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "x" , "Vector3" , vec3_elems), |
377 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "y" , "Vector3" , vec3_elems), |
378 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "z" , "Vector3" , vec3_elems), |
379 | // Transform3D |
380 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM3D, "basis" , "Basis" , vec3_elems * 3), |
381 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM3D, "origin" , "Vector3" , vec3_elems), |
382 | // Projection |
383 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "x" , "Vector4" , vec4_elems), |
384 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "y" , "Vector4" , vec4_elems), |
385 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "z" , "Vector4" , vec4_elems), |
386 | REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "w" , "Vector4" , vec4_elems), |
387 | // Color (always composed of 4bytes floats) |
388 | { Variant::COLOR, "r" , "float" , sizeof(float), "float" , sizeof(float), "float" , sizeof(float), "float" , sizeof(float) }, |
389 | { Variant::COLOR, "g" , "float" , sizeof(float), "float" , sizeof(float), "float" , sizeof(float), "float" , sizeof(float) }, |
390 | { Variant::COLOR, "b" , "float" , sizeof(float), "float" , sizeof(float), "float" , sizeof(float), "float" , sizeof(float) }, |
391 | { Variant::COLOR, "a" , "float" , sizeof(float), "float" , sizeof(float), "float" , sizeof(float), "float" , sizeof(float) }, |
392 | // End marker, must stay last |
393 | { Variant::NIL, nullptr, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0 }, |
394 | }; |
395 | |
396 | Array core_type_member_offsets; |
397 | |
398 | for (int i = 0; i < 4; i++) { |
399 | Dictionary d; |
400 | d["build_configuration" ] = build_config_name[i]; |
401 | Array type_offsets; |
402 | uint32_t idx = 0; |
403 | |
404 | Variant::Type previous_type = Variant::NIL; |
405 | |
406 | Dictionary d2; |
407 | Array members; |
408 | uint32_t offset = 0; |
409 | |
410 | while (true) { |
411 | Variant::Type t = member_offset_array[idx].type; |
412 | if (t != previous_type) { |
413 | if (previous_type != Variant::NIL) { |
414 | d2["members" ] = members; |
415 | type_offsets.push_back(d2); |
416 | } |
417 | if (t == Variant::NIL) { |
418 | break; |
419 | } |
420 | |
421 | String name = t == Variant::VARIANT_MAX ? String("Variant" ) : Variant::get_type_name(t); |
422 | d2 = Dictionary(); |
423 | members = Array(); |
424 | offset = 0; |
425 | d2["name" ] = name; |
426 | previous_type = t; |
427 | } |
428 | Dictionary d3; |
429 | const char *member_meta = nullptr; |
430 | uint32_t member_size = 0; |
431 | switch (i) { |
432 | case 0: |
433 | member_meta = member_offset_array[idx].member_meta_32_bits_real_float; |
434 | member_size = member_offset_array[idx].member_size_32_bits_real_float; |
435 | break; |
436 | case 1: |
437 | member_meta = member_offset_array[idx].member_meta_64_bits_real_float; |
438 | member_size = member_offset_array[idx].member_size_64_bits_real_float; |
439 | break; |
440 | case 2: |
441 | member_meta = member_offset_array[idx].member_meta_32_bits_real_double; |
442 | member_size = member_offset_array[idx].member_size_32_bits_real_double; |
443 | break; |
444 | case 3: |
445 | member_meta = member_offset_array[idx].member_meta_64_bits_real_double; |
446 | member_size = member_offset_array[idx].member_size_64_bits_real_double; |
447 | break; |
448 | } |
449 | d3["member" ] = member_offset_array[idx].member; |
450 | d3["offset" ] = offset; |
451 | d3["meta" ] = member_meta; |
452 | offset += member_size; |
453 | members.push_back(d3); |
454 | idx++; |
455 | } |
456 | d["classes" ] = type_offsets; |
457 | core_type_member_offsets.push_back(d); |
458 | } |
459 | api_dump["builtin_class_member_offsets" ] = core_type_member_offsets; |
460 | } |
461 | |
462 | { |
463 | // Global enums and constants. |
464 | Array constants; |
465 | HashMap<String, List<Pair<String, int64_t>>> enum_list; |
466 | HashMap<String, bool> enum_is_bitfield; |
467 | |
468 | for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) { |
469 | int64_t value = CoreConstants::get_global_constant_value(i); |
470 | String enum_name = CoreConstants::get_global_constant_enum(i); |
471 | String name = CoreConstants::get_global_constant_name(i); |
472 | bool bitfield = CoreConstants::is_global_constant_bitfield(i); |
473 | if (!enum_name.is_empty()) { |
474 | enum_list[enum_name].push_back(Pair<String, int64_t>(name, value)); |
475 | enum_is_bitfield[enum_name] = bitfield; |
476 | } else { |
477 | Dictionary d; |
478 | d["name" ] = name; |
479 | d["value" ] = value; |
480 | d["is_bitfield" ] = bitfield; |
481 | constants.push_back(d); |
482 | } |
483 | } |
484 | |
485 | api_dump["global_constants" ] = constants; |
486 | |
487 | Array enums; |
488 | for (const KeyValue<String, List<Pair<String, int64_t>>> &E : enum_list) { |
489 | Dictionary d1; |
490 | d1["name" ] = E.key; |
491 | d1["is_bitfield" ] = enum_is_bitfield[E.key]; |
492 | Array values; |
493 | for (const Pair<String, int64_t> &F : E.value) { |
494 | Dictionary d2; |
495 | d2["name" ] = F.first; |
496 | d2["value" ] = F.second; |
497 | values.push_back(d2); |
498 | } |
499 | d1["values" ] = values; |
500 | enums.push_back(d1); |
501 | } |
502 | |
503 | api_dump["global_enums" ] = enums; |
504 | } |
505 | { |
506 | Array utility_funcs; |
507 | |
508 | List<StringName> utility_func_names; |
509 | Variant::get_utility_function_list(&utility_func_names); |
510 | |
511 | for (const StringName &name : utility_func_names) { |
512 | Dictionary func; |
513 | func["name" ] = String(name); |
514 | if (Variant::has_utility_function_return_value(name)) { |
515 | Variant::Type rt = Variant::get_utility_function_return_type(name); |
516 | func["return_type" ] = rt == Variant::NIL ? String("Variant" ) : Variant::get_type_name(rt); |
517 | } |
518 | switch (Variant::get_utility_function_type(name)) { |
519 | case Variant::UTILITY_FUNC_TYPE_MATH: |
520 | func["category" ] = "math" ; |
521 | break; |
522 | case Variant::UTILITY_FUNC_TYPE_RANDOM: |
523 | func["category" ] = "random" ; |
524 | break; |
525 | case Variant::UTILITY_FUNC_TYPE_GENERAL: |
526 | func["category" ] = "general" ; |
527 | break; |
528 | } |
529 | bool vararg = Variant::is_utility_function_vararg(name); |
530 | func["is_vararg" ] = Variant::is_utility_function_vararg(name); |
531 | func["hash" ] = Variant::get_utility_function_hash(name); |
532 | Array arguments; |
533 | int argcount = Variant::get_utility_function_argument_count(name); |
534 | for (int i = 0; i < argcount; i++) { |
535 | Dictionary arg; |
536 | String argname = vararg ? "arg" + itos(i + 1) : Variant::get_utility_function_argument_name(name, i); |
537 | arg["name" ] = argname; |
538 | arg["type" ] = get_builtin_or_variant_type_name(Variant::get_utility_function_argument_type(name, i)); |
539 | //no default value support in utility functions |
540 | arguments.push_back(arg); |
541 | } |
542 | |
543 | if (arguments.size()) { |
544 | func["arguments" ] = arguments; |
545 | } |
546 | |
547 | utility_funcs.push_back(func); |
548 | } |
549 | |
550 | api_dump["utility_functions" ] = utility_funcs; |
551 | } |
552 | |
553 | { |
554 | // builtin types |
555 | |
556 | Array builtins; |
557 | |
558 | for (int i = 0; i < Variant::VARIANT_MAX; i++) { |
559 | if (i == Variant::OBJECT) { |
560 | continue; |
561 | } |
562 | |
563 | Variant::Type type = Variant::Type(i); |
564 | |
565 | Dictionary d; |
566 | d["name" ] = Variant::get_type_name(type); |
567 | if (Variant::has_indexing(type)) { |
568 | d["indexing_return_type" ] = get_builtin_or_variant_type_name(Variant::get_indexed_element_type(type)); |
569 | } |
570 | |
571 | d["is_keyed" ] = Variant::is_keyed(type); |
572 | |
573 | { |
574 | //members |
575 | Array members; |
576 | |
577 | List<StringName> member_names; |
578 | Variant::get_member_list(type, &member_names); |
579 | for (const StringName &member_name : member_names) { |
580 | Dictionary d2; |
581 | d2["name" ] = String(member_name); |
582 | d2["type" ] = get_builtin_or_variant_type_name(Variant::get_member_type(type, member_name)); |
583 | members.push_back(d2); |
584 | } |
585 | if (members.size()) { |
586 | d["members" ] = members; |
587 | } |
588 | } |
589 | { |
590 | //constants |
591 | Array constants; |
592 | |
593 | List<StringName> constant_names; |
594 | Variant::get_constants_for_type(type, &constant_names); |
595 | for (const StringName &constant_name : constant_names) { |
596 | Dictionary d2; |
597 | d2["name" ] = String(constant_name); |
598 | Variant constant = Variant::get_constant_value(type, constant_name); |
599 | d2["type" ] = get_builtin_or_variant_type_name(constant.get_type()); |
600 | d2["value" ] = constant.get_construct_string(); |
601 | constants.push_back(d2); |
602 | } |
603 | if (constants.size()) { |
604 | d["constants" ] = constants; |
605 | } |
606 | } |
607 | { |
608 | //enums |
609 | Array enums; |
610 | |
611 | List<StringName> enum_names; |
612 | Variant::get_enums_for_type(type, &enum_names); |
613 | for (const StringName &enum_name : enum_names) { |
614 | Dictionary enum_dict; |
615 | enum_dict["name" ] = String(enum_name); |
616 | |
617 | List<StringName> enumeration_names; |
618 | Variant::get_enumerations_for_enum(type, enum_name, &enumeration_names); |
619 | |
620 | Array values; |
621 | |
622 | for (const StringName &enumeration : enumeration_names) { |
623 | Dictionary values_dict; |
624 | values_dict["name" ] = String(enumeration); |
625 | values_dict["value" ] = Variant::get_enum_value(type, enum_name, enumeration); |
626 | values.push_back(values_dict); |
627 | } |
628 | |
629 | if (values.size()) { |
630 | enum_dict["values" ] = values; |
631 | } |
632 | enums.push_back(enum_dict); |
633 | } |
634 | |
635 | if (enums.size()) { |
636 | d["enums" ] = enums; |
637 | } |
638 | } |
639 | { |
640 | //operators |
641 | Array operators; |
642 | |
643 | for (int j = 0; j < Variant::VARIANT_MAX; j++) { |
644 | for (int k = 0; k < Variant::OP_MAX; k++) { |
645 | Variant::Type rt = Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j)); |
646 | if (rt != Variant::NIL) { |
647 | Dictionary d2; |
648 | d2["name" ] = Variant::get_operator_name(Variant::Operator(k)); |
649 | if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) { |
650 | d2["right_type" ] = get_builtin_or_variant_type_name(Variant::Type(j)); |
651 | } |
652 | d2["return_type" ] = get_builtin_or_variant_type_name(Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j))); |
653 | operators.push_back(d2); |
654 | } |
655 | } |
656 | } |
657 | if (operators.size()) { |
658 | d["operators" ] = operators; |
659 | } |
660 | } |
661 | { |
662 | //methods |
663 | Array methods; |
664 | |
665 | List<StringName> method_names; |
666 | Variant::get_builtin_method_list(type, &method_names); |
667 | for (const StringName &method_name : method_names) { |
668 | Dictionary d2; |
669 | d2["name" ] = String(method_name); |
670 | if (Variant::has_builtin_method_return_value(type, method_name)) { |
671 | Variant::Type ret_type = Variant::get_builtin_method_return_type(type, method_name); |
672 | d2["return_type" ] = ret_type == Variant::NIL ? String("Variant" ) : Variant::get_type_name(ret_type); |
673 | } |
674 | d2["is_vararg" ] = Variant::is_builtin_method_vararg(type, method_name); |
675 | d2["is_const" ] = Variant::is_builtin_method_const(type, method_name); |
676 | d2["is_static" ] = Variant::is_builtin_method_static(type, method_name); |
677 | d2["hash" ] = Variant::get_builtin_method_hash(type, method_name); |
678 | |
679 | Vector<Variant> default_args = Variant::get_builtin_method_default_arguments(type, method_name); |
680 | |
681 | Array arguments; |
682 | int argcount = Variant::get_builtin_method_argument_count(type, method_name); |
683 | for (int j = 0; j < argcount; j++) { |
684 | Dictionary d3; |
685 | d3["name" ] = Variant::get_builtin_method_argument_name(type, method_name, j); |
686 | d3["type" ] = get_builtin_or_variant_type_name(Variant::get_builtin_method_argument_type(type, method_name, j)); |
687 | |
688 | if (j >= (argcount - default_args.size())) { |
689 | int dargidx = j - (argcount - default_args.size()); |
690 | d3["default_value" ] = default_args[dargidx].get_construct_string(); |
691 | } |
692 | arguments.push_back(d3); |
693 | } |
694 | |
695 | if (arguments.size()) { |
696 | d2["arguments" ] = arguments; |
697 | } |
698 | |
699 | methods.push_back(d2); |
700 | } |
701 | if (methods.size()) { |
702 | d["methods" ] = methods; |
703 | } |
704 | } |
705 | { |
706 | //constructors |
707 | Array constructors; |
708 | |
709 | for (int j = 0; j < Variant::get_constructor_count(type); j++) { |
710 | Dictionary d2; |
711 | d2["index" ] = j; |
712 | |
713 | Array arguments; |
714 | int argcount = Variant::get_constructor_argument_count(type, j); |
715 | for (int k = 0; k < argcount; k++) { |
716 | Dictionary d3; |
717 | d3["name" ] = Variant::get_constructor_argument_name(type, j, k); |
718 | d3["type" ] = get_builtin_or_variant_type_name(Variant::get_constructor_argument_type(type, j, k)); |
719 | arguments.push_back(d3); |
720 | } |
721 | if (arguments.size()) { |
722 | d2["arguments" ] = arguments; |
723 | } |
724 | constructors.push_back(d2); |
725 | } |
726 | |
727 | if (constructors.size()) { |
728 | d["constructors" ] = constructors; |
729 | } |
730 | } |
731 | { |
732 | //destructor |
733 | d["has_destructor" ] = Variant::has_destructor(type); |
734 | } |
735 | |
736 | builtins.push_back(d); |
737 | } |
738 | |
739 | api_dump["builtin_classes" ] = builtins; |
740 | } |
741 | |
742 | { |
743 | // classes |
744 | Array classes; |
745 | |
746 | List<StringName> class_list; |
747 | |
748 | ClassDB::get_class_list(&class_list); |
749 | |
750 | class_list.sort_custom<StringName::AlphCompare>(); |
751 | |
752 | for (const StringName &class_name : class_list) { |
753 | if (!ClassDB::is_class_exposed(class_name)) { |
754 | continue; |
755 | } |
756 | Dictionary d; |
757 | d["name" ] = String(class_name); |
758 | d["is_refcounted" ] = ClassDB::is_parent_class(class_name, "RefCounted" ); |
759 | d["is_instantiable" ] = ClassDB::can_instantiate(class_name); |
760 | StringName parent_class = ClassDB::get_parent_class(class_name); |
761 | if (parent_class != StringName()) { |
762 | d["inherits" ] = String(parent_class); |
763 | } |
764 | |
765 | { |
766 | ClassDB::APIType api = ClassDB::get_api_type(class_name); |
767 | static const char *api_type[5] = { "core" , "editor" , "extension" , "editor_extension" }; |
768 | d["api_type" ] = api_type[api]; |
769 | } |
770 | |
771 | { |
772 | //constants |
773 | Array constants; |
774 | List<String> constant_list; |
775 | ClassDB::get_integer_constant_list(class_name, &constant_list, true); |
776 | for (const String &F : constant_list) { |
777 | StringName enum_name = ClassDB::get_integer_constant_enum(class_name, F); |
778 | if (enum_name != StringName()) { |
779 | continue; //enums will be handled on their own |
780 | } |
781 | |
782 | Dictionary d2; |
783 | d2["name" ] = String(F); |
784 | d2["value" ] = ClassDB::get_integer_constant(class_name, F); |
785 | |
786 | constants.push_back(d2); |
787 | } |
788 | |
789 | if (constants.size()) { |
790 | d["constants" ] = constants; |
791 | } |
792 | } |
793 | { |
794 | //enum |
795 | Array enums; |
796 | List<StringName> enum_list; |
797 | ClassDB::get_enum_list(class_name, &enum_list, true); |
798 | for (const StringName &F : enum_list) { |
799 | Dictionary d2; |
800 | d2["name" ] = String(F); |
801 | d2["is_bitfield" ] = ClassDB::is_enum_bitfield(class_name, F); |
802 | |
803 | Array values; |
804 | List<StringName> enum_constant_list; |
805 | ClassDB::get_enum_constants(class_name, F, &enum_constant_list, true); |
806 | for (List<StringName>::Element *G = enum_constant_list.front(); G; G = G->next()) { |
807 | Dictionary d3; |
808 | d3["name" ] = String(G->get()); |
809 | d3["value" ] = ClassDB::get_integer_constant(class_name, G->get()); |
810 | values.push_back(d3); |
811 | } |
812 | |
813 | d2["values" ] = values; |
814 | |
815 | enums.push_back(d2); |
816 | } |
817 | |
818 | if (enums.size()) { |
819 | d["enums" ] = enums; |
820 | } |
821 | } |
822 | { |
823 | //methods |
824 | Array methods; |
825 | List<MethodInfo> method_list; |
826 | ClassDB::get_method_list(class_name, &method_list, true); |
827 | for (const MethodInfo &F : method_list) { |
828 | StringName method_name = F.name; |
829 | if ((F.flags & METHOD_FLAG_VIRTUAL) && !(F.flags & METHOD_FLAG_OBJECT_CORE)) { |
830 | //virtual method |
831 | const MethodInfo &mi = F; |
832 | Dictionary d2; |
833 | d2["name" ] = String(method_name); |
834 | d2["is_const" ] = (F.flags & METHOD_FLAG_CONST) ? true : false; |
835 | d2["is_static" ] = (F.flags & METHOD_FLAG_STATIC) ? true : false; |
836 | d2["is_vararg" ] = false; |
837 | d2["is_virtual" ] = true; |
838 | // virtual functions have no hash since no MethodBind is involved |
839 | bool has_return = mi.return_val.type != Variant::NIL || (mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT); |
840 | Array arguments; |
841 | for (int i = (has_return ? -1 : 0); i < mi.arguments.size(); i++) { |
842 | PropertyInfo pinfo = i == -1 ? mi.return_val : mi.arguments[i]; |
843 | Dictionary d3; |
844 | |
845 | if (i >= 0) { |
846 | d3["name" ] = pinfo.name; |
847 | } |
848 | |
849 | d3["type" ] = get_property_info_type_name(pinfo); |
850 | |
851 | if (mi.get_argument_meta(i) > 0) { |
852 | d3["meta" ] = get_type_meta_name((GodotTypeInfo::Metadata)mi.get_argument_meta(i)); |
853 | } |
854 | |
855 | if (i == -1) { |
856 | d2["return_value" ] = d3; |
857 | } else { |
858 | arguments.push_back(d3); |
859 | } |
860 | } |
861 | |
862 | if (arguments.size()) { |
863 | d2["arguments" ] = arguments; |
864 | } |
865 | |
866 | methods.push_back(d2); |
867 | |
868 | } else if (F.name.begins_with("_" )) { |
869 | //hidden method, ignore |
870 | |
871 | } else { |
872 | Dictionary d2; |
873 | d2["name" ] = String(method_name); |
874 | |
875 | MethodBind *method = ClassDB::get_method(class_name, method_name); |
876 | if (!method) { |
877 | continue; |
878 | } |
879 | |
880 | d2["is_const" ] = method->is_const(); |
881 | d2["is_vararg" ] = method->is_vararg(); |
882 | d2["is_static" ] = method->is_static(); |
883 | d2["is_virtual" ] = false; |
884 | d2["hash" ] = method->get_hash(); |
885 | |
886 | Vector<uint32_t> compat_hashes = ClassDB::get_method_compatibility_hashes(class_name, method_name); |
887 | if (compat_hashes.size()) { |
888 | Array compatibility; |
889 | for (int i = 0; i < compat_hashes.size(); i++) { |
890 | compatibility.push_back(compat_hashes[i]); |
891 | } |
892 | d2["hash_compatibility" ] = compatibility; |
893 | } |
894 | |
895 | Vector<Variant> default_args = method->get_default_arguments(); |
896 | |
897 | Array arguments; |
898 | for (int i = (method->has_return() ? -1 : 0); i < method->get_argument_count(); i++) { |
899 | PropertyInfo pinfo = i == -1 ? method->get_return_info() : method->get_argument_info(i); |
900 | Dictionary d3; |
901 | |
902 | if (i >= 0) { |
903 | d3["name" ] = pinfo.name; |
904 | } |
905 | d3["type" ] = get_property_info_type_name(pinfo); |
906 | |
907 | if (method->get_argument_meta(i) > 0) { |
908 | d3["meta" ] = get_type_meta_name(method->get_argument_meta(i)); |
909 | } |
910 | |
911 | if (i >= 0 && i >= (method->get_argument_count() - default_args.size())) { |
912 | int dargidx = i - (method->get_argument_count() - default_args.size()); |
913 | d3["default_value" ] = default_args[dargidx].get_construct_string(); |
914 | } |
915 | |
916 | if (i == -1) { |
917 | d2["return_value" ] = d3; |
918 | } else { |
919 | arguments.push_back(d3); |
920 | } |
921 | } |
922 | |
923 | if (arguments.size()) { |
924 | d2["arguments" ] = arguments; |
925 | } |
926 | |
927 | methods.push_back(d2); |
928 | } |
929 | } |
930 | |
931 | if (methods.size()) { |
932 | d["methods" ] = methods; |
933 | } |
934 | } |
935 | |
936 | { |
937 | //signals |
938 | Array signals; |
939 | List<MethodInfo> signal_list; |
940 | ClassDB::get_signal_list(class_name, &signal_list, true); |
941 | for (const MethodInfo &F : signal_list) { |
942 | StringName signal_name = F.name; |
943 | Dictionary d2; |
944 | d2["name" ] = String(signal_name); |
945 | |
946 | Array arguments; |
947 | |
948 | for (int i = 0; i < F.arguments.size(); i++) { |
949 | Dictionary d3; |
950 | d3["name" ] = F.arguments[i].name; |
951 | d3["type" ] = get_property_info_type_name(F.arguments[i]); |
952 | if (F.get_argument_meta(i) > 0) { |
953 | d3["meta" ] = get_type_meta_name((GodotTypeInfo::Metadata)F.get_argument_meta(i)); |
954 | } |
955 | arguments.push_back(d3); |
956 | } |
957 | if (arguments.size()) { |
958 | d2["arguments" ] = arguments; |
959 | } |
960 | |
961 | signals.push_back(d2); |
962 | } |
963 | |
964 | if (signals.size()) { |
965 | d["signals" ] = signals; |
966 | } |
967 | } |
968 | { |
969 | //properties |
970 | Array properties; |
971 | List<PropertyInfo> property_list; |
972 | ClassDB::get_property_list(class_name, &property_list, true); |
973 | for (const PropertyInfo &F : property_list) { |
974 | if (F.usage & PROPERTY_USAGE_CATEGORY || F.usage & PROPERTY_USAGE_GROUP || F.usage & PROPERTY_USAGE_SUBGROUP || (F.type == Variant::NIL && F.usage & PROPERTY_USAGE_ARRAY)) { |
975 | continue; //not real properties |
976 | } |
977 | if (F.name.begins_with("_" )) { |
978 | continue; //hidden property |
979 | } |
980 | if (F.name.find("/" ) >= 0) { |
981 | // Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector. |
982 | continue; |
983 | } |
984 | StringName property_name = F.name; |
985 | Dictionary d2; |
986 | d2["type" ] = get_property_info_type_name(F); |
987 | d2["name" ] = String(property_name); |
988 | StringName setter = ClassDB::get_property_setter(class_name, F.name); |
989 | if (!(setter == "" )) { |
990 | d2["setter" ] = setter; |
991 | } |
992 | StringName getter = ClassDB::get_property_getter(class_name, F.name); |
993 | if (!(getter == "" )) { |
994 | d2["getter" ] = getter; |
995 | } |
996 | int index = ClassDB::get_property_index(class_name, F.name); |
997 | if (index != -1) { |
998 | d2["index" ] = index; |
999 | } |
1000 | properties.push_back(d2); |
1001 | } |
1002 | |
1003 | if (properties.size()) { |
1004 | d["properties" ] = properties; |
1005 | } |
1006 | } |
1007 | |
1008 | classes.push_back(d); |
1009 | } |
1010 | |
1011 | api_dump["classes" ] = classes; |
1012 | } |
1013 | |
1014 | { |
1015 | // singletons |
1016 | |
1017 | Array singletons; |
1018 | List<Engine::Singleton> singleton_list; |
1019 | Engine::get_singleton()->get_singletons(&singleton_list); |
1020 | |
1021 | for (const Engine::Singleton &s : singleton_list) { |
1022 | Dictionary d; |
1023 | d["name" ] = s.name; |
1024 | if (s.class_name != StringName()) { |
1025 | d["type" ] = String(s.class_name); |
1026 | } else { |
1027 | d["type" ] = String(s.ptr->get_class()); |
1028 | } |
1029 | singletons.push_back(d); |
1030 | } |
1031 | |
1032 | if (singletons.size()) { |
1033 | api_dump["singletons" ] = singletons; |
1034 | } |
1035 | } |
1036 | |
1037 | { |
1038 | Array native_structures; |
1039 | |
1040 | List<StringName> native_structs; |
1041 | ClassDB::get_native_struct_list(&native_structs); |
1042 | native_structs.sort_custom<StringName::AlphCompare>(); |
1043 | |
1044 | for (const StringName &E : native_structs) { |
1045 | String code = ClassDB::get_native_struct_code(E); |
1046 | |
1047 | Dictionary d; |
1048 | d["name" ] = String(E); |
1049 | d["format" ] = code; |
1050 | |
1051 | native_structures.push_back(d); |
1052 | } |
1053 | |
1054 | api_dump["native_structures" ] = native_structures; |
1055 | } |
1056 | |
1057 | return api_dump; |
1058 | } |
1059 | |
1060 | void GDExtensionAPIDump::generate_extension_json_file(const String &p_path) { |
1061 | Dictionary api = generate_extension_api(); |
1062 | Ref<JSON> json; |
1063 | json.instantiate(); |
1064 | |
1065 | String text = json->stringify(api, "\t" , false) + "\n" ; |
1066 | Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE); |
1067 | ERR_FAIL_COND_MSG(fa.is_null(), vformat("Cannot open file '%s' for writing." , p_path)); |
1068 | fa->store_string(text); |
1069 | } |
1070 | |
1071 | static bool compare_value(const String &p_path, const String &p_field, const Variant &p_old_value, const Variant &p_new_value, bool p_allow_name_change) { |
1072 | bool failed = false; |
1073 | String path = p_path + "/" + p_field; |
1074 | if (p_old_value.get_type() == Variant::ARRAY && p_new_value.get_type() == Variant::ARRAY) { |
1075 | Array old_array = p_old_value; |
1076 | Array new_array = p_new_value; |
1077 | if (!compare_value(path, "size" , old_array.size(), new_array.size(), p_allow_name_change)) { |
1078 | failed = true; |
1079 | } |
1080 | for (int i = 0; i < old_array.size() && i < new_array.size(); i++) { |
1081 | if (!compare_value(path, itos(i), old_array[i], new_array[i], p_allow_name_change)) { |
1082 | failed = true; |
1083 | } |
1084 | } |
1085 | } else if (p_old_value.get_type() == Variant::DICTIONARY && p_new_value.get_type() == Variant::DICTIONARY) { |
1086 | Dictionary old_dict = p_old_value; |
1087 | Dictionary new_dict = p_new_value; |
1088 | Array old_keys = old_dict.keys(); |
1089 | for (int i = 0; i < old_keys.size(); i++) { |
1090 | Variant key = old_keys[i]; |
1091 | if (!new_dict.has(key)) { |
1092 | failed = true; |
1093 | print_error(vformat("Validate extension JSON: Error: Field '%s': %s was removed." , p_path, key)); |
1094 | continue; |
1095 | } |
1096 | if (p_allow_name_change && key == "name" ) { |
1097 | continue; |
1098 | } |
1099 | if (!compare_value(path, key, old_dict[key], new_dict[key], p_allow_name_change)) { |
1100 | failed = true; |
1101 | } |
1102 | } |
1103 | Array new_keys = old_dict.keys(); |
1104 | for (int i = 0; i < new_keys.size(); i++) { |
1105 | Variant key = new_keys[i]; |
1106 | if (!old_dict.has(key)) { |
1107 | failed = true; |
1108 | print_error(vformat("Validate extension JSON: Error: Field '%s': %s was added with value %s." , p_path, key, new_dict[key])); |
1109 | } |
1110 | } |
1111 | } else { |
1112 | bool equal = Variant::evaluate(Variant::OP_EQUAL, p_old_value, p_new_value); |
1113 | if (!equal) { |
1114 | print_error(vformat("Validate extension JSON: Error: Field '%s': %s changed value in new API, from %s to %s." , p_path, p_field, p_old_value.get_construct_string(), p_new_value.get_construct_string())); |
1115 | return false; |
1116 | } |
1117 | } |
1118 | return !failed; |
1119 | } |
1120 | |
1121 | static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_new_api, const String &p_base_array, const String &p_name_field, const Vector<String> &p_fields_to_compare, bool p_compare_hashes, const String &p_outer_class = String(), bool p_compare_operators = false, bool p_compare_enum_value = false) { |
1122 | String base_array = p_outer_class + p_base_array; |
1123 | if (!p_old_api.has(p_base_array)) { |
1124 | return true; // May just not have this array and its still good. Probably added recently. |
1125 | } |
1126 | bool failed = false; |
1127 | ERR_FAIL_COND_V_MSG(!p_new_api.has(p_base_array), false, "New API lacks base array: " + p_base_array); |
1128 | Array new_api = p_new_api[p_base_array]; |
1129 | HashMap<String, Dictionary> new_api_assoc; |
1130 | |
1131 | for (int i = 0; i < new_api.size(); i++) { |
1132 | Dictionary elem = new_api[i]; |
1133 | ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug." , base_array, p_name_field)); |
1134 | String name = elem[p_name_field]; |
1135 | if (p_compare_operators && elem.has("right_type" )) { |
1136 | name += " " + String(elem["right_type" ]); |
1137 | } |
1138 | new_api_assoc.insert(name, elem); |
1139 | } |
1140 | |
1141 | Array old_api = p_old_api[p_base_array]; |
1142 | for (int i = 0; i < old_api.size(); i++) { |
1143 | Dictionary old_elem = old_api[i]; |
1144 | if (!old_elem.has(p_name_field)) { |
1145 | failed = true; |
1146 | print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'." , base_array, p_name_field)); |
1147 | continue; |
1148 | } |
1149 | String name = old_elem[p_name_field]; |
1150 | if (p_compare_operators && old_elem.has("right_type" )) { |
1151 | name += " " + String(old_elem["right_type" ]); |
1152 | } |
1153 | if (!new_api_assoc.has(name)) { |
1154 | failed = true; |
1155 | print_error(vformat("Validate extension JSON: API was removed: %s/%s" , base_array, name)); |
1156 | continue; |
1157 | } |
1158 | |
1159 | Dictionary new_elem = new_api_assoc[name]; |
1160 | |
1161 | for (int j = 0; j < p_fields_to_compare.size(); j++) { |
1162 | String field = p_fields_to_compare[j]; |
1163 | bool optional = field.begins_with("*" ); |
1164 | if (optional) { |
1165 | // This is an optional field, but if exists it has to exist in both. |
1166 | field = field.substr(1, field.length()); |
1167 | } |
1168 | |
1169 | bool added = field.begins_with("+" ); |
1170 | if (added) { |
1171 | // Meaning this field must either exist or contents may not exist. |
1172 | field = field.substr(1, field.length()); |
1173 | } |
1174 | |
1175 | bool enum_values = field.begins_with("$" ); |
1176 | if (enum_values) { |
1177 | // Meaning this field is a list of enum values. |
1178 | field = field.substr(1, field.length()); |
1179 | } |
1180 | |
1181 | bool allow_name_change = field.begins_with("@" ); |
1182 | if (allow_name_change) { |
1183 | // Meaning that when structurally comparing the old and new value, the dictionary entry 'name' may change. |
1184 | field = field.substr(1, field.length()); |
1185 | } |
1186 | |
1187 | Variant old_value; |
1188 | |
1189 | if (!old_elem.has(field)) { |
1190 | if (optional) { |
1191 | if (new_elem.has(field)) { |
1192 | failed = true; |
1193 | print_error(vformat("Validate extension JSON: JSON file: Field was added in a way that breaks compatibility '%s/%s': %s" , base_array, name, field)); |
1194 | } |
1195 | } else if (added && new_elem.has(field)) { |
1196 | // Should be ok, field now exists, should not be verified in prior versions where it does not. |
1197 | } else { |
1198 | failed = true; |
1199 | print_error(vformat("Validate extension JSON: JSON file: Missing field in '%s/%s': %s" , base_array, name, field)); |
1200 | } |
1201 | continue; |
1202 | } else { |
1203 | old_value = old_elem[field]; |
1204 | } |
1205 | |
1206 | if (!new_elem.has(field)) { |
1207 | failed = true; |
1208 | ERR_PRINT(vformat("Validate extension JSON: Missing field in current API '%s/%s': %s. This is a bug." , base_array, name, field)); |
1209 | continue; |
1210 | } |
1211 | |
1212 | Variant new_value = new_elem[field]; |
1213 | |
1214 | if (p_compare_enum_value && name.ends_with("_MAX" )) { |
1215 | if (static_cast<int64_t>(new_value) > static_cast<int64_t>(old_value)) { |
1216 | // Ignore the _MAX value of an enum increasing. |
1217 | continue; |
1218 | } |
1219 | } |
1220 | if (enum_values) { |
1221 | if (!compare_dict_array(old_elem, new_elem, field, "name" , { "value" }, false, base_array + "/" + name + "/" , false, true)) { |
1222 | failed = true; |
1223 | } |
1224 | } else if (!compare_value(base_array + "/" + name, field, old_value, new_value, allow_name_change)) { |
1225 | failed = true; |
1226 | } |
1227 | } |
1228 | |
1229 | if (p_compare_hashes) { |
1230 | if (!old_elem.has("hash" )) { |
1231 | if (old_elem.has("is_virtual" ) && bool(old_elem["is_virtual" ]) && !new_elem.has("hash" )) { |
1232 | continue; // No hash for virtual methods, go on. |
1233 | } |
1234 | |
1235 | failed = true; |
1236 | print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: 'hash'." , base_array)); |
1237 | continue; |
1238 | } |
1239 | |
1240 | uint64_t old_hash = old_elem["hash" ]; |
1241 | |
1242 | if (!new_elem.has("hash" )) { |
1243 | failed = true; |
1244 | print_error(vformat("Validate extension JSON: Error: Field '%s' is missing the field: 'hash'." , base_array)); |
1245 | continue; |
1246 | } |
1247 | |
1248 | uint64_t new_hash = new_elem["hash" ]; |
1249 | bool hash_found = false; |
1250 | if (old_hash == new_hash) { |
1251 | hash_found = true; |
1252 | } else if (new_elem.has("hash_compatibility" )) { |
1253 | Array compatibility = new_elem["hash_compatibility" ]; |
1254 | for (int j = 0; j < compatibility.size(); j++) { |
1255 | new_hash = compatibility[j]; |
1256 | if (new_hash == old_hash) { |
1257 | hash_found = true; |
1258 | break; |
1259 | } |
1260 | } |
1261 | } |
1262 | |
1263 | if (!hash_found) { |
1264 | failed = true; |
1265 | print_error(vformat("Validate extension JSON: Error: Hash changed for '%s/%s', from %08X to %08X. This means that the function has changed and no compatibility function was provided." , base_array, name, old_hash, new_hash)); |
1266 | continue; |
1267 | } |
1268 | } |
1269 | } |
1270 | |
1271 | return !failed; |
1272 | } |
1273 | |
1274 | static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered, const String &p_outer, const String &p_outer_name, const Dictionary &p_old_api, const Dictionary &p_new_api, const String &p_base_array, const String &p_name_field, const Vector<String> &p_fields_to_compare, bool p_compare_hashes, bool p_compare_operators = false) { |
1275 | if (!p_old_api.has(p_outer)) { |
1276 | return true; // May just not have this array and its still good. Probably added recently or optional. |
1277 | } |
1278 | bool failed = false; |
1279 | ERR_FAIL_COND_V_MSG(!p_new_api.has(p_outer), false, "New API lacks base array: " + p_outer); |
1280 | Array new_api = p_new_api[p_outer]; |
1281 | HashMap<String, Dictionary> new_api_assoc; |
1282 | |
1283 | for (int i = 0; i < new_api.size(); i++) { |
1284 | Dictionary elem = new_api[i]; |
1285 | ERR_FAIL_COND_V_MSG(!elem.has(p_outer_name), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug." , p_outer, p_outer_name)); |
1286 | new_api_assoc.insert(elem[p_outer_name], elem); |
1287 | } |
1288 | |
1289 | Array old_api = p_old_api[p_outer]; |
1290 | |
1291 | for (int i = 0; i < old_api.size(); i++) { |
1292 | Dictionary old_elem = old_api[i]; |
1293 | if (!old_elem.has(p_outer_name)) { |
1294 | failed = true; |
1295 | print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'." , p_outer, p_outer_name)); |
1296 | continue; |
1297 | } |
1298 | String name = old_elem[p_outer_name]; |
1299 | if (!new_api_assoc.has(name)) { |
1300 | failed = true; |
1301 | if (!r_removed_classes_registered.has(name)) { |
1302 | print_error(vformat("Validate extension JSON: API was removed: %s/%s" , p_outer, name)); |
1303 | r_removed_classes_registered.insert(name); |
1304 | } |
1305 | continue; |
1306 | } |
1307 | |
1308 | Dictionary new_elem = new_api_assoc[name]; |
1309 | |
1310 | if (!compare_dict_array(old_elem, new_elem, p_base_array, p_name_field, p_fields_to_compare, p_compare_hashes, p_outer + "/" + name + "/" , p_compare_operators)) { |
1311 | failed = true; |
1312 | } |
1313 | } |
1314 | |
1315 | return !failed; |
1316 | } |
1317 | |
1318 | Error GDExtensionAPIDump::validate_extension_json_file(const String &p_path) { |
1319 | Error error; |
1320 | String text = FileAccess::get_file_as_string(p_path, &error); |
1321 | if (error != OK) { |
1322 | ERR_PRINT(vformat("Validate extension JSON: Could not open file '%s'." , p_path)); |
1323 | return error; |
1324 | } |
1325 | |
1326 | Ref<JSON> json; |
1327 | json.instantiate(); |
1328 | error = json->parse(text); |
1329 | if (error != OK) { |
1330 | ERR_PRINT(vformat("Validate extension JSON: Error parsing '%s' at line %d: %s" , p_path, json->get_error_line(), json->get_error_message())); |
1331 | return error; |
1332 | } |
1333 | |
1334 | Dictionary old_api = json->get_data(); |
1335 | Dictionary new_api = generate_extension_api(); |
1336 | |
1337 | { // Validate header: |
1338 | Dictionary = old_api["header" ]; |
1339 | ERR_FAIL_COND_V(!header.has("version_major" ), ERR_INVALID_DATA); |
1340 | ERR_FAIL_COND_V(!header.has("version_minor" ), ERR_INVALID_DATA); |
1341 | int major = header["version_major" ]; |
1342 | int minor = header["version_minor" ]; |
1343 | |
1344 | ERR_FAIL_COND_V_MSG(major != VERSION_MAJOR, ERR_INVALID_DATA, vformat("JSON API dump is for a different engine version (%d) than this one (%d)" , major, VERSION_MAJOR)); |
1345 | ERR_FAIL_COND_V_MSG(minor > VERSION_MINOR, ERR_INVALID_DATA, vformat("JSON API dump is for a newer version of the engine: %d.%d" , major, minor)); |
1346 | } |
1347 | |
1348 | bool failed = false; |
1349 | |
1350 | HashSet<String> removed_classes_registered; |
1351 | |
1352 | if (!compare_dict_array(old_api, new_api, "global_constants" , "name" , Vector<String>({ "value" , "is_bitfield" }), false)) { |
1353 | failed = true; |
1354 | } |
1355 | |
1356 | if (!compare_dict_array(old_api, new_api, "global_enums" , "name" , Vector<String>({ "$values" , "is_bitfield" }), false)) { |
1357 | failed = true; |
1358 | } |
1359 | |
1360 | if (!compare_dict_array(old_api, new_api, "utility_functions" , "name" , Vector<String>({ "category" , "is_vararg" , "*return_type" , "*@arguments" }), true)) { |
1361 | failed = true; |
1362 | } |
1363 | |
1364 | if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes" , "name" , old_api, new_api, "members" , "name" , { "type" }, false)) { |
1365 | failed = true; |
1366 | } |
1367 | |
1368 | if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes" , "name" , old_api, new_api, "constants" , "name" , { "type" , "value" }, false)) { |
1369 | failed = true; |
1370 | } |
1371 | |
1372 | if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes" , "name" , old_api, new_api, "operators" , "name" , { "return_type" }, false, true)) { |
1373 | failed = true; |
1374 | } |
1375 | |
1376 | if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes" , "name" , old_api, new_api, "methods" , "name" , { "is_vararg" , "is_static" , "is_const" , "*return_type" , "*@arguments" }, true)) { |
1377 | failed = true; |
1378 | } |
1379 | |
1380 | if (!compare_sub_dict_array(removed_classes_registered, "builtin_classes" , "name" , old_api, new_api, "constructors" , "index" , { "*@arguments" }, false)) { |
1381 | failed = true; |
1382 | } |
1383 | |
1384 | if (!compare_sub_dict_array(removed_classes_registered, "classes" , "name" , old_api, new_api, "constants" , "name" , { "value" }, false)) { |
1385 | failed = true; |
1386 | } |
1387 | |
1388 | if (!compare_sub_dict_array(removed_classes_registered, "classes" , "name" , old_api, new_api, "enums" , "name" , { "is_bitfield" , "$values" }, false)) { |
1389 | failed = true; |
1390 | } |
1391 | |
1392 | if (!compare_sub_dict_array(removed_classes_registered, "classes" , "name" , old_api, new_api, "methods" , "name" , { "is_virtual" , "is_vararg" , "is_static" , "is_const" , "*return_value" , "*@arguments" }, true)) { |
1393 | failed = true; |
1394 | } |
1395 | |
1396 | if (!compare_sub_dict_array(removed_classes_registered, "classes" , "name" , old_api, new_api, "signals" , "name" , { "*@arguments" }, false)) { |
1397 | failed = true; |
1398 | } |
1399 | |
1400 | if (!compare_sub_dict_array(removed_classes_registered, "classes" , "name" , old_api, new_api, "properties" , "name" , { "type" , "*setter" , "*getter" , "*index" }, false)) { |
1401 | failed = true; |
1402 | } |
1403 | |
1404 | if (!compare_dict_array(old_api, new_api, "singletons" , "name" , Vector<String>({ "type" }), false)) { |
1405 | failed = true; |
1406 | } |
1407 | |
1408 | if (!compare_dict_array(old_api, new_api, "native_structures" , "name" , Vector<String>({ "format" }), false)) { |
1409 | failed = true; |
1410 | } |
1411 | |
1412 | if (failed) { |
1413 | return ERR_INVALID_DATA; |
1414 | } else { |
1415 | return OK; |
1416 | } |
1417 | } |
1418 | |
1419 | #endif // TOOLS_ENABLED |
1420 | |