Skip to content

Commit 3faea09

Browse files
committed
wip
1 parent d358b32 commit 3faea09

24 files changed

+1138
-2585
lines changed

Diff for: scripts/extension_api_parser/__main__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pathlib import Path
88

99
from . import parse_extension_api_json, BuildConfig
10+
from .type_spec import TYPES_DB
1011

1112

1213
try:
@@ -26,5 +27,8 @@
2627

2728

2829
for build_config in build_configs:
30+
initial_types_db = TYPES_DB.copy()
2931
print(f"Checking {extension_api_path} with config {build_config.value}")
30-
parse_extension_api_json(extension_api_path, build_config)
32+
parse_extension_api_json(extension_api_path, build_config, skip_classes=False)
33+
TYPES_DB.clear()
34+
TYPES_DB.update(initial_types_db)

Diff for: scripts/extension_api_parser/api.py

+85-62
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,14 @@
11
from typing import List, Dict, Tuple, Optional
22
from collections import OrderedDict
3-
from dataclasses import dataclass
3+
from dataclasses import dataclass, replace
44
from pathlib import Path
55
import json
66
from 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

2214
class 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,32 +131,35 @@ 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

177164
def get_class_meth_hash(self, classname: str, methname: str) -> int:
178165
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:
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

Comments
 (0)