| 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 | |