Skip to content

Commit 69bdf00

Browse files
committed
fix logic in detecting cycles
1 parent 6d1eed0 commit 69bdf00

File tree

2 files changed

+84
-37
lines changed

2 files changed

+84
-37
lines changed

dot2svg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#!/usr/bin/env sh
22
# Note: I ran this for 34 minutes and it still didn't finish
3-
dot -Tsvg deps.dot -o deps.svg
3+
#dot -Tsvg deps.dot -o deps.svg
4+
dot -Tsvg out-cycle-types.dot -o out-cycle-types.svg

go

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -178,77 +178,123 @@ def main():
178178

179179
print("types verified")
180180

181-
print("calculating recursive type references...")
181+
out_direct_deps_filename = "out-direct-deps.txt"
182+
print("generating {}...".format(out_direct_deps_filename))
183+
with open(os.path.join(script_dir, out_direct_deps_filename), "w") as file:
184+
for api in apis:
185+
table = api_direct_type_refs_table[api]
186+
for type_name,refs in table.top_level.items():
187+
file.write("{}:{}\n".format(api, type_name))
188+
for ref in refs:
189+
file.write(" {}\n".format(ref))
190+
191+
192+
out_recursive_deps_filename = "out-recursive-deps.txt"
193+
print("calculating recursive type references (will store in {})...".format(out_recursive_deps_filename))
182194
api_recursive_type_refs_table: Dict[str,dict[str,Set[ApiRef]]] = {}
183-
with open(os.path.join(script_dir, "out-recursive-deps.txt"), "w") as file:
195+
with open(os.path.join(script_dir, out_recursive_deps_filename), "w") as file:
184196
for api in apis:
185197
#print("calculating recursive deps on {}...".format(api))
186198
direct_type_refs_table = api_direct_type_refs_table[api]
187199
recursive_type_refs_table = {}
188200
for type_name,refs in direct_type_refs_table.top_level.items():
189201
recursive_chains: List[List[ApiRef]] = []
190-
getRecursiveChains(api_direct_type_refs_table, set(), refs, recursive_chains, None)
191-
file.write("{}:{} -> {}\n".format(api, type_name, recursive_chains))
202+
getRecursiveChains(api_direct_type_refs_table, set(), refs, recursive_chains, [])
203+
if len(recursive_chains) > 0:
204+
file.write("{}:{}\n".format(api, type_name))
205+
for chain in recursive_chains:
206+
file.write(" {}\n".format(chain))
192207
recursive_type_refs_table[type_name] = recursive_chains
193208
api_recursive_type_refs_table[api] = recursive_type_refs_table
194209
print("done calculating recursive type references")
195210

196211
print("searching for cycles...")
197212
full_type_set: Set[ApiRef] = set()
198-
cycle_type_set: Set[ApiRef] = set()
213+
api_cycle_type_set: Dict[ApiRef,List[ApiRef]] = {}
214+
#self_cycle_type_set: Dict[ApiRef,List[ApiRef]] = {}
215+
216+
def addApiCycleType(type_set, t, cycle):
217+
if t in type_set:
218+
pass
219+
#print("type {} is in multiple cycles?".format(t))
220+
else:
221+
type_set[t] = cycle
222+
199223
with open(os.path.join(script_dir, "out-cycles.txt"), "w") as file:
200224
for api in apis:
201225
#print("API: {}".format(api))
202226
table = api_recursive_type_refs_table[api]
203-
cycle_count = 0
227+
api_cycle_count = 0
204228
for type_name, recursive_chains in table.items():
205229
type_api_ref = ApiRef(api, type_name)
206230
full_type_set.add(type_api_ref)
207231
for chain in recursive_chains:
208-
state = 0
209-
for ref in chain:
210-
if ref.api == api:
211-
if state == 1:
212-
state = 2
213-
break
232+
found_external_type = False
233+
cycle_len = 0
234+
for i in range(0, len(chain)):
235+
ref = chain[i]
236+
if not found_external_type:
237+
if ref.api != api:
238+
found_external_type = True
214239
else:
215-
if state == 0:
216-
state = 1
217-
if state == 2:
218-
file.write("{}:{} CHAIN={}\n".format(api, type_name, chain))
219-
cycle_count += 1
220-
cycle_type_set.add(type_api_ref)
221-
for ref in chain:
222-
cycle_type_set.add(ref)
240+
if ref.api == api:
241+
cycle_len = i+1
242+
break
243+
if cycle_len > 0:
244+
cycle = chain[:cycle_len]
245+
file.write("{}:{} cycle={}\n".format(api, type_name, cycle))
246+
api_cycle_count += 1
247+
248+
if cycle[-1] != type_api_ref:
249+
addApiCycleType(api_cycle_type_set, type_api_ref, cycle)
250+
for ref in cycle:
251+
addApiCycleType(api_cycle_type_set, ref, cycle)
223252
else:
224253
pass
225254
#print("NOT CYCLIC: {}:{} CHAIN={}".format(api, type_name, chain))
226-
if cycle_count > 0:
227-
print("{:4} cycles: {}:{}".format(cycle_count, api, type_name))
255+
if api_cycle_count > 0:
256+
print("{:4} cycles: {}:{}".format(api_cycle_count, api, type_name))
257+
258+
print("{} out of {} types involved in cycles".format(len(api_cycle_type_set), len(full_type_set)))
259+
#print("{} types have self-referential cycles".format(len(self_cycle_type_set)))
228260

229-
print("{} out of {} types involved in cycles".format(len(cycle_type_set), len(full_type_set)))
261+
api_types_list = list(api_cycle_type_set)
262+
api_types_list.sort()
230263
with open(os.path.join(script_dir, "out-cycle-types.txt"), "w") as file:
231-
cycle_types_list = list(cycle_type_set)
232-
cycle_types_list.sort()
233-
for cycle_type in cycle_types_list:
234-
file.write("{}\n".format(cycle_type.combined))
264+
for cycle_type in api_types_list:
265+
file.write("{}\n".format(cycle_type))
266+
for t in api_cycle_type_set[cycle_type]:
267+
file.write(" {}\n".format(t))
268+
269+
# NOTE: this is not all the cycles, just some of the for now
270+
with open(os.path.join(script_dir, "out-cycle-types.dot"), "w") as file:
271+
file.write("digraph type_cycles {\n")
272+
handled = set()
273+
for cycle_type in api_types_list:
274+
cycle = api_cycle_type_set[cycle_type]
275+
file.write("\"{}\" -> \"{}\"\n".format(cycle_type, cycle[0]))
276+
for i in range(1, len(cycle)):
277+
file.write("\"{}\" -> \"{}\"\n".format(cycle[i-1], cycle[i]))
278+
file.write("}\n")
279+
#with open(os.path.join(script_dir, "out-self-cycle-types.txt"), "w") as file:
280+
# types_list = list(self_cycle_type_set)
281+
# types_list.sort()
282+
# for cycle_type in types_list:
283+
# file.write("{} (first chain {})\n".format(cycle_type.combined, self_cycle_type_set[cycle_type]))
235284

236285
print("done")
237286

238287

239-
def getRecursiveChains(api_direct_type_refs_table: Dict[str,ApiTypeNameToApiRefMap], handled: Set[ApiRef], refs: Set[ApiRef], result: List[List[ApiRef]], current_chain: Optional[List[ApiRef]]) -> None:
288+
def getRecursiveChains(api_direct_type_refs_table: Dict[str,ApiTypeNameToApiRefMap], handled: Set[ApiRef], refs: Set[ApiRef], result: List[List[ApiRef]], base_chain: List[ApiRef]) -> None:
240289
for ref in refs:
241290
ref_api_table = api_direct_type_refs_table[ref.api]
242-
if not isAnonType(ref.name) and ref.name in ref_api_table.top_level:
291+
if (not isAnonType(ref.name)) and (ref.name in ref_api_table.top_level):
243292
#file.write("\"{}\" -> \"{}\";\n".format(type_name, ref.name))
244-
next_chain = current_chain
245-
if not next_chain:
246-
next_chain = []
247-
result.append(next_chain)
248-
next_chain.append(ref)
249-
293+
next_chain = base_chain + [ref]
250294
ref_refs = ref_api_table.top_level[ref.name]
251-
if not ref in handled:
295+
if (len(ref_refs) == 0) or (ref in handled):
296+
result.append(next_chain)
297+
else:
252298
handled.add(ref)
253299
getRecursiveChains(api_direct_type_refs_table, handled, ref_refs, result, next_chain)
254300

0 commit comments

Comments
 (0)