316 lines
17 KiB
Plaintext
316 lines
17 KiB
Plaintext
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
|