import t, c from stdint import * import string import stdlib import memhub import viperlib import win32.fileio as fileio import win32.win32file import win32.win32base import ast import mlirlite import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HandlesTranslator import lib.core.IncludesScanner as IncludesScanner import lib.Stub.StubMerger as StubMerger import lib.core.Gen.Generator as GenGenerator import lib.core.CompilerFactory as CompilerFactory import lib.Phase.BuildPipeline as BuildPipeline import lib.Projectrans.Config as Config import lib.Stub.StubGen.Converter as StubConverter import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect import lib.Stub.StubMerger.StubMergerIR as StubMergerIR import lib.Stub.StubMerger.StubMergerStore as StubMergerStore import lib.Phase.Phase1.Phase1Scan as Phase1Scan import lib.Phase.Phase1.Phase1Topo as Phase1Topo import lib.Phase.Phase1 as Phase1 # Phase1Topo - topologically sort source files by class inheritance and import dependencies. # Functions live in this module (accessed via its prefix); globals go through Phase1._xxx. # _TopoSortFiles - topologically sort files by class inheritance (Kahn). # Builds a "file SHA1 -> dependency SHA1 set" map, computes in-degrees (parent-file first), then outputs the order; returns 0 or -1. def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, out_order: t.CPtr, out_count: t.CPtr, temp_dir: str, td_len_ts: sizet) -> int: """Topologically sort files by class inheritance and import dependencies.""" if result is None or out_order is None or out_count is None: return -1 entry_size: sizet = IncludesScanner.FileEntry.__sizeof__() # 1. Allocate flat arrays: file SHA1, dependency SHA1 list, dep counts, in-degrees, processed flags. # Flat arrays replace a FileDepEntry struct to avoid NULL str-pointer fields crashing. file_sha1s_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN) # 取消整数提升后,模块属性 CDefine(Phase1.MAX_*) 按最小位宽物化为 i16/i8;三段乘积 256*16*17=69632 会在 i16 回绕, # 首乘数显式 sizet() 加宽到 64 位。上一行 256*17=4352 未超 i16,故无需改。 file_deps_buf: bytes = mb.alloc(sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN) file_dep_counts: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4) file_indegrees: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4) file_processed: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4) if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None: if file_sha1s_buf is not None: mb.free(file_sha1s_buf) if file_deps_buf is not None: mb.free(file_deps_buf) if file_dep_counts is not None: mb.free(file_dep_counts) if file_indegrees is not None: mb.free(file_indegrees) if file_processed is not None: mb.free(file_processed) return -1 string.memset(file_sha1s_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN) string.memset(file_deps_buf, 0, sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN) string.memset(file_dep_counts, 0, Phase1.MAX_FILE_DEPS * 4) string.memset(file_indegrees, 0, Phase1.MAX_FILE_DEPS * 4) string.memset(file_processed, 0, Phase1.MAX_FILE_DEPS * 4) file_count: int = 0 # sha1 -> file_index map (simple linear search; file count is small) for i in range(int(result.Count)): entry_addr: uint64 = uint64(result.Entries) + i * entry_size entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr) if entry is None or entry.Sha1 is None: continue sha1_e: str = entry.Sha1 in_set: int = 0 if use_reachable != 0: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, reachable_set, reachable_count) else: in_set = StubMergerCollect._is_in_sha1_set(sha1_e, sha1_set, set_count) if in_set == 0: continue if file_count >= Phase1.MAX_FILE_DEPS: break # Initialize the flat array: file SHA1 (offset = file_count * Phase1.SHA1_LEN). # DepCount/InDegree/Processed are already zeroed by memset. sha1_dst: str = file_sha1s_buf + file_count * Phase1.SHA1_LEN string.strcpy(sha1_dst, sha1_e) file_count += 1 # 2. Build dependency edges: for each class, find the parent's defining file and add a child->parent edge for ci in range(int(class_count)): # Read parent class name (offset = ci * Phase1.CLASS_NAME_LEN) parent_ptr: str = parent_names_buf + ci * Phase1.CLASS_NAME_LEN if parent_ptr[0] == '\0': continue # Read child class definition SHA1 (offset = ci * Phase1.SHA1_LEN) child_def_sha1: str = def_sha1s_buf + ci * Phase1.SHA1_LEN # Find the SHA1 of the file defining the parent class parent_sha1: str = Phase1Scan._FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr) if parent_sha1 is None: continue # parent not in includes (e.g. Object, CEnum marker base classes) # The parent file SHA1 must differ from the child's (same-file inheritance needs no dependency) if string.strcmp(parent_sha1, child_def_sha1) == 0: continue # Find the child file's index and add the parent file SHA1 to its dependency list for fi in range(int(file_count)): fsha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN if string.strcmp(fsha1, child_def_sha1) != 0: continue # Read the current dependency count cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4 cnt_ptr: int* = (int*)(cnt_addr) if cnt_ptr is None: break cur_cnt: int = cnt_ptr[0] # Check whether the dependency is already present (dedupe) already: int = 0 for di in range(int(cur_cnt)): dep_slot: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN if string.strcmp(dep_slot, parent_sha1) == 0: already = 1 break if already != 0: break if cur_cnt < Phase1.MAX_DEP_EDGES: dep_dst: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + cur_cnt * Phase1.SHA1_LEN string.strcpy(dep_dst, parent_sha1) cnt_ptr[0] = cur_cnt + 1 break # 2.5 Add import dependency edges: imported modules must be fully translated first. # Class inheritance alone is insufficient (cross-module globals/vtable TypeStr/generics need deps); deps come from Phase 1a-pre, mapped via _BuildIncludesSha1Map. sha1_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) mod_arr_ts: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) map_count_ts: int = 0 if sha1_arr_ts is not None and mod_arr_ts is not None: map_count_ts = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts) for fi_imp in range(int(file_count)): if map_count_ts <= 0: break fsha1_imp: str = file_sha1s_buf + fi_imp * Phase1.SHA1_LEN deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp) if deps_imp is None: continue dl_imp: sizet = string.strlen(deps_imp) dpos_imp: sizet = 0 while dpos_imp < dl_imp: dstart_imp: sizet = dpos_imp while dpos_imp < dl_imp and deps_imp[dpos_imp] != ' ': dpos_imp += 1 dlen_imp: sizet = dpos_imp - dstart_imp if dpos_imp < dl_imp: dpos_imp += 1 if dlen_imp == 0: continue dname_imp: bytes = stdlib.malloc(dlen_imp + 1) if dname_imp is None: continue string.strncpy(dname_imp, deps_imp + dstart_imp, dlen_imp) dname_imp[dlen_imp] = '\0' dsha1_imp: str = StubMergerIR._FindSha1ByModName(sha1_arr_ts, mod_arr_ts, map_count_ts, dname_imp) stdlib.free(dname_imp) if dsha1_imp is None: continue # Find the dependency file's index in the list (skip self) for fj_imp in range(int(file_count)): if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0: continue if fj_imp == fi_imp: break # Dedupe and add the edge: fi_imp depends on fj_imp (InDegree[fi_imp]++) cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4 cnt_ptr_imp: int* = (int*)(cnt_addr_imp) if cnt_ptr_imp is None: break cur_cnt_imp: int = cnt_ptr_imp[0] already_imp: int = 0 for di_imp in range(int(cur_cnt_imp)): dep_slot_imp: str = file_deps_buf + fi_imp * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_imp * Phase1.SHA1_LEN if string.strcmp(dep_slot_imp, dsha1_imp) == 0: already_imp = 1 break if already_imp == 0 and cur_cnt_imp < Phase1.MAX_DEP_EDGES: dep_dst_imp: str = file_deps_buf + fi_imp * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + cur_cnt_imp * Phase1.SHA1_LEN string.strcpy(dep_dst_imp, dsha1_imp) cnt_ptr_imp[0] = cur_cnt_imp + 1 break # Free the local mapping arrays used for import edges if sha1_arr_ts is not None: stdlib.free(sha1_arr_ts) if mod_arr_ts is not None: stdlib.free(mod_arr_ts) # 3. Kahn: InDegree = DepCount (number of parent files depended on). # Process zero-dependency files first, then decrement the in-degree of dependents. for fi in range(int(file_count)): cnt_addr3: uint64 = uint64(file_dep_counts) + fi * 4 cnt_ptr3: int* = (int*)(cnt_addr3) ind_addr3: uint64 = uint64(file_indegrees) + fi * 4 ind_ptr3: int* = (int*)(ind_addr3) if cnt_ptr3 is not None and ind_ptr3 is not None: ind_ptr3[0] = cnt_ptr3[0] # 4. Topological sort output out_idx: int = 0 changed: int = 1 while changed != 0: changed = 0 for fi in range(int(file_count)): # Check processed flag proc_addr4: uint64 = uint64(file_processed) + fi * 4 proc_ptr4: int* = (int*)(proc_addr4) if proc_ptr4 is None or proc_ptr4[0] != 0: continue # Check in-degree ind_addr4: uint64 = uint64(file_indegrees) + fi * 4 ind_ptr4: int* = (int*)(ind_addr4) if ind_ptr4 is None or ind_ptr4[0] > 0: continue # In-degree 0: can be processed # Find the matching result.Entries index target_sha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN for ri in range(int(result.Count)): ea_r: uint64 = uint64(result.Entries) + ri * entry_size ent_r: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r) if ent_r is not None and ent_r.Sha1 is not None: if string.strcmp(ent_r.Sha1, target_sha1) == 0: # Write into the output index array out_idx_addr: uint64 = uint64(out_order) + out_idx * 4 out_int_ptr: int* = (int*)(out_idx_addr) if out_int_ptr is not None: out_int_ptr[0] = ri out_idx += 1 break # Mark as processed proc_ptr4[0] = 1 changed = 1 # Decrement the in-degree of files that depend on this one for fj in range(int(file_count)): proc_addr5: uint64 = uint64(file_processed) + fj * 4 proc_ptr5: int* = (int*)(proc_addr5) if proc_ptr5 is None or proc_ptr5[0] != 0: continue # Read fj's dependency count cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4 cnt_ptr5: int* = (int*)(cnt_addr5) if cnt_ptr5 is None: continue fj_dep_cnt: int = cnt_ptr5[0] for di in range(int(fj_dep_cnt)): dep_slot5: str = file_deps_buf + fj * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN if string.strcmp(dep_slot5, target_sha1) == 0: ind_addr5: uint64 = uint64(file_indegrees) + fj * 4 ind_ptr5: int* = (int*)(ind_addr5) if ind_ptr5 is not None: ind_ptr5[0] = ind_ptr5[0] - 1 break # Handle cycles: within a cycle emit the more-depended-upon files first. # On-demand container injection (e.g. string -> _list) plus real imports form pseudo-cycles Kahn cannot fully drain; scan order would translate dependents first -> unregistered cross-module vtable TypeStr. if out_idx < file_count: rem_idx: int* = (int*)(stdlib.malloc(4 * file_count)) rem_depcnt: int* = (int*)(stdlib.malloc(4 * file_count)) rem_count: int = 0 if rem_idx is not None and rem_depcnt is not None: for fi in range(int(file_count)): proc_addr7: uint64 = uint64(file_processed) + fi * 4 proc_ptr7: int* = (int*)(proc_addr7) if proc_ptr7 is not None and proc_ptr7[0] == 0: rem_idx[rem_count] = fi rem_depcnt[rem_count] = 0 rem_count += 1 # Compute each remaining file's dependents count (among the remaining files) for i_a in range(int(rem_count)): fi_a: int = rem_idx[i_a] target_sha1_a: str = file_sha1s_buf + fi_a * Phase1.SHA1_LEN for i_b in range(int(rem_count)): fi_b: int = rem_idx[i_b] if fi_b == fi_a: continue cnt_addr_b: uint64 = uint64(file_dep_counts) + fi_b * 4 cnt_ptr_b: int* = (int*)(cnt_addr_b) if cnt_ptr_b is None: continue cnt_b: int = cnt_ptr_b[0] for di_b in range(int(cnt_b)): dep_slot_b: str = file_deps_buf + fi_b * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_b * Phase1.SHA1_LEN if string.strcmp(dep_slot_b, target_sha1_a) == 0: rem_depcnt[i_a] = rem_depcnt[i_a] + 1 break # Bubble sort descending (more dependents first) for i_a in range(int(rem_count)): for j_a in range(int(rem_count - 1)): if rem_depcnt[j_a] < rem_depcnt[j_a + 1]: tmp_i: int = rem_idx[j_a] rem_idx[j_a] = rem_idx[j_a + 1] rem_idx[j_a + 1] = tmp_i tmp_d: int = rem_depcnt[j_a] rem_depcnt[j_a] = rem_depcnt[j_a + 1] rem_depcnt[j_a + 1] = tmp_d # Emit the remaining files in order for i_a in range(int(rem_count)): fi_a2: int = rem_idx[i_a] proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4 proc_ptr8: int* = (int*)(proc_addr8) if proc_ptr8 is None or proc_ptr8[0] != 0: continue target_sha1_2: str = file_sha1s_buf + fi_a2 * Phase1.SHA1_LEN for ri in range(int(result.Count)): ea_r2: uint64 = uint64(result.Entries) + ri * entry_size ent_r2: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r2) if ent_r2 is not None and ent_r2.Sha1 is not None: if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0: out_idx_addr2: uint64 = uint64(out_order) + out_idx * 4 out_int_ptr2: int* = (int*)(out_idx_addr2) if out_int_ptr2 is not None: out_int_ptr2[0] = ri out_idx += 1 break proc_ptr8[0] = 1 stdlib.free(rem_idx) stdlib.free(rem_depcnt) # Write the output count out_count_ptr: int* = (int*)(out_count) if out_count_ptr is not None: out_count_ptr[0] = out_idx # Free the flat arrays mb.free(file_sha1s_buf) mb.free(file_deps_buf) mb.free(file_dep_counts) mb.free(file_indegrees) mb.free(file_processed) return 0