Skip to content

Commit 6118e9a

Browse files
committed
initial go at it
1 parent bc2fd55 commit 6118e9a

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/win32json

go

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Steps
4+
# --------------------------------------------------------------------------------
5+
# * identify all the "direct ApiRef dependencies" for every type
6+
# * using these direct dependnecies, identify which types have dependencies
7+
# that escape the current Api, then come back
8+
# * all types in this *cycle*, should be moved to a core json file
9+
10+
import os
11+
import sys
12+
import json
13+
from typing import List, Set, Dict
14+
15+
def apiRefTypeName(type_obj):
16+
return ".".join(type_obj["Parents"] + [type_obj["Name"]])
17+
18+
#def apiRefName(type_obj):
19+
# return type_obj["Api"] + ":" + apiRefTypeName(type_obj)
20+
21+
class DefaultDict(dict):
22+
def __init__(self, factory):
23+
self.factory = factory
24+
def __missing__(self, key):
25+
self[key] = self.factory(key)
26+
return self[key]
27+
28+
29+
#class TypeRef:
30+
# def __init__(self, referenced_by, type_reference):
31+
# self.referenced_by = referenced_by
32+
# self.type_reference = type_reference
33+
class TypeRefSet:
34+
def __init__(self, name: str):
35+
self.name = name
36+
self.referenced_by: List[str] = []
37+
38+
class ApiRefSet:
39+
def __init__(self, name: str):
40+
self.name = name
41+
#self.refs: List[TypeRef] = []
42+
self.refs = DefaultDict(TypeRefSet)
43+
44+
class ApiObj:
45+
def __init__(self, json_obj: dict):
46+
self.json_obj = json_obj
47+
class ApiObjConstant(ApiObj):
48+
def __init__(self, json_obj: dict):
49+
super().__init__(json_obj)
50+
51+
52+
class ApiRef:
53+
def __init__(self, api: str, name: str):
54+
self.api = api
55+
self.name = name
56+
self.combined = format("{}:{}".format(api, name))
57+
def __eq__(self, other):
58+
return self.combined.__eq__(other.combined)
59+
def __hash__(self):
60+
return self.combined.__hash__()
61+
62+
def makeApiSet(ignore) -> Set[str]:
63+
return set()
64+
65+
def getJsonApiRefs(api_refs: Set[ApiRef], json_obj):
66+
if isinstance(json_obj, dict):
67+
if "Kind" in json_obj:
68+
if json_obj["Kind"] == "ApiRef":
69+
api_refs.add(ApiRef(json_obj["Api"], apiRefTypeName(json_obj)))
70+
return
71+
for name,val in json_obj.items():
72+
getJsonApiRefs(api_refs, val)
73+
elif isinstance(json_obj, list):
74+
for entry in json_obj:
75+
getJsonApiRefs(api_refs, entry)
76+
elif isinstance(json_obj, str) or (json_obj is None) or isinstance(json_obj, bool) or isinstance(json_obj, int):
77+
pass
78+
else:
79+
sys.exit("{}".format(type(json_obj).__name__))
80+
81+
82+
def isAnonType(type_name):
83+
return type_name.endswith("_e__Struct") or type_name.endswith("_e__Union")
84+
85+
def main():
86+
win32json_branch = "10.0.19041.202-preview"
87+
win32json_sha = "7164f4ce9fe17b7c5da3473eed26886753ce1173"
88+
89+
script_dir = os.path.dirname(os.path.abspath(__file__))
90+
win32json = os.path.join(script_dir, "win32json")
91+
if not os.path.exists(win32json):
92+
print("Error: missing '{}'".format(win32json))
93+
print("Clone it with:")
94+
print(" git clone https://github.com/marlersoft/win32json {} -b {} && git -C {} checkout {} -b for_win32_transform".format(win32json, win32json_branch, win32json, win32json_sha))
95+
96+
api_dir = os.path.join(win32json, "api")
97+
98+
def getApiName(basename):
99+
if not basename.endswith(".json"):
100+
sys.exit("found a non-json file '{}' in directory '{}'".format(basename, api_dir))
101+
return basename[:-5]
102+
apis = [getApiName(basename) for basename in os.listdir(api_dir)]
103+
apis.sort()
104+
105+
#api_deps = ApiDeps()
106+
api_direct_deps = DefaultDict(makeApiSet)
107+
108+
api_direct_type_refs_table: dict[str,dict[str,Set[ApiRef]]] = {}
109+
print("loading types...")
110+
for api_name in apis:
111+
#print(api_name)
112+
filename = os.path.join(api_dir, api_name + ".json")
113+
with open(filename, "r", encoding='utf-8-sig') as file:
114+
api = json.load(file)
115+
constants = api["Constants"]
116+
types = api["Types"]
117+
functions = api["Functions"]
118+
unicode_aliases = api["UnicodeAliases"]
119+
120+
api_ref_sets = DefaultDict(ApiRefSet)
121+
122+
#print(" {} Constants".format(len(constants)))
123+
124+
# types = {}
125+
# for constant in constants:
126+
# constant_type = constant["Type"]
127+
# kind = constant_type["Kind"]
128+
# if kind == "ApiRef":
129+
# api_entry = api_ref_sets[constant_type["Api"]]
130+
# ref_name = apiRefTypeName(constant_type)
131+
# api_entry.refs[ref_name].referenced_by.append(ApiObjConstant(constant))
132+
# for api_ref_set in api_ref_sets.values():
133+
# print(" Referencing {} types from '{}'".format(len(api_ref_set.refs), api_ref_set.name))
134+
# api_direct_deps[api_name].add(api_ref_set.name)
135+
# for ref_name in api_ref_set.refs:
136+
# print(" {} referenced by {} types".format(ref_name, len(api_ref_set.refs[ref_name].referenced_by)))
137+
138+
139+
#print(" {} Types".format(len(types)))
140+
#print(" {} Functions".format(len(functions)))
141+
#print(" {} UnicodeAliases".format(len(unicode_aliases)))
142+
143+
def addTypeRefs(type_refs_table: Dict[str,Set[ApiRef]], name_prefix: str, type_json: Dict):
144+
kind = type_json["Kind"]
145+
full_name = name_prefix + type_json["Name"]
146+
#if len(name_prefix) > 0:
147+
# print("nested type '{}'".format(full_name))
148+
api_refs: Set[ApiRef] = set()
149+
if kind == "Struct" or kind == "Union":
150+
getJsonApiRefs(api_refs, type_json)
151+
for nested_type_json in type_json["NestedTypes"]:
152+
addTypeRefs(type_refs_table, full_name + ".", nested_type_json)
153+
elif kind == "Enum" or kind == "ComClassID":
154+
getJsonApiRefs(api_refs, type_json)
155+
assert(len(api_refs) == 0)
156+
elif kind == "Com" or kind == "FunctionPointer" or kind == "NativeTypedef":
157+
getJsonApiRefs(api_refs, type_json)
158+
else:
159+
sys.exit(kind)
160+
161+
# this could happen because of the same types defined for multiple architectures
162+
if full_name in type_refs_table:
163+
#raise ValueError("key '{}' already exists".format(full_name))
164+
type_refs_table[full_name] = type_refs_table[full_name].union(api_refs)
165+
else:
166+
type_refs_table[full_name] = api_refs
167+
168+
169+
type_refs_table: dict[str,Set[ApiRef]] = {}
170+
for type_json in types:
171+
addTypeRefs(type_refs_table, "", type_json)
172+
api_direct_type_refs_table[api_name] = type_refs_table
173+
174+
print("types loaded")
175+
for api in apis:
176+
direct_type_refs_table = api_direct_type_refs_table[api]
177+
print("api {}".format(api))
178+
179+
i = 0
180+
for type_name,refs in direct_type_refs_table.items():
181+
i += 1
182+
if refs:
183+
print(" {} refs from {}".format(len(refs), type_name))
184+
for ref in refs:
185+
#print(" {}".format(ref.combined))
186+
# make sure the API is valid
187+
table = api_direct_type_refs_table[ref.api]
188+
# if not an anonymous type, make sure the type is valid
189+
if not isAnonType(ref.name):
190+
if ref.name not in table:
191+
# check if it is a nested type here
192+
# TODO: I think I need to check every level of nesting
193+
print("api {} type_name{} ref.name {}".format(api, type_name, ref.name))
194+
type_names = type_name.split(".")
195+
ref_names = ref.name.split(".")
196+
i = 0
197+
while i < len(ref_names) and i < len(type_names):
198+
if ref_names[i] != type_names[-i-1]:
199+
break
200+
i += 1
201+
202+
nested_name = ".".join(type_names + ref_names[i:])
203+
if not nested_name in direct_type_refs_table:
204+
sys.exit("!!!!!!!!!!! {} not in {} (and {} not in {})".format(ref.name, api, nested_name, api))
205+
206+
207+
208+
209+
# recursive_dep_table: dict[str,Set[str]] = {}
210+
# for api in apis:
211+
# recursive_deps: Set[str] = set()
212+
# getRecursiveDeps(api_direct_deps, api, recursive_deps)
213+
# #print("{}: {}".format(api, recursive_deps))
214+
# recursive_dep_table[api] = recursive_deps
215+
# for api in apis:
216+
# recursive_deps = recursive_dep_table[api]
217+
# print("{}: {}".format(api, recursive_deps))
218+
# if api in recursive_deps:
219+
# print("{} is CYCLIC!!!!".format(api))
220+
#
221+
#def getRecursiveDeps(direct_dep_table: Set[Set[str]], name: str, result: Set[str]) -> None:
222+
# for direct_dep in direct_dep_table[name]:
223+
# if not direct_dep in result:
224+
# result.add(direct_dep)
225+
# getRecursiveDeps(direct_dep_table, direct_dep, result)
226+
#
227+
228+
main()

0 commit comments

Comments
 (0)