11from typing import List , Dict , Tuple , Optional
22from collections import OrderedDict
3- from dataclasses import dataclass
3+ from dataclasses import dataclass , replace
44from pathlib import Path
55import json
66from enum import Enum
77
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 *
2012
2113
2214class BuildConfig (Enum ):
@@ -34,23 +26,15 @@ def parse(cls, item: dict) -> "GlobalConstantSpec":
3426 raise NotImplementedError
3527
3628
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+ )
5438
5539
5640@dataclass
@@ -147,34 +131,37 @@ class ExtensionApi:
147131 version_build : str # e.g. "official"
148132 version_full_name : str # e.g. "Godot Engine v4.0.alpha13.official"
149133
150- variant_size : int
151-
152- classes : List [ClassSpec ]
153- builtins : List [BuiltinSpec ]
134+ classes : List [ClassTypeSpec ]
135+ builtins : List [BuiltinTypeSpec ]
154136 global_constants : List [GlobalConstantSpec ]
155- global_enums : List [GlobalEnumSpec ]
137+ global_enums : List [EnumTypeSpec ]
156138 utility_functions : List [UtilityFunctionSpec ]
157139 singletons : List [SingletonSpec ]
158140 native_structures : List [NativeStructureSpec ]
159141
160- # Expose scalars
142+ # Expose scalars, nil and variant
161143
162144 @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 ):
164154 return TYPES_DB ["bool" ]
165- # return next(builtin for builtin in self.builtins if builtin.name == "int")
166155
167156 @property
168- def int_spec (self ):
157+ def int_type (self ):
169158 return TYPES_DB ["int" ]
170- # return next(builtin for builtin in self.builtins if builtin.name == "int")
171159
172160 @property
173- def float_spec (self ):
161+ def float_type (self ):
174162 return TYPES_DB ["float" ]
175- # return next(builtin for builtin in self.builtins if builtin.name == "int")
176163
177- def get_class_meth_hash (self , classname : str , methname : str ) -> int :
164+ def get_class_meth_hash (self , classname : str , methname : str ) -> Optional [ int ] :
178165 klass = next (c for c in self .classes if c .original_name == classname )
179166 meth = next (m for m in klass .methods if m .original_name == methname )
180167 return meth .hash
@@ -235,65 +222,101 @@ def merge_builtins_size_info(api_json: dict, build_config: BuildConfig) -> None:
235222 else :
236223 raise RuntimeError (f"Member `{ member } ` doesn't seem to be part of `{ name } ` !" )
237224
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 .
240227 api_json ["variant_size" ] = builtin_class_sizes ["Variant" ]
228+ api_json ["object_size" ] = builtin_class_sizes ["Object" ]
241229
242230
243- def order_classes (classes : List [ClassSpec ]) -> List [ClassSpec ]:
231+ def order_classes (classes : List [ClassTypeSpec ]) -> List [ClassTypeSpec ]:
244232 # Order classes by inheritance dependency needs
245233 ordered_classes = OrderedDict () # Makes it explicit we need ordering here !
246234 ordered_count = 0
247235
248236 while len (classes ) != len (ordered_classes ):
249237 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
252240
253241 # Sanity check to avoid infinite loop in case of error in `extension_api.json`
254242 if ordered_count == len (ordered_classes ):
255243 bad_class = next (
256244 klass
257245 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
259247 )
260248 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 } `"
262250 )
263251 ordered_count = len (ordered_classes )
264252
265253 return list (ordered_classes .values ())
266254
267255
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 :
269259 api_json = json .loads (path .read_text (encoding = "utf8" ))
270260 assert isinstance (api_json , dict )
271261
272262 merge_builtins_size_info (api_json , build_config )
273263
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+
274306 api = ExtensionApi (
275307 version_major = api_json ["header" ]["version_major" ],
276308 version_minor = api_json ["header" ]["version_minor" ],
277309 version_patch = api_json ["header" ]["version_patch" ],
278310 version_status = api_json ["header" ]["version_status" ],
279311 version_build = api_json ["header" ]["version_build" ],
280312 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 ,
284315 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 ,
286317 utility_functions = [UtilityFunctionSpec .parse (x ) for x in api_json ["utility_functions" ]],
287318 singletons = [SingletonSpec .parse (x ) for x in api_json ["singletons" ]],
288319 native_structures = [NativeStructureSpec .parse (x ) for x in api_json ["native_structures" ]],
289320 )
290321
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-
299322 return api
0 commit comments