1
1
from typing import List , Dict , Tuple , Optional
2
2
from collections import OrderedDict
3
- from dataclasses import dataclass
3
+ from dataclasses import dataclass , replace
4
4
from pathlib import Path
5
5
import json
6
6
from enum import Enum
7
7
8
- from .builtins import BuiltinSpec
9
- from .classes import ClassSpec
10
- from .type import (
11
- TYPES_DB ,
12
- TypeInUse ,
13
- ValueInUse ,
14
- register_variant_in_types_db ,
15
- register_builtins_in_types_db ,
16
- register_classes_in_types_db ,
17
- register_global_enums_in_types_db ,
18
- )
19
- from .utils import correct_name , assert_api_consistency
8
+ from .type_spec import *
9
+ from .builtins import *
10
+ from .classes import *
11
+ from .utils import *
20
12
21
13
22
14
class BuildConfig (Enum ):
@@ -34,23 +26,15 @@ def parse(cls, item: dict) -> "GlobalConstantSpec":
34
26
raise NotImplementedError
35
27
36
28
37
- @dataclass
38
- class GlobalEnumSpec :
39
- original_name : str
40
- name : str
41
- values : Dict [str , int ]
42
-
43
- @classmethod
44
- def parse (cls , item : dict ) -> "GlobalEnumSpec" :
45
- item .setdefault ("original_name" , item ["name" ])
46
- # Fix `Variant.Operator` & `Variant.Type`
47
- item ["name" ] = item ["name" ].replace ("." , "" )
48
- assert_api_consistency (cls , item )
49
- return cls (
50
- name = correct_name (item ["name" ]),
51
- original_name = item ["original_name" ],
52
- values = {x ["name" ]: x ["value" ] for x in item ["values" ]},
53
- )
29
+ def parse_global_enum (spec : dict ) -> EnumTypeSpec :
30
+ assert spec .keys () == {"name" , "values" }, spec .keys ()
31
+ return EnumTypeSpec (
32
+ original_name = spec ["name" ],
33
+ py_type = spec ["name" ],
34
+ cy_type = spec ["name" ],
35
+ is_bitfield = False ,
36
+ values = {x ["name" ]: x ["value" ] for x in spec ["values" ]},
37
+ )
54
38
55
39
56
40
@dataclass
@@ -147,32 +131,35 @@ class ExtensionApi:
147
131
version_build : str # e.g. "official"
148
132
version_full_name : str # e.g. "Godot Engine v4.0.alpha13.official"
149
133
150
- variant_size : int
151
-
152
- classes : List [ClassSpec ]
153
- builtins : List [BuiltinSpec ]
134
+ classes : List [ClassTypeSpec ]
135
+ builtins : List [BuiltinTypeSpec ]
154
136
global_constants : List [GlobalConstantSpec ]
155
- global_enums : List [GlobalEnumSpec ]
137
+ global_enums : List [EnumTypeSpec ]
156
138
utility_functions : List [UtilityFunctionSpec ]
157
139
singletons : List [SingletonSpec ]
158
140
native_structures : List [NativeStructureSpec ]
159
141
160
- # Expose scalars
142
+ # Expose scalars, nil and variant
161
143
162
144
@property
163
- def bool_spec (self ):
145
+ def variant_type (self ):
146
+ return TYPES_DB ["Variant" ]
147
+
148
+ @property
149
+ def nil_type (self ):
150
+ return TYPES_DB ["Nil" ]
151
+
152
+ @property
153
+ def bool_type (self ):
164
154
return TYPES_DB ["bool" ]
165
- # return next(builtin for builtin in self.builtins if builtin.name == "int")
166
155
167
156
@property
168
- def int_spec (self ):
157
+ def int_type (self ):
169
158
return TYPES_DB ["int" ]
170
- # return next(builtin for builtin in self.builtins if builtin.name == "int")
171
159
172
160
@property
173
- def float_spec (self ):
161
+ def float_type (self ):
174
162
return TYPES_DB ["float" ]
175
- # return next(builtin for builtin in self.builtins if builtin.name == "int")
176
163
177
164
def get_class_meth_hash (self , classname : str , methname : str ) -> int :
178
165
klass = next (c for c in self .classes if c .original_name == classname )
@@ -235,65 +222,101 @@ def merge_builtins_size_info(api_json: dict, build_config: BuildConfig) -> None:
235
222
else :
236
223
raise RuntimeError (f"Member `{ member } ` doesn't seem to be part of `{ name } ` !" )
237
224
238
- # Variant is not present among the `builtin_classes`, only it size is provided.
239
- # So we have to create our own custom entry for this value .
225
+ # Variant&Object are not present among the `builtin_classes`, only their size is provided.
226
+ # So we have to create our own custom entry for them .
240
227
api_json ["variant_size" ] = builtin_class_sizes ["Variant" ]
228
+ api_json ["object_size" ] = builtin_class_sizes ["Object" ]
241
229
242
230
243
- def order_classes (classes : List [ClassSpec ]) -> List [ClassSpec ]:
231
+ def order_classes (classes : List [ClassTypeSpec ]) -> List [ClassTypeSpec ]:
244
232
# Order classes by inheritance dependency needs
245
233
ordered_classes = OrderedDict () # Makes it explicit we need ordering here !
246
234
ordered_count = 0
247
235
248
236
while len (classes ) != len (ordered_classes ):
249
237
for klass in classes :
250
- if klass .inherits is None or klass .inherits in ordered_classes :
251
- ordered_classes [klass .name ] = klass
238
+ if klass .inherits is None or klass .inherits . type_name in ordered_classes :
239
+ ordered_classes [klass .original_name ] = klass
252
240
253
241
# Sanity check to avoid infinite loop in case of error in `extension_api.json`
254
242
if ordered_count == len (ordered_classes ):
255
243
bad_class = next (
256
244
klass
257
245
for klass in classes
258
- if klass .inherits is not None and klass .inherits not in ordered_classes
246
+ if klass .inherits is not None and klass .inherits . type_name not in ordered_classes
259
247
)
260
248
raise RuntimeError (
261
- f"Class `{ bad_class .name } ` inherits of unknown class `{ bad_class .inherits } `"
249
+ f"Class `{ bad_class .original_name } ` inherits of unknown class `{ bad_class .inherits . type_name } `"
262
250
)
263
251
ordered_count = len (ordered_classes )
264
252
265
253
return list (ordered_classes .values ())
266
254
267
255
268
- def parse_extension_api_json (path : Path , build_config : BuildConfig ) -> ExtensionApi :
256
+ def parse_extension_api_json (
257
+ path : Path , build_config : BuildConfig , skip_classes : bool = False
258
+ ) -> ExtensionApi :
269
259
api_json = json .loads (path .read_text (encoding = "utf8" ))
270
260
assert isinstance (api_json , dict )
271
261
272
262
merge_builtins_size_info (api_json , build_config )
273
263
264
+ # Not much info about variant
265
+ variant_type = VariantTypeSpec (size = api_json ["variant_size" ])
266
+ TYPES_DB_REGISTER_TYPE ("Variant" , variant_type )
267
+
268
+ # Unlike int type that is always 8 bytes long, float depends on config
269
+ if build_config in (BuildConfig .DOUBLE_32 , BuildConfig .DOUBLE_64 ):
270
+ real_type = replace (TYPES_DB [f"meta:float" ], original_name = "float" )
271
+ else :
272
+ real_type = replace (TYPES_DB [f"meta:double" ], original_name = "float" )
273
+ TYPES_DB_REGISTER_TYPE ("float" , real_type )
274
+
275
+ def _register_enums (enums , parent_id = None ):
276
+ for enum_type in enums :
277
+ classifier = "bitfield" if enum_type .is_bitfield else "enum"
278
+ if parent_id :
279
+ type_id = f"{ classifier } ::{ parent_id } .{ enum_type .original_name } "
280
+ else :
281
+ type_id = f"{ classifier } ::{ enum_type .original_name } "
282
+ TYPES_DB_REGISTER_TYPE (type_id , enum_type )
283
+
284
+ builtins = parse_builtins_ignore_scalars_and_nil (api_json ["builtin_classes" ])
285
+ for builtin_type in builtins :
286
+ TYPES_DB_REGISTER_TYPE (builtin_type .original_name , builtin_type )
287
+ _register_enums (builtin_type .enums , parent_id = builtin_type .original_name )
288
+
289
+ # Parsing classes takes ~75% of the time while not being needed to render builtins stuff
290
+ if skip_classes :
291
+ # Only keep Object root class that is always needed
292
+ api_json ["classes" ] = [next (k for k in api_json ["classes" ] if k ["name" ] == "Object" )]
293
+
294
+ classes = order_classes (
295
+ [parse_class (x , object_size = api_json ["object_size" ]) for x in api_json ["classes" ]]
296
+ )
297
+ for class_type in classes :
298
+ TYPES_DB_REGISTER_TYPE (class_type .original_name , class_type )
299
+ _register_enums (class_type .enums , parent_id = class_type .original_name )
300
+
301
+ global_enums = [parse_global_enum (x ) for x in api_json ["global_enums" ]]
302
+ _register_enums (global_enums )
303
+
304
+ ensure_types_db_consistency ()
305
+
274
306
api = ExtensionApi (
275
307
version_major = api_json ["header" ]["version_major" ],
276
308
version_minor = api_json ["header" ]["version_minor" ],
277
309
version_patch = api_json ["header" ]["version_patch" ],
278
310
version_status = api_json ["header" ]["version_status" ],
279
311
version_build = api_json ["header" ]["version_build" ],
280
312
version_full_name = api_json ["header" ]["version_full_name" ],
281
- variant_size = api_json ["variant_size" ],
282
- classes = order_classes ([ClassSpec .parse (x ) for x in api_json ["classes" ]]),
283
- builtins = [BuiltinSpec .parse (x ) for x in api_json ["builtin_classes" ]],
313
+ classes = classes ,
314
+ builtins = builtins ,
284
315
global_constants = [GlobalConstantSpec .parse (x ) for x in api_json ["global_constants" ]],
285
- global_enums = [ GlobalEnumSpec . parse ( x ) for x in api_json [ " global_enums" ]] ,
316
+ global_enums = global_enums ,
286
317
utility_functions = [UtilityFunctionSpec .parse (x ) for x in api_json ["utility_functions" ]],
287
318
singletons = [SingletonSpec .parse (x ) for x in api_json ["singletons" ]],
288
319
native_structures = [NativeStructureSpec .parse (x ) for x in api_json ["native_structures" ]],
289
320
)
290
321
291
- # This is the kind-of ugly part where we register in a global dict the types
292
- # we've just parsed (so that they could be lazily retreived from all the
293
- # `TypeInUse` that reference them)
294
- register_variant_in_types_db (api .variant_size )
295
- register_builtins_in_types_db (api .builtins )
296
- register_classes_in_types_db (api .classes )
297
- register_global_enums_in_types_db (api .global_enums )
298
-
299
322
return api
0 commit comments