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
42static 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
50static 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
85static 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
90Dictionary GDExtensionAPIDump::generate_extension_api() {
91 Dictionary api_dump;
92
93 {
94 //header
95 Dictionary header;
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
1060void 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
1071static 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
1121static 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
1274static 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
1318Error 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 header = 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