支持 tls 关键字,全面准备清退 t, c
This commit is contained in:
1 parent
6e17716a69
commit
3242560d02
74 files changed
+524
-576
No files matched your search
+138
-70
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdio
|
||||
import string
|
||||
@@ -262,8 +261,8 @@ class BuildResult:
|
||||
OutputPath: str
|
||||
ErrorMsg: str
|
||||
|
||||
def __new__(self) -> t.CPtr:
|
||||
return t.CPtr(_mbuddy.alloc(BuildResult.__sizeof__()))
|
||||
def __new__(self) -> rawptr:
|
||||
return rawptr(_mbuddy.alloc(BuildResult.__sizeof__()))
|
||||
|
||||
def __init__(self):
|
||||
self.Success = 0
|
||||
@@ -576,12 +575,12 @@ def _run_cmd_wait(cmd: str) -> int:
|
||||
string.memset(&sa, 0, win32.win32base.SECURITY_ATTRIBUTES.__sizeof__())
|
||||
string.memset(&pi, 0, win32process.PROCESS_INFORMATION.__sizeof__())
|
||||
sa.nLength = win32.win32base.SECURITY_ATTRIBUTES.__sizeof__()
|
||||
sa.lpSecurityDescriptor = t.CPtr(0)
|
||||
sa.lpSecurityDescriptor = None
|
||||
sa.bInheritHandle = 1
|
||||
si.cb = win32process.STARTUPINFOA.__sizeof__()
|
||||
# create_flags=0: the child inherits the parent console (output shown, like system);
|
||||
# si.dwFlags stays 0 after memset (no STARTF_USESTDHANDLES, no std handles).
|
||||
ok: int = win32process.CreateProcessA(t.CPtr(0), cmd_line, t.CPtr(0), t.CPtr(0), 1, 0, t.CPtr(0), t.CPtr(0), &si, &pi)
|
||||
ok: int = win32process.CreateProcessA(None, cmd_line, None, None, 1, 0, None, None, &si, &pi)
|
||||
# CreateProcessA failure diagnostics: read GetLastError immediately (later calls overwrite
|
||||
# it). Use warning, not error (error would sys.exit in a worker thread).
|
||||
if ok == 0:
|
||||
@@ -597,10 +596,10 @@ def _run_cmd_wait(cmd: str) -> int:
|
||||
return int(exit_code)
|
||||
|
||||
|
||||
def _llc_task_worker(arg: t.CPtr) -> t.CPtr:
|
||||
def _llc_task_worker(arg: rawptr) -> rawptr:
|
||||
"""llc thread entry: only calls _run_cmd_wait, no pool allocation (thread-safe)."""
|
||||
task: LlcTask* = (LlcTask*)(arg)
|
||||
if task is None: return t.CPtr(0)
|
||||
if task is None: return None
|
||||
# MLIR backend: validate (mlir-opt) -> lower (mlir-translate), then ASan opt -> llc
|
||||
if task.MlirOptCmd is not None:
|
||||
mo_rc: int = _run_cmd_wait(task.MlirOptCmd)
|
||||
@@ -608,21 +607,21 @@ def _llc_task_worker(arg: t.CPtr) -> t.CPtr:
|
||||
task.ExitCode = mo_rc
|
||||
task.FailStage = LLC_STAGE_MLIR_OPT
|
||||
task.Result = 1
|
||||
return t.CPtr(0)
|
||||
return None
|
||||
if task.MlirTranslateCmd is not None:
|
||||
mt_rc: int = _run_cmd_wait(task.MlirTranslateCmd)
|
||||
if mt_rc != 0:
|
||||
task.ExitCode = mt_rc
|
||||
task.FailStage = LLC_STAGE_MLIR_TRANSLATE
|
||||
task.Result = 1
|
||||
return t.CPtr(0)
|
||||
return None
|
||||
if task.OptCmd is not None:
|
||||
opt_rc: int = _run_cmd_wait(task.OptCmd)
|
||||
if opt_rc != 0:
|
||||
task.ExitCode = opt_rc
|
||||
task.FailStage = LLC_STAGE_ASAN_OPT
|
||||
task.Result = 1
|
||||
return t.CPtr(0)
|
||||
return None
|
||||
llc_rc: int = _run_cmd_wait(task.LlcCmd)
|
||||
task.ExitCode = llc_rc
|
||||
if llc_rc != 0:
|
||||
@@ -631,7 +630,7 @@ def _llc_task_worker(arg: t.CPtr) -> t.CPtr:
|
||||
else:
|
||||
task.FailStage = LLC_STAGE_NONE
|
||||
task.Result = 0
|
||||
return t.CPtr(0)
|
||||
return None
|
||||
|
||||
|
||||
def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
|
||||
@@ -650,7 +649,7 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
|
||||
return 0
|
||||
if max_threads <= 0: max_threads = 1
|
||||
# Thread-handle array: list[str] (str=bytes=i8*, explicit elem_size=8) stores handles;
|
||||
# list[t.CPtr] append crashes reading page zero. Grab the handle right after start().
|
||||
# list[rawptr] append crashes reading page zero. Grab the handle right after start().
|
||||
handles: list[str]* = list[str](_mbuddy, 8)
|
||||
if handles is None:
|
||||
VLogger.error("run_llc_parallel: failed to allocate thread handle array", "PhaseB")
|
||||
@@ -664,13 +663,13 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
|
||||
# spawn this batch
|
||||
for j in range(int(batch_n)):
|
||||
tsk: LlcTask* = (LlcTask*)(uint64(tasks) + sizet(batch_start + j) * slot_size)
|
||||
th: vthreading.Thread* = vthreading.Thread(_llc_task_worker, t.CPtr(tsk))
|
||||
th: vthreading.Thread* = vthreading.Thread(_llc_task_worker, rawptr(tsk))
|
||||
th.start()
|
||||
# Read the handle right after start() (the Thread object's stack slot may be
|
||||
# reused; reading self.handle at join could see a cleared/overwritten value).
|
||||
handles.append(t.CPtr(th.handle))
|
||||
handles.append(rawptr(th.handle))
|
||||
for j in range(int(batch_n)):
|
||||
h_j: t.CPtr = t.CPtr(handles.get(batch_start + j))
|
||||
h_j: rawptr = rawptr(handles.get(batch_start + j))
|
||||
wr: uint32 = win32sync.WaitForSingleObject(h_j, 600000)
|
||||
if wr != 0: VLogger.error("llc thread join timeout/failure (not finished within 600 seconds)", "PhaseB")
|
||||
win32.win32base.CloseHandle(h_j)
|
||||
@@ -1052,8 +1051,8 @@ class TbPeLayout:
|
||||
SizeOfImage: uint64
|
||||
TextRva: uint64
|
||||
|
||||
def __new__(self) -> t.CPtr:
|
||||
return t.CPtr(_mbuddy.alloc(TbPeLayout.__sizeof__()))
|
||||
def __new__(self) -> rawptr:
|
||||
return rawptr(_mbuddy.alloc(TbPeLayout.__sizeof__()))
|
||||
|
||||
def __init__(self):
|
||||
self.Ok = 0
|
||||
@@ -1071,8 +1070,8 @@ class TbDump:
|
||||
FuncCap: int
|
||||
FuncCount: int
|
||||
|
||||
def __new__(self) -> t.CPtr:
|
||||
return t.CPtr(_mbuddy.alloc(TbDump.__sizeof__()))
|
||||
def __new__(self) -> rawptr:
|
||||
return rawptr(_mbuddy.alloc(TbDump.__sizeof__()))
|
||||
|
||||
def __init__(self):
|
||||
self.RowBuf = None
|
||||
@@ -1090,8 +1089,8 @@ class TbPool:
|
||||
Offs: bytes
|
||||
OffCount: int
|
||||
|
||||
def __new__(self) -> t.CPtr:
|
||||
return t.CPtr(_mbuddy.alloc(TbPool.__sizeof__()))
|
||||
def __new__(self) -> rawptr:
|
||||
return rawptr(_mbuddy.alloc(TbPool.__sizeof__()))
|
||||
|
||||
def __init__(self):
|
||||
self.Pool = None
|
||||
@@ -1118,9 +1117,12 @@ def _tb_rd_u32(buf: bytes, off: sizet) -> uint32:
|
||||
|
||||
|
||||
def _tb_rd_u64(buf: bytes, off: sizet) -> uint64:
|
||||
lo: uint64 = uint64(_tb_rd_u32(buf, off))
|
||||
hi: uint64 = uint64(_tb_rd_u32(buf, off + 4))
|
||||
return lo | (hi << 32)
|
||||
# Assemble via memcpy instead of u32 widening + shifts: the manual form left 0xFFFFFFFF in
|
||||
# the high half (a stale register/slot), which then looked like a wild pointer in the
|
||||
# stackmap tables. memcpy is exact and little-endian on x86-64.
|
||||
v: uint64 = 0
|
||||
string.memcpy(&v, buf + off, 8)
|
||||
return v
|
||||
|
||||
|
||||
def _tb_wr_u32(buf: bytes, off: sizet, v: uint64):
|
||||
@@ -1131,8 +1133,18 @@ def _tb_wr_u32(buf: bytes, off: sizet, v: uint64):
|
||||
|
||||
|
||||
def _tb_wr_u64(buf: bytes, off: sizet, v: uint64):
|
||||
_tb_wr_u32(buf, off, v & 0xFFFFFFFF)
|
||||
_tb_wr_u32(buf, off + 4, (v >> 32) & 0xFFFFFFFF)
|
||||
string.memcpy(buf + off, &v, 8)
|
||||
|
||||
|
||||
def _tb_wr_ptr(buf: bytes, off: sizet, p: str):
|
||||
"""Store a string pointer into a byte table without a ptr->int cast.
|
||||
|
||||
Do NOT write `memcpy(buf + off, &p, 8)`: passing `&p` (i8**) to a `rawptr` parameter is
|
||||
currently compiled as a dereference (it copies the string's first 8 bytes instead of the
|
||||
pointer value), so go through a typed slot instead.
|
||||
"""
|
||||
slot: str* = str*(buf + off)
|
||||
slot[0] = p
|
||||
|
||||
|
||||
# ---- tiny string helpers (no re module) ----
|
||||
@@ -1142,6 +1154,18 @@ def _tb_cc(s: str, i: sizet) -> int:
|
||||
return int(s[i]) & 0xFF
|
||||
|
||||
|
||||
def _tb_str_ok(s: str) -> int:
|
||||
"""Return 1 when s is a plausible user-space string pointer (not 0, -1 or garbage).
|
||||
|
||||
DWARF/objdump text is parsed into pointer-bearing tables; a single bad slot would later be
|
||||
dereferenced by _tb_cc/strlen and crash the compiler with ASan "unknown address 0xffff...".
|
||||
Callers use this to reject such a slot at the boundary instead of storing it.
|
||||
"""
|
||||
if s is None: return 0
|
||||
v: uint64 = uint64(s)
|
||||
return 1 if v >= 65536 and v <= 0x7FFFFFFFFFFF else 0
|
||||
|
||||
|
||||
def _tb_contains(s: str, sub: str) -> int:
|
||||
if s is None or sub is None: return 0
|
||||
return 1 if string.strstr(s, sub) is not None else 0
|
||||
@@ -1331,7 +1355,13 @@ def _tb_quoted_after(mb: memhub.MemBuddy*, s: str, key: str) -> str:
|
||||
|
||||
def _tb_dir_lookup(dirs: bytes, count: int, idx: int) -> str:
|
||||
for i in range(int(count)):
|
||||
if int(_tb_rd_u32(dirs, sizet(i) * TB_DIR_SIZE)) == idx: return rawptr(_tb_rd_u64(dirs, sizet(i) * TB_DIR_SIZE + 8))
|
||||
if int(_tb_rd_u32(dirs, sizet(i) * TB_DIR_SIZE)) == idx:
|
||||
p: uint64 = _tb_rd_u64(dirs, sizet(i) * TB_DIR_SIZE + 8)
|
||||
# fail-fast: a wild name pointer would crash inside _tb_is_abs_path/strlen later.
|
||||
if p < 65536 or p > 0x7FFFFFFFFFFF:
|
||||
VLogger.error(f"--traceback: internal error: DWARF directory record {i} has an invalid name pointer ({p})", "TRACEBACK")
|
||||
return None
|
||||
return rawptr(p)
|
||||
return None
|
||||
|
||||
|
||||
@@ -1345,8 +1375,20 @@ def _tb_resolve_file(mb: memhub.MemBuddy*, files: bytes, fcount: int, dirs: byte
|
||||
"""Resolve a file-table entry to a path (absolute name wins, else dir join)."""
|
||||
roff: sizet = sizet(frec) * TB_FILE_SIZE
|
||||
cached: uint64 = _tb_rd_u64(files, roff + 24)
|
||||
if cached != 0: return rawptr(cached)
|
||||
name: str = rawptr(_tb_rd_u64(files, roff + 8))
|
||||
if cached != 0:
|
||||
# fail-fast: a wild cached path would crash inside strlen/is_abs_path below; report the
|
||||
# record that carries it together with the table state instead.
|
||||
if cached < 65536 or cached > 0x7FFFFFFFFFFF:
|
||||
VLogger.error(f"--traceback: internal error: DWARF file record {frec} has an invalid cached path pointer ({cached})", "TRACEBACK")
|
||||
return None
|
||||
return rawptr(cached)
|
||||
nptr: uint64 = _tb_rd_u64(files, roff + 8)
|
||||
if nptr < 65536 or nptr > 0x7FFFFFFFFFFF:
|
||||
ridx: uint64 = uint64(_tb_rd_u32(files, roff))
|
||||
rdir: uint64 = uint64(_tb_rd_u32(files, roff + 4))
|
||||
VLogger.error(f"--traceback: internal error: DWARF file record {frec} has an invalid name pointer ({nptr}); record idx={ridx} dir_index={rdir}, records in table={fcount}", "TRACEBACK")
|
||||
return None
|
||||
name: str = rawptr(nptr)
|
||||
path: str = name
|
||||
if name is not None and _tb_is_abs_path(name) == 0:
|
||||
didx: int = int(_tb_rd_u32(files, roff + 4))
|
||||
@@ -1357,7 +1399,7 @@ def _tb_resolve_file(mb: memhub.MemBuddy*, files: bytes, fcount: int, dirs: byte
|
||||
if root is not None and _tb_is_abs_path(root) != 0: base = _tb_join(mb, root, base)
|
||||
path = _tb_join(mb, base, name)
|
||||
if path is None or string.strlen(path) == 0: path = "??"
|
||||
_tb_wr_u64(files, roff + 24, uint64(path))
|
||||
_tb_wr_ptr(files, roff + 24, path)
|
||||
return path
|
||||
|
||||
|
||||
@@ -1371,6 +1413,10 @@ def _tb_grow_rec(mb: memhub.MemBuddy*, buf: bytes, old_cap: int, rec_size: int,
|
||||
|
||||
def _tb_emit_row(mb: memhub.MemBuddy*, d: TbDump*, addr: uint64, path: str, line: int, fname: str = None) -> int:
|
||||
if d is None: return 1
|
||||
# Every row must carry a file path. A caller that could not resolve one passes None (or a
|
||||
# wild sentinel); storing that as-is makes the later strlen() sizing pass fault. Normalise to
|
||||
# the stackmap's existing "??" convention (the DWARF path already does the same).
|
||||
if _tb_str_ok(path) == 0: path = "??"
|
||||
if d.RowCount >= d.RowCap:
|
||||
ncap: int = d.RowCap * 2
|
||||
nb: bytes = _tb_grow_rec(mb, d.RowBuf, d.RowCap, TB_ROW_SIZE, ncap)
|
||||
@@ -1381,11 +1427,10 @@ def _tb_emit_row(mb: memhub.MemBuddy*, d: TbDump*, addr: uint64, path: str, line
|
||||
d.RowCap = ncap
|
||||
off: sizet = sizet(d.RowCount) * TB_ROW_SIZE
|
||||
_tb_wr_u64(d.RowBuf, off, addr)
|
||||
_tb_wr_u64(d.RowBuf, off + TB_ROW_OFF_FILE, uint64(path))
|
||||
_tb_wr_ptr(d.RowBuf, off + TB_ROW_OFF_FILE, path)
|
||||
_tb_wr_u32(d.RowBuf, off + TB_ROW_OFF_LINE, uint64(line))
|
||||
fv: uint64 = 0
|
||||
if fname is not None: fv = uint64(fname)
|
||||
_tb_wr_u64(d.RowBuf, off + TB_ROW_OFF_FUNC, fv)
|
||||
if _tb_str_ok(fname) != 0: _tb_wr_ptr(d.RowBuf, off + TB_ROW_OFF_FUNC, fname)
|
||||
else: _tb_wr_u64(d.RowBuf, off + TB_ROW_OFF_FUNC, 0)
|
||||
d.RowCount = d.RowCount + 1
|
||||
d.RawRowCount = d.RawRowCount + 1
|
||||
return 0
|
||||
@@ -1404,7 +1449,7 @@ def _tb_emit_func(mb: memhub.MemBuddy*, d: TbDump*, low: uint64, high: uint64, n
|
||||
off: sizet = sizet(d.FuncCount) * TB_FUNC_SIZE
|
||||
_tb_wr_u64(d.FuncBuf, off, low)
|
||||
_tb_wr_u64(d.FuncBuf, off + 8, high)
|
||||
_tb_wr_u64(d.FuncBuf, off + 16, uint64(name))
|
||||
_tb_wr_ptr(d.FuncBuf, off + 16, name)
|
||||
d.FuncCount = d.FuncCount + 1
|
||||
return 0
|
||||
|
||||
@@ -1549,10 +1594,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
|
||||
if in_line != 0:
|
||||
if _tb_startswith(st, "debug_line[") != 0:
|
||||
# new CU: commit pending file, reset both tables
|
||||
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
|
||||
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
|
||||
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
|
||||
file_count += 1
|
||||
pkind = 0
|
||||
@@ -1562,10 +1607,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
|
||||
file_count = 0
|
||||
continue
|
||||
if _tb_startswith(st, "file_names[") != 0:
|
||||
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
|
||||
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
|
||||
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
|
||||
file_count += 1
|
||||
pname = None
|
||||
@@ -1574,10 +1619,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
|
||||
pidx = _tb_bracket_idx(st)
|
||||
continue
|
||||
if _tb_startswith(st, "directory_names[") != 0 or _tb_startswith(st, "include_directories[") != 0:
|
||||
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
|
||||
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
|
||||
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
|
||||
file_count += 1
|
||||
pname = None
|
||||
@@ -1586,9 +1631,13 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
|
||||
val: str = _tb_value_after_sep(_tb_after_bracket(st))
|
||||
if val is not None and _tb_len_trimmed(val) > 0:
|
||||
if dir_count < TB_DIR_CAP:
|
||||
_tb_wr_u32(dirs, sizet(dir_count) * TB_DIR_SIZE, uint64(pidx))
|
||||
_tb_wr_u64(dirs, sizet(dir_count) * TB_DIR_SIZE + 8, uint64(_tb_unquote(mb, val)))
|
||||
dir_count += 1
|
||||
dname: str = _tb_unquote(mb, val)
|
||||
if _tb_str_ok(dname) != 0:
|
||||
_tb_wr_u32(dirs, sizet(dir_count) * TB_DIR_SIZE, uint64(pidx))
|
||||
_tb_wr_ptr(dirs, sizet(dir_count) * TB_DIR_SIZE + 8, dname)
|
||||
dir_count += 1
|
||||
else:
|
||||
VLogger.warning("--traceback: ignoring a DWARF directory entry whose name could not be copied", "TRACEBACK")
|
||||
pkind = 0
|
||||
else: pkind = 2
|
||||
continue
|
||||
@@ -1596,9 +1645,9 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
|
||||
val2: str = _tb_unquote(mb, _tb_ltrim(st + 5))
|
||||
if pkind == 1: pname = val2
|
||||
else:
|
||||
if dir_count < TB_DIR_CAP and val2 is not None:
|
||||
if dir_count < TB_DIR_CAP and _tb_str_ok(val2) != 0:
|
||||
_tb_wr_u32(dirs, sizet(dir_count) * TB_DIR_SIZE, uint64(pidx))
|
||||
_tb_wr_u64(dirs, sizet(dir_count) * TB_DIR_SIZE + 8, uint64(val2))
|
||||
_tb_wr_ptr(dirs, sizet(dir_count) * TB_DIR_SIZE + 8, val2)
|
||||
dir_count += 1
|
||||
pkind = 0
|
||||
continue
|
||||
@@ -1608,10 +1657,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
|
||||
pdir = _tb_parse_dec(_tb_ltrim(st + 10))
|
||||
continue
|
||||
if _tb_startswith(st, "Address") != 0 and _tb_contains(st, "Column") != 0:
|
||||
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
|
||||
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
|
||||
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
|
||||
file_count += 1
|
||||
pkind = 0
|
||||
@@ -1619,10 +1668,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
|
||||
pdir = 0
|
||||
continue
|
||||
if _tb_cc(linebuf, 0) == 48 and _tb_cc(linebuf, 1) == 120:
|
||||
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
|
||||
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
|
||||
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
|
||||
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
|
||||
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
|
||||
file_count += 1
|
||||
pkind = 0
|
||||
@@ -1657,7 +1706,7 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
|
||||
|
||||
# ---- DWARF debug_info subprogram parsing ----
|
||||
if _tb_contains(linebuf, "DW_TAG_") != 0:
|
||||
if in_prog != 0 and cur_has_low != 0 and cname is not None and string.strlen(cname) > 0:
|
||||
if in_prog != 0 and cur_has_low != 0 and _tb_str_ok(cname) != 0 and string.strlen(cname) > 0:
|
||||
hi: uint64 = cur_low + 1
|
||||
if cur_has_high != 0:
|
||||
hi = cur_high
|
||||
@@ -1734,8 +1783,8 @@ class TbCallParse:
|
||||
Grp: bytes # array of TB_GRP_SIZE slots
|
||||
GrpCap: int
|
||||
|
||||
def __new__(self) -> t.CPtr:
|
||||
return t.CPtr(_mbuddy.alloc(TbCallParse.__sizeof__()))
|
||||
def __new__(self) -> rawptr:
|
||||
return rawptr(_mbuddy.alloc(TbCallParse.__sizeof__()))
|
||||
|
||||
def __init__(self):
|
||||
self.Cr = None
|
||||
@@ -1776,8 +1825,8 @@ def _tb_grp_get(grp: bytes, cap: int, caller: str, callee: str, do_insert: int)
|
||||
if uint64(_tb_rd_u32(grp, off + TB_GRP_OFF_USED)) == 0:
|
||||
if do_insert == 0: return -1
|
||||
_tb_wr_u32(grp, off + TB_GRP_OFF_USED, uint64(1))
|
||||
_tb_wr_u64(grp, off + TB_GRP_OFF_CALLER, uint64(caller))
|
||||
_tb_wr_u64(grp, off + TB_GRP_OFF_CALLEE, uint64(callee))
|
||||
_tb_wr_ptr(grp, off + TB_GRP_OFF_CALLER, caller)
|
||||
_tb_wr_ptr(grp, off + TB_GRP_OFF_CALLEE, callee)
|
||||
_tb_wr_u32(grp, off + TB_GRP_OFF_HEAD, uint64(TB_IDX_NONE))
|
||||
_tb_wr_u32(grp, off + TB_GRP_OFF_TAIL, uint64(TB_IDX_NONE))
|
||||
_tb_wr_u32(grp, off + TB_GRP_OFF_CURSOR, uint64(TB_IDX_NONE))
|
||||
@@ -1811,9 +1860,9 @@ def _tb_cp_add_cr(mb: memhub.MemBuddy*, cp: TbCallParse*, caller: str, callee: s
|
||||
cp.Cr = nb
|
||||
cp.CrCap = ncap
|
||||
off: sizet = sizet(cp.CrN) * TB_CR_SIZE
|
||||
_tb_wr_u64(cp.Cr, off + TB_CR_OFF_CALLER, uint64(caller))
|
||||
_tb_wr_u64(cp.Cr, off + TB_CR_OFF_CALLEE, uint64(callee))
|
||||
_tb_wr_u64(cp.Cr, off + TB_CR_OFF_FILE, uint64(file))
|
||||
_tb_wr_ptr(cp.Cr, off + TB_CR_OFF_CALLER, caller)
|
||||
_tb_wr_ptr(cp.Cr, off + TB_CR_OFF_CALLEE, callee)
|
||||
_tb_wr_ptr(cp.Cr, off + TB_CR_OFF_FILE, file)
|
||||
_tb_wr_u32(cp.Cr, off + TB_CR_OFF_LINE, uint64(line))
|
||||
_tb_wr_u32(cp.Cr, off + TB_CR_OFF_NEXT, uint64(TB_IDX_NONE))
|
||||
cp.CrN = cp.CrN + 1
|
||||
@@ -1830,8 +1879,8 @@ def _tb_cp_add_fr(mb: memhub.MemBuddy*, cp: TbCallParse*, sym: str, file: str, d
|
||||
cp.Fr = nb
|
||||
cp.FrCap = ncap
|
||||
off: sizet = sizet(cp.FrN) * TB_FR_SIZE
|
||||
_tb_wr_u64(cp.Fr, off + TB_FR_OFF_SYM, uint64(sym))
|
||||
_tb_wr_u64(cp.Fr, off + TB_FR_OFF_FILE, uint64(file))
|
||||
_tb_wr_ptr(cp.Fr, off + TB_FR_OFF_SYM, sym)
|
||||
_tb_wr_ptr(cp.Fr, off + TB_FR_OFF_FILE, file)
|
||||
_tb_wr_u32(cp.Fr, off + TB_FR_OFF_DEFLINE, uint64(defline))
|
||||
cp.FrN = cp.FrN + 1
|
||||
return 0
|
||||
@@ -1853,6 +1902,10 @@ def _tb_read_whole(mb: memhub.MemBuddy*, path: str) -> str:
|
||||
got: LONG = f.read_all(buf, sizet(sz) + 2)
|
||||
f.close()
|
||||
if got < 0: return None
|
||||
# NUL-terminate at the actually read length. The pool buffer is NOT zero-filled (MemBuddy's
|
||||
# diagnostic fill is 0xCD), so without this the caller's strlen() walks into uninitialised
|
||||
# bytes and can run past the end of the arena.
|
||||
buf[got] = 0
|
||||
return buf
|
||||
|
||||
|
||||
@@ -1873,7 +1926,7 @@ def _tb_parse_f_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
|
||||
defline: int = int(_tb_parse_dec(s + p))
|
||||
p = f3 + 1
|
||||
file: str = _tb_dup_span(mb, s + p, b - p)
|
||||
if sym is None or file is None: return 0
|
||||
if _tb_str_ok(sym) == 0 or _tb_str_ok(file) == 0: return 0
|
||||
return _tb_cp_add_fr(mb, cp, sym, file, defline)
|
||||
|
||||
|
||||
@@ -1899,7 +1952,7 @@ def _tb_parse_c_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
|
||||
line: int = int(_tb_parse_dec(s + p))
|
||||
p = e4 + 1
|
||||
file: str = _tb_dup_span(mb, s + p, b - p)
|
||||
if caller is None or callee is None or file is None: return 0
|
||||
if _tb_str_ok(caller) == 0 or _tb_str_ok(callee) == 0 or _tb_str_ok(file) == 0: return 0
|
||||
return _tb_cp_add_cr(mb, cp, caller, callee, file, line)
|
||||
|
||||
|
||||
@@ -1967,7 +2020,7 @@ def _tb_build_groups(mb: memhub.MemBuddy*, cp: TbCallParse*) -> int:
|
||||
|
||||
def _tb_emit_call_row(mb: memhub.MemBuddy*, d: TbDump*, cp: TbCallParse*, img_lo: uint64, caller: str, callee: str, ret_va: uint64) -> int:
|
||||
"""Emit a row for a return address from the next unconsumed C record of (caller, callee)."""
|
||||
if caller is None or callee is None or cp.Grp is None or ret_va < img_lo: return 0
|
||||
if _tb_str_ok(caller) == 0 or _tb_str_ok(callee) == 0 or cp.Grp is None or ret_va < img_lo: return 0
|
||||
slot: int = _tb_grp_get(cp.Grp, cp.GrpCap, caller, callee, 0)
|
||||
if slot < 0: return 0
|
||||
goff: sizet = sizet(slot) * TB_GRP_SIZE
|
||||
@@ -1984,11 +2037,11 @@ def _tb_emit_call_row(mb: memhub.MemBuddy*, d: TbDump*, cp: TbCallParse*, img_lo
|
||||
|
||||
def _tb_emit_entry_row(mb: memhub.MemBuddy*, d: TbDump*, cp: TbCallParse*, img_lo: uint64, addr: uint64, sym: str) -> int:
|
||||
"""Emit a fallback function-entry row for an objdump label sym (file/line from the F record)."""
|
||||
if sym is None or cp.Fr is None or cp.FrN <= 0 or addr < img_lo: return 0
|
||||
if _tb_str_ok(sym) == 0 or cp.Fr is None or cp.FrN <= 0 or addr < img_lo: return 0
|
||||
for i in range(int(cp.FrN)):
|
||||
off: sizet = sizet(i) * TB_FR_SIZE
|
||||
s: str = rawptr(_tb_rd_u64(cp.Fr, off + TB_FR_OFF_SYM))
|
||||
if s is not None and string.strcmp(s, sym) == 0:
|
||||
if _tb_str_ok(s) != 0 and string.strcmp(s, sym) == 0:
|
||||
file: str = rawptr(_tb_rd_u64(cp.Fr, off + TB_FR_OFF_FILE))
|
||||
dl: int = int(_tb_rd_u32(cp.Fr, off + TB_FR_OFF_DEFLINE))
|
||||
return _tb_emit_row(mb, d, addr, file, dl, sym)
|
||||
@@ -2066,7 +2119,7 @@ def _tb_scan_objdump(mb: memhub.MemBuddy*, dump: str, img_lo: uint64, d: TbDump*
|
||||
j: sizet = lb
|
||||
while j < le and _tb_cc(dump, j) != 62: j += 1
|
||||
cur_func = _tb_dup_span(mb, dump + lb, j - lb)
|
||||
if cur_func is not None: _tb_emit_entry_row(mb, d, cp, img_lo, laddr, cur_func)
|
||||
if _tb_str_ok(cur_func) != 0: _tb_emit_entry_row(mb, d, cp, img_lo, laddr, cur_func)
|
||||
continue
|
||||
# ---- instruction line `ADDR: bytes mnemonic operands` ----
|
||||
k: sizet = ls
|
||||
@@ -2275,7 +2328,14 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
|
||||
pool_need: sizet = 1
|
||||
i = 0
|
||||
while i < uniq:
|
||||
pth: str = rawptr(_tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE))
|
||||
fptr: uint64 = _tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE)
|
||||
# fail-fast: Windows x64 user addresses stay below 0x7FFF_FFFF_FFFF; anything else is a
|
||||
# wild pointer (0, -1 or an uninitialised slot). Report the record instead of letting
|
||||
# strlen walk off into unmapped memory (ASan access-violation at 0xffffffffffffffff).
|
||||
if fptr < 65536 or fptr > 0x7FFFFFFFFFFF:
|
||||
VLogger.error(f"--traceback: internal error: line record {i} carries an invalid file-path pointer ({fptr})", "TRACEBACK")
|
||||
return 1
|
||||
pth: str = rawptr(fptr)
|
||||
pool_need += string.strlen(pth) + 1
|
||||
i += 1
|
||||
# our rows carry the function name directly (string pool); DWARF rows take it from the subprogram table.
|
||||
@@ -2283,13 +2343,21 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
|
||||
while i < uniq:
|
||||
fp: uint64 = _tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FUNC)
|
||||
if fp != 0:
|
||||
if fp > 0x7FFFFFFFFFFF:
|
||||
VLogger.error(f"--traceback: internal error: line record {i} carries an invalid function-name pointer ({fp})", "TRACEBACK")
|
||||
return 1
|
||||
fns: str = rawptr(fp)
|
||||
if fns is not None: pool_need += string.strlen(fns) + 1
|
||||
pool_need += string.strlen(fns) + 1
|
||||
i += 1
|
||||
i = 0
|
||||
while i < fkept:
|
||||
nm0: str = rawptr(_tb_rd_u64(d.FuncBuf, sizet(i) * TB_FUNC_SIZE + 16))
|
||||
if nm0 is not None: pool_need += string.strlen(nm0) + 1
|
||||
nmf: uint64 = _tb_rd_u64(d.FuncBuf, sizet(i) * TB_FUNC_SIZE + 16)
|
||||
if nmf != 0:
|
||||
if nmf > 0x7FFFFFFFFFFF:
|
||||
VLogger.error(f"--traceback: internal error: subprogram record {i} carries an invalid name pointer ({nmf})", "TRACEBACK")
|
||||
return 1
|
||||
nm0: str = rawptr(nmf)
|
||||
pool_need += string.strlen(nm0) + 1
|
||||
i += 1
|
||||
p: TbPool* = TbPool()
|
||||
if p is None:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
@@ -368,10 +367,10 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
class_names_buf: bytes = mb.alloc(sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN)
|
||||
parent_names_buf: bytes = mb.alloc(sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN)
|
||||
def_sha1s_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
|
||||
topo_order: t.CPtr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
|
||||
topo_count_box: t.CPtr = mb.alloc(4)
|
||||
generic_file_indices: t.CPtr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
|
||||
generic_file_count_box: t.CPtr = mb.alloc(4)
|
||||
topo_order: rawptr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
|
||||
topo_count_box: rawptr = mb.alloc(4)
|
||||
generic_file_indices: rawptr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
|
||||
generic_file_count_box: rawptr = mb.alloc(4)
|
||||
if generic_file_indices is not None: string.memset(generic_file_indices, 0, 4 * Phase1.MAX_FILE_DEPS)
|
||||
if generic_file_count_box is not None: string.memset(generic_file_count_box, 0, 4)
|
||||
topo_count: int = 0
|
||||
@@ -427,7 +426,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
file_idx: int = int(i)
|
||||
if topo_count > 0:
|
||||
od_addr: uint64 = uint64(topo_order) + i * 4
|
||||
od_ptr: int* = (int*)(t.CPtr(od_addr))
|
||||
od_ptr: int* = int*(od_addr)
|
||||
if od_ptr is not None: file_idx = od_ptr[0]
|
||||
|
||||
entry_addr_r: uint64 = uint64(result.Entries) + file_idx * entry_size
|
||||
|
||||
@@ -1,31 +1,14 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import sys
|
||||
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
|
||||
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
@@ -40,7 +23,7 @@ import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
# _ScanClassInheritance - pre-scan includes files, parse ASTs and collect ClassDef
|
||||
# names/parent names into flat byte arrays; returns the class count (-1 on failure).
|
||||
def _ScanClassInheritance(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, generic_file_indices: t.CPtr, generic_file_count_box: t.CPtr) -> int:
|
||||
def _ScanClassInheritance(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, generic_file_indices: rawptr, generic_file_count_box: rawptr) -> int:
|
||||
"""Pre-scan includes files and collect class inheritance info."""
|
||||
if result is None: return -1
|
||||
# The three flat buffers are mb.alloc'ed by the caller; a NULL buffer would make
|
||||
@@ -322,7 +305,7 @@ def _AnnIsContainer(ann: ast.AST*) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr, depth: int = 0):
|
||||
def _ScanTreeForContainers(node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr, depth: int = 0):
|
||||
"""Recursively scan the AST for list/dict container usage."""
|
||||
# Recursion depth guard: a self-referential AST cycle would recurse until the
|
||||
# stack is exhausted; fail-fast abort.
|
||||
|
||||
@@ -1,30 +1,14 @@
|
||||
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.
|
||||
@@ -32,7 +16,7 @@ import lib.Phase.Phase1 as Phase1
|
||||
|
||||
# _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:
|
||||
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: rawptr, out_count: rawptr, 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
|
||||
|
||||
@@ -44,9 +28,9 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
# 取消整数提升后,模块属性 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)
|
||||
file_dep_counts: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
file_indegrees: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
|
||||
file_processed: rawptr = 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)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdio
|
||||
import string
|
||||
@@ -38,8 +37,7 @@ define SRC_BUF_SIZE = 1048576
|
||||
# Max source files (recursive scanning grows the count; too small truncates the list -> undefined symbol)
|
||||
define MAX_SRC_FILES = 128
|
||||
|
||||
@t.NoVTable
|
||||
class SrcFileEntry:
|
||||
novirtual class SrcFileEntry:
|
||||
"""Source file entry"""
|
||||
Path: str # full path
|
||||
Sha1: str # first 16 chars of SHA1
|
||||
|
||||
Reference in new issue
Block a user