修正了一些错误

This commit is contained in:
2026-07-26 20:32:26 +08:00
parent ca7c2120b8
commit 1837339f69
203 changed files with 374300 additions and 2638 deletions

View File

@@ -1,81 +0,0 @@
"""
Auto-generated Python stub file from w32.fileio.py
Module: w32.fileio
"""
import t, c
from stdint import *
import stdio
import w32.win32base
import w32.win32file
class MODE(t.CEnum):
R = 0
W = 1
A = 2
RP = 3
WP = 4
AP = 5
X = 6
XP = 7
class FRESULT(t.CEnum):
OK = 0
ERR = -1
ERR_CLOSED = -2
ERR_PERM = -3
ERR_IO = -4
ERR_NOTFOUND = -5
SEEK_SET: t.CDefine = 0
SEEK_CUR: t.CDefine = 1
SEEK_END: t.CDefine = 2
INVALID_SET_FILE_POINTER: t.CDefine = -1
SHARE_READ: t.CDefine = 0x00000001
SHARE_WRITE: t.CDefine = 0x00000002
SHARE_DELETE: t.CDefine = 0x00000004
class File:
handle: w32.win32base.HANDLE
closed: bool
can_read: bool
can_write: bool
is_append: bool
_share_mode: ULONG
def __init__(self: File, filename: str, mode: ULONG, share: ULONG) -> t.CInt: pass
def __enter__(self: File) -> 'File' | t.CPtr: pass
def __exit__(self: File) -> t.CInt: pass
def read(self: File, buf: bytes, count: ULONG) -> LONG: pass
def write(self: File, buf: bytes, count: ULONG) -> LONG: pass
def write_str(self: File, s: str) -> LONG: pass
def seek(self: File, offset: LONG, origin: ULONG) -> LONG: pass
def tell(self: File) -> LONG: pass
def close(self: File) -> LONG: pass
def flush(self: File) -> LONG: pass
def size(self: File) -> LONGLONG: pass
def readline(self: File, buf: bytes, max_count: ULONG) -> LONG: pass
def read_all(self: File, buf: bytes, max_count: ULONG) -> LONG: pass
class FileW:
handle: w32.win32base.HANDLE
closed: bool
can_read: bool
can_write: bool
is_append: bool
_share_mode: ULONG
def __init__(self: FileW, filename: w32.win32base.LPCWSTR, mode: ULONG, share: ULONG) -> t.CInt: pass
def __enter__(self: FileW) -> 'FileW' | t.CPtr: pass
def __exit__(self: FileW) -> t.CInt: pass
def read(self: FileW, buf: bytes, count: ULONG) -> LONG: pass
def write(self: FileW, buf: bytes, count: ULONG) -> LONG: pass
def write_str(self: FileW, s: str) -> LONG: pass
def seek(self: FileW, offset: LONG, origin: ULONG) -> LONG: pass
def tell(self: FileW) -> LONG: pass
def close(self: FileW) -> LONG: pass
def flush(self: FileW) -> LONG: pass
def size(self: FileW) -> LONGLONG: pass
def readline(self: FileW, buf: bytes, max_count: ULONG) -> LONG: pass
def read_all(self: FileW, buf: bytes, max_count: ULONG) -> LONG: pass
def open(filename: str, mode: ULONG, share: ULONG) -> File | t.CPtr: pass
def openw(filename: LPCWSTR, mode: ULONG, share: ULONG) -> FileW | t.CPtr: pass

View File

@@ -1,100 +0,0 @@
"""
Auto-generated Python stub file from w32.win32base.py
Module: w32.win32base
"""
import c
import t
from stdint import *
HANDLE: t.CTypedef = VOIDPTR
LPCSTR: t.CTypedef = t.CConst | t.CChar | t.CPtr
LPCWSTR: t.CTypedef = t.CConst | t.CUnsignedShort | t.CPtr
INVALID_HANDLE_VALUE: t.CDefine = t.CVoid(-1)
NULL: t.CDefine = 0
TRUE: t.CDefine = 1
FALSE: t.CDefine = 0
INFINITE: t.CDefine = 0xFFFFFFFF
WAIT_FAILED: t.CDefine = 0xFFFFFFFF
WAIT_OBJECT_0: t.CDefine = 0
WAIT_TIMEOUT: t.CDefine = 258
WAIT_ABANDONED: t.CDefine = 0x80
MAX_PATH: t.CDefine = 260
ERROR_SUCCESS: t.CDefine = 0
ERROR_FILE_NOT_FOUND: t.CDefine = 2
ERROR_ACCESS_DENIED: t.CDefine = 5
ERROR_INSUFFICIENT_BUFFER: t.CDefine = 122
class SECURITY_ATTRIBUTES:
nLength: ULONG
lpSecurityDescriptor: VOIDPTR
bInheritHandle: BOOL
class OVERLAPPED:
Internal: ULONGLONG
InternalHigh: ULONGLONG
Offset: ULONG
OffsetHigh: ULONG
hEvent: HANDLE
class FILETIME:
dwLowDateTime: DWORD
dwHighDateTime: DWORD
class SYSTEMTIME:
wYear: WORD
wMonth: WORD
wDayOfWeek: WORD
wDay: WORD
wHour: WORD
wMinute: WORD
wSecond: WORD
wMilliseconds: WORD
class GUID:
Data1: DWORD
Data2: WORD
Data3: WORD
Data4: BYTEPTR
class LARGE_INTEGER:
QuadPart: LONGLONG
class ULARGE_INTEGER:
QuadPart: ULONGLONG
def GetLastError() -> ULONG | t.State: pass
def SetLastError(dwErrCode: ULONG) -> t.State: pass
def CloseHandle(hObject: HANDLE) -> BOOL | t.State: pass
def GetProcAddress(hModule: HANDLE, lpProcName: LPCSTR) -> VOIDPTR | t.State: pass
def GetModuleHandleA(lpModuleName: LPCSTR) -> HANDLE | t.State: pass
def GetModuleHandleW(lpModuleName: LPCWSTR) -> HANDLE | t.State: pass
def LoadLibraryA(lpLibFileName: LPCSTR) -> HANDLE | t.State: pass
def LoadLibraryW(lpLibFileName: LPCWSTR) -> HANDLE | t.State: pass
def FreeLibrary(hLibModule: HANDLE) -> BOOL | t.State: pass
def GetSystemTime(lpSystemTime: SYSTEMTIME | t.CPtr) -> t.State: pass
def GetLocalTime(lpSystemTime: SYSTEMTIME | t.CPtr) -> t.State: pass
def FileTimeToSystemTime(lpFileTime: FILETIME | t.CPtr, lpSystemTime: SYSTEMTIME | t.CPtr) -> BOOL | t.State: pass
def SystemTimeToFileTime(lpSystemTime: SYSTEMTIME | t.CPtr, lpFileTime: FILETIME | t.CPtr) -> BOOL | t.State: pass
def QueryPerformanceCounter(lpPerformanceCount: LARGE_INTEGER | t.CPtr) -> BOOL | t.State: pass
def QueryPerformanceFrequency(lpFrequency: LARGE_INTEGER | t.CPtr) -> BOOL | t.State: pass
def Sleep(dwMilliseconds: ULONG) -> t.State: pass
def SleepEx(dwMilliseconds: ULONG, bAlertable: BOOL) -> ULONG | t.State: pass
def GetTickCount() -> ULONG | t.State: pass
def GetTickCount64() -> ULONGLONG | t.State: pass
def GetCommandLineA() -> CHARPTR | t.State: pass

367074
1.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,8 @@ from lib.Projectrans import (
get_file_dependencies, find_reachable_files_from_entries,
topological_sort_files, Phase1Generator, DeclarationGenerator,
Phase2Translator, _parallel_translate_worker,
save_sha1_map, Load_sha1_map, Load_project_config, resolve_paths, main
save_sha1_map, Load_sha1_map, get_sha1_map_store, clear_sha1_map_store,
Load_project_config, resolve_paths, main
)
__all__ = [
@@ -15,7 +16,8 @@ __all__ = [
'get_file_dependencies', 'find_reachable_files_from_entries',
'topological_sort_files', 'Phase1Generator', 'DeclarationGenerator',
'Phase2Translator', '_parallel_translate_worker',
'save_sha1_map', 'Load_sha1_map', 'Load_project_config', 'resolve_paths', 'main'
'save_sha1_map', 'Load_sha1_map', 'get_sha1_map_store', 'clear_sha1_map_store',
'Load_project_config', 'resolve_paths', 'main'
]
if __name__ == '__main__':

4
TODO
View File

@@ -31,4 +31,6 @@ MCViper after 自举
# Ptr解引用含义冲突a[x]与ptr[x]冲突
__iter__ 期望 for a, b in __iter__
init_set 构造
init_set 构造
不在乎编译器时间的大小优化(无关符号剔除)

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true,
"startup": false

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -21,7 +21,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -28,7 +28,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": false
}

View File

@@ -21,7 +21,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -21,7 +21,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 0,
"sha1_slice_level": 0,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -23,7 +23,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -21,7 +21,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -23,7 +23,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -23,7 +23,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -23,7 +23,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -21,7 +21,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -29,7 +29,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -3,7 +3,9 @@ from stdint import *
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
from json.__parser import parse as json_parse
from json import JsonValue, JSON_OBJECT, JSON_INT
import w32.fileio as fileio
@@ -50,86 +52,132 @@ def Load_project_config(path: str) -> int:
global IncludesDir
if path is None:
stdio.printf("[CFG] FAIL: path is None\n")
stdio.fflush(0)
return 1
if _mbuddy is None:
stdio.printf("[CFG] FAIL: _mbuddy is None\n")
stdio.fflush(0)
return 1
# 打开文件
stdio.printf("[CFG] before File ctor path=%s\n", path)
stdio.fflush(0)
f: fileio.File | t.CPtr = fileio.File(path, fileio.MODE.R)
f_closed_flag: int = 1
if f is not None:
if f.closed:
f_closed_flag = 1
else:
f_closed_flag = 0
stdio.printf("[CFG] after File ctor f=%p closed=%d\n", f, f_closed_flag)
stdio.fflush(0)
if f is None:
stdio.printf("[CFG] FAIL: File ctor returned None\n")
stdio.fflush(0)
return 1
if f.closed:
stdio.printf("[CFG] FAIL: f.closed is True\n")
stdio.fflush(0)
return 1
# 分配读取缓冲区
CFG_BUF_SIZE: t.CSizeT = 8192
buf: bytes = _mbuddy.alloc(CFG_BUF_SIZE)
if buf is None:
stdio.printf("[CFG] FAIL: alloc buf failed\n")
stdio.fflush(0)
f.close()
return 1
stdio.printf("[CFG] before read_all\n")
stdio.fflush(0)
bytes_read: t.CInt64T = f.read_all(buf, CFG_BUF_SIZE)
f.close()
stdio.printf("[CFG] after read_all bytes_read=%d\n", bytes_read)
stdio.fflush(0)
if bytes_read <= 0:
stdio.printf("[CFG] FAIL: bytes_read <= 0\n")
stdio.fflush(0)
return 1
# 解析 JSON
stdio.printf("[CFG] before json_parse\n")
stdio.fflush(0)
root: JsonValue | t.CPtr = json_parse(_mbuddy, buf)
stdio.printf("[CFG] after json_parse root=%p\n", root)
stdio.fflush(0)
if root is None:
stdio.printf("[CFG] FAIL: json_parse returned None\n")
stdio.fflush(0)
return 1
if not root.is_object():
stdio.printf("[CFG] FAIL: root is not object\n")
stdio.fflush(0)
return 1
stdio.printf("[CFG] root is object OK\n")
stdio.fflush(0)
# 读取顶层字段
SourceDir = root["source_dir"].as_string() if root["source_dir"] is not None else None
# 读取顶层字段(使用显式 __getitem__ 调用,兼容旧编译器二进制)
sd_val: JsonValue | t.CPtr = root.__getitem__("source_dir")
SourceDir = sd_val.as_string() if sd_val is not None else None
# build_dir: 统一构建目录(默认 ./.tpv_buildtemp 和 output 作为其子目录
bd_val: JsonValue | t.CPtr = root["build_dir"]
bd_val: JsonValue | t.CPtr = root.__getitem__("build_dir")
if bd_val is not None and bd_val.is_string():
BuildDir = bd_val.as_string()
else:
BuildDir = "./.tpv_build"
ProjectName = root["name"].as_string() if root["name"] is not None else None
ProjectVersion = root["version"].as_string() if root["version"] is not None else None
name_val: JsonValue | t.CPtr = root.__getitem__("name")
ProjectName = name_val.as_string() if name_val is not None else None
ver_val: JsonValue | t.CPtr = root.__getitem__("version")
ProjectVersion = ver_val.as_string() if ver_val is not None else None
# 读取 compiler 子对象
compiler: JsonValue | t.CPtr = root["compiler"]
compiler: JsonValue | t.CPtr = root.__getitem__("compiler")
if compiler is not None and compiler.is_object():
CompilerCmd = compiler["cmd"].as_string() if compiler["cmd"] is not None else None
cc_cmd_val: JsonValue | t.CPtr = compiler.__getitem__("cmd")
CompilerCmd = cc_cmd_val.as_string() if cc_cmd_val is not None else None
# 读取 compiler.flags 数组,拼接成空格分隔的字符串
cc_flags_val: JsonValue | t.CPtr = compiler["flags"]
cc_flags_val: JsonValue | t.CPtr = compiler.__getitem__("flags")
CompilerFlags = _join_flags_array(cc_flags_val)
# 读取 linker 子对象
linker: JsonValue | t.CPtr = root["linker"]
linker: JsonValue | t.CPtr = root.__getitem__("linker")
if linker is not None and linker.is_object():
LinkerCmd = linker["cmd"].as_string() if linker["cmd"] is not None else None
LinkerOutput = linker["output"].as_string() if linker["output"] is not None else None
ld_cmd_val: JsonValue | t.CPtr = linker.__getitem__("cmd")
LinkerCmd = ld_cmd_val.as_string() if ld_cmd_val is not None else None
ld_out_val: JsonValue | t.CPtr = linker.__getitem__("output")
LinkerOutput = ld_out_val.as_string() if ld_out_val is not None else None
# 读取 linker.flags 数组,拼接成空格分隔的字符串
ld_flags_val: JsonValue | t.CPtr = linker["flags"]
ld_flags_val: JsonValue | t.CPtr = linker.__getitem__("flags")
LinkerFlags = _join_flags_array(ld_flags_val)
# 读取 target 子对象
target: JsonValue | t.CPtr = root["target"]
target: JsonValue | t.CPtr = root.__getitem__("target")
if target is not None and target.is_object():
TargetTriple = target["triple"].as_string() if target["triple"] is not None else None
TargetDataLayout = target["datalayout"].as_string() if target["datalayout"] is not None else None
tgt_triple_val: JsonValue | t.CPtr = target.__getitem__("triple")
TargetTriple = tgt_triple_val.as_string() if tgt_triple_val is not None else None
tgt_dl_val: JsonValue | t.CPtr = target.__getitem__("datalayout")
TargetDataLayout = tgt_dl_val.as_string() if tgt_dl_val is not None else None
# 读取 options 子对象
# Sha1SliceLevel: SHA1 切片层数(默认 1每层取 SHA1 前 2 字符作为子目录)
# level=0 → 不切片,文件直接放 base_dir
# level=1 → b7/{sha1}.ext
# level=2 → b7/90/{sha1}.ext
options: JsonValue | t.CPtr = root["options"]
options: JsonValue | t.CPtr = root.__getitem__("options")
Sha1SliceLevel = 1
StrictMode = 1
if options is not None and options.is_object():
sl: JsonValue | t.CPtr = options["sha1_slice_level"]
sl: JsonValue | t.CPtr = options.__getitem__("sha1_slice_level")
if sl is not None and sl.is_int():
Sha1SliceLevel = sl.as_int()
sm: JsonValue | t.CPtr = options["strict_mode"]
sm: JsonValue | t.CPtr = options.__getitem__("strict_mode")
if sm is not None and sm.is_bool():
StrictMode = 1 if sm.as_bool() else 0
# 读取 includes 数组(取首个元素作为 includes 目录)
includes_val: JsonValue | t.CPtr = root["includes"]
includes_val: JsonValue | t.CPtr = root.__getitem__("includes")
if includes_val is not None and includes_val.is_array():
arr_len: t.CSizeT = includes_val.__len__()
if arr_len > 0:
@@ -307,24 +355,38 @@ def get_includes_binary_dir() -> str:
def print_config():
"""打印当前配置(调试用)"""
"""打印当前配置"""
buf: t.CChar | t.CPtr = VLogger.fmt_buf()
if buf is None:
return
if ProjectName is not None:
stdio.printf(" project: %s v%s\n", ProjectName, ProjectVersion if ProjectVersion is not None else "?")
viperlib.snprintf(buf, 1024, "project: %s v%s", ProjectName,
ProjectVersion if ProjectVersion is not None else "?")
VLogger.info(buf, "config")
if SourceDir is not None:
stdio.printf(" source_dir: %s\n", SourceDir)
viperlib.snprintf(buf, 1024, "source_dir: %s", SourceDir)
VLogger.info(buf, "config")
if BuildDir is not None:
stdio.printf(" build_dir: %s\n", BuildDir)
viperlib.snprintf(buf, 1024, "build_dir: %s", BuildDir)
VLogger.info(buf, "config")
if TempDir is not None:
stdio.printf(" temp_dir: %s\n", TempDir)
viperlib.snprintf(buf, 1024, "temp_dir: %s", TempDir)
VLogger.info(buf, "config")
if OutputDir is not None:
stdio.printf(" output_dir: %s\n", OutputDir)
viperlib.snprintf(buf, 1024, "output_dir: %s", OutputDir)
VLogger.info(buf, "config")
if CompilerCmd is not None:
stdio.printf(" compiler: %s\n", CompilerCmd)
viperlib.snprintf(buf, 1024, "compiler: %s", CompilerCmd)
VLogger.info(buf, "config")
if LinkerCmd is not None:
stdio.printf(" linker: %s -> %s\n", LinkerCmd, LinkerOutput if LinkerOutput is not None else "?")
viperlib.snprintf(buf, 1024, "linker: %s -> %s", LinkerCmd,
LinkerOutput if LinkerOutput is not None else "?")
VLogger.info(buf, "config")
if IncludesDir is not None:
stdio.printf(" includes: %s\n", IncludesDir)
stdio.printf(" sha1_slice_level: %d\n", Sha1SliceLevel)
viperlib.snprintf(buf, 1024, "includes: %s", IncludesDir)
VLogger.info(buf, "config")
viperlib.snprintf(buf, 1024, "sha1_slice_level: %d", Sha1SliceLevel)
VLogger.info(buf, "config")
# ============================================================

View File

@@ -63,12 +63,14 @@ def compute_sha1(pool: memhub.MemBuddy | t.CPtr, content: str) -> str:
# ============================================================
# CleanDir - 删除目录中所有文件(非递归,保留目录本身)
# CleanDir - 递归删除目录中所有文件和子目录(保留 dir_path 本身)
#
# 用于 --clean 选项:清理 temp_dir 和 output_dir 中的旧文件。
# SHA1 分片目录结构(如 temp/1c/、temp/7a/)需要递归清理,
# 否则旧的截断文件(如 256KB text.ll会被 Phase1 跳过逻辑复用。
# ============================================================
def CleanDir(dir_path: str) -> int:
"""删除目录中所有文件(非递归),返回删除的文件数,-1 表示错误"""
"""递归删除目录中所有文件和子目录,保留 dir_path 本身,返回删除的文件数,-1 表示错误"""
if dir_path is None:
return -1
dir_len: t.CSizeT = string.strlen(dir_path)
@@ -104,8 +106,19 @@ def CleanDir(dir_path: str) -> int:
full_path: bytes = stdlib.malloc(dir_len + fname_len + 2)
if full_path is not None:
viperlib.snprintf(full_path, dir_len + fname_len + 2, "%s/%s", dir_path, fname)
if w32.win32file.DeleteFileA(full_path) != 0:
deleted += 1
# 检查是否为目录dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
is_dir: t.CUInt32T = find_data.dwFileAttributes & w32.win32file.FILE_ATTRIBUTE_DIRECTORY
if is_dir != 0:
# 递归清理子目录中的文件
sub_deleted: int = CleanDir(full_path)
if sub_deleted > 0:
deleted += sub_deleted
# 删除空子目录RemoveDirectoryA 要求目录为空)
w32.win32file.RemoveDirectoryA(full_path)
else:
# 删除文件
if w32.win32file.DeleteFileA(full_path) != 0:
deleted += 1
stdlib.free(full_path)
if w32.win32file.FindNextFileA(handle, find_data) == 0:

View File

@@ -10,6 +10,7 @@ import w32.win32base
import subprocess
import viperlib
import ast
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesType as HandlesType
@@ -68,6 +69,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
f.close()
if bytes_read <= 0:
stdlib.free(src_buf)
return None
if bytes_read < SRC_BUF_SIZE:
src_buf[bytes_read] = 0
@@ -77,6 +79,8 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str,
ast._lexer_init(lx, src_buf, mb)
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
# parse 完成后 AST 树已独立,释放源代码缓冲区(避免 1MB/文件泄漏)
stdlib.free(src_buf)
if tree is None:
return None
tr: HandlesTranslator.Translator | t.CPtr = mb.alloc(HandlesTranslator.Translator.__sizeof__())
@@ -268,12 +272,27 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
if result is None:
stdio.printf("[FATAL][LLC] subprocess.run 返回 None: %s\n", module_name)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "subprocess.run 返回 None: %s", module_name)
VLogger.error(fb, "LLC")
return 1
if result.returncode != 0:
stdio.printf("[FATAL][LLC] 编译失败 (module=%s, cmd=%s)\n", module_name, cmd)
# 先直接输出 llc 的具体错误信息VLogger.error 会 sys.exit必须先输出
# 注意: subprocess 在 Windows 下将 stderr 合并到 stdoutsi.hStdError = stdout_write
# 因此 result.stderr 总是 None错误信息在 result.stdout 中
# stdout 通常已含换行符,不再额外加 \n
if result.stdout is not None:
stdio.printf("[LLC] 输出:\n%s\n", result.stdout)
stdio.printf("%s", result.stdout)
stdio.fflush(0)
if result.stderr is not None:
stdio.printf("%s", result.stderr)
stdio.fflush(0)
# 最后输出编译失败摘要VLogger.error 会 sys.exit
fb = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "编译失败 (module=%s, cmd=%s)", module_name, cmd)
VLogger.error(fb, "LLC")
return 1
return 0
@@ -370,9 +389,15 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
if includes_binary_dir is not None:
extra_len = collect_obj_files(includes_binary_dir, extra_objs, EXTRA_BUF_SIZE)
if extra_len > 0:
stdio.printf("[link] 附加 %d 字节的 includes.binary .obj 文件\n", extra_len)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "附加 %d 字节的 includes.binary .obj 文件", extra_len)
VLogger.info(fb, "link")
else:
stdio.printf("[link] 警告: includes.binary 无 .obj 文件: %s\n", includes_binary_dir)
fb = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "includes.binary 无 .obj 文件: %s", includes_binary_dir)
VLogger.warning(fb, "link")
# 构造命令: clang++ {obj_path} extra_objs -o {output_dir}/{linker_output} linker_flags
# 注意: .obj 文件必须在 -l 库标志之前,否则链接器无法解析符号依赖
@@ -391,16 +416,25 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
if result is None:
stdio.printf("[link] subprocess.run 返回 None\n")
VLogger.error("subprocess.run 返回 None", "link")
return 1
if result.returncode != 0:
stdio.printf("[link] 链接失败,返回码: %d\n", result.returncode)
stdio.printf("[link] 命令: %s\n", cmd)
# 显示链接器错误输出subprocess 将 stderr 合并到 stdout
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "链接失败,返回码: %d", result.returncode)
VLogger.error(fb, "link")
fb = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "命令: %s", cmd)
VLogger.error(fb, "link")
# 显示链接器错误输出
# 注意: subprocess 在 Windows 下将 stderr 合并到 stdoutresult.stderr 总是 None
if result.stdout is not None:
stdio.printf("[link] 链接器输出:\n%s\n", result.stdout)
VLogger.error(result.stdout, "link")
else:
stdio.printf("[link] 无输出捕获\n")
VLogger.error("无输出捕获", "link")
if result.stderr is not None:
VLogger.error(result.stderr, "link")
return 1
return 0
@@ -428,7 +462,10 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT,
# Step 1: 写 .ll 文件
ret: int = write_ir_to_file(ir_buf, ir_len, temp_dir, module_name)
if ret != 0:
stdio.printf("[compile] 写 .ll 失败: %s\n", module_name)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "写 .ll 失败: %s", module_name)
VLogger.error(fb, "compile")
return 1
# Step 2: 构造切片 .ll 路径并编译 → .obj
@@ -438,7 +475,10 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: t.CSizeT,
ret = compile_ll_to_obj(ir_path, output_dir, module_name, cc_cmd, cc_flags)
if ret != 0:
stdio.printf("[compile] llc 编译失败: %s\n", module_name)
fb = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "llc 编译失败: %s", module_name)
VLogger.error(fb, "compile")
return 1
return 0
@@ -486,13 +526,22 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT,
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
if result is None:
stdio.printf("[link] subprocess.run 返回 None\n")
VLogger.error("subprocess.run 返回 None", "link")
return 1
if result.returncode != 0:
stdio.printf("[link] 链接失败,返回码: %d\n", result.returncode)
stdio.printf("[link] 命令: %s\n", cmd)
# 先直接输出 linker 的具体错误信息VLogger.error 会 sys.exit必须先输出
# stdout/stderr 通常已含换行符,不再额外加 \n
if result.stdout is not None:
stdio.printf("[link] 链接器输出:\n%s\n", result.stdout)
stdio.printf("%s", result.stdout)
stdio.fflush(0)
if result.stderr is not None:
stdio.printf("%s", result.stderr)
stdio.fflush(0)
# 最后输出链接失败摘要VLogger.error 会 sys.exit
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "链接失败,返回码: %d, 命令: %s", result.returncode, cmd)
VLogger.error(fb, "link")
return 1
return 0

View File

@@ -12,6 +12,7 @@ import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesStruct as HandlesStruct
# ============================================================
@@ -62,6 +63,64 @@ def extract_cdefine_int_value(val_node: ast.AST | t.CPtr) -> int:
return cn.int_val
# ============================================================
# _annotation_contains_name - 递归检查注解中是否包含指定名称
#
# 支持 BinOp(BitOr) 递归,同时检查 Attribute.attr 和 Name.id。
# ============================================================
def _annotation_contains_name(annot: ast.AST | t.CPtr, name: str) -> int:
"""递归检查注解中是否包含指定名称,返回 1=包含 / 0=不包含"""
if annot is None or name is None:
return 0
k: int = annot.kind()
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot)
if at.attr is not None and string.strcmp(at.attr, name) == 0:
return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot)
if nm.id is not None and string.strcmp(nm.id, name) == 0:
return 1
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if _annotation_contains_name(bop.left, name) != 0:
return 1
if _annotation_contains_name(bop.right, name) != 0:
return 1
return 0
# ============================================================
# _is_ptr_element_annotation - 检查注解是否为 bytes|t.CPtr 或 str|t.CPtr
#
# 返回 1=是(下标按 8 字节步长, i8** 语义), 0=否(下标按 1 字节步长, i8* 语义)
#
# 规则:
# bytes|t.CPtr / str|t.CPtr → i8**8 字节步长)→ 返回 1
# t.CChar|t.CPtr / t.CInt8T|t.CPtr → i8*1 字节步长)→ 返回 0
# 纯 bytes / str → i8*1 字节步长)→ 返回 0
#
# 注: 注解中的 "|" 是"和"的意思(组合类型),非右值中的位或运算
# ============================================================
def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int:
"""检查注解是否为 bytes|t.CPtr 或 str|t.CPtr 形式"""
if annot is None:
return 0
k: int = annot.kind()
if k != ast.ASTKind.BinOp:
return 0
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if bop.op != ast.OpKind.BitOr:
return 0
if _annotation_contains_name(annot, "str") != 0:
return 1
if _annotation_contains_name(annot, "bytes") != 0:
return 1
return 0
@t.NoVTable
class AnnAssignHandle(HandlesBase.Mixin):
"""AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针"""
@@ -121,7 +180,7 @@ class AnnAssignHandle(HandlesBase.Mixin):
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, annot, self.Trans._imported_modules, self.Trans._from_imports)
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is None:
# 尝试特化泛型类注解(如 list[str]
# resolve_annotation_type 返回 None 可能是因为泛型类未特化
@@ -138,11 +197,13 @@ class AnnAssignHandle(HandlesBase.Mixin):
self.Trans, sub_nm.id, type_args_ps)
if spec_name_ps is not None:
resolved = HandlesType.resolve_annotation_type(
pool, annot, self.Trans._imported_modules, self.Trans._from_imports)
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is not None:
var_ty = resolved
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, var_ty)
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
HandlesStruct.ensure_struct_def_in_module(pool, self.Trans.Module, var_ty)
alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, var_ty)
if alloca is not None:
if HandlesVar.define_var(
self.Trans.SymTab, nm.id, alloca) == 0:
@@ -153,6 +214,10 @@ class AnnAssignHandle(HandlesBase.Mixin):
if cls_nm_ps is not None:
HandlesVar.set_var_annot_class_name(
self.Trans.SymTab, nm.id, cls_nm_ps)
# 检查是否为 bytes|t.CPtr 或 str|t.CPtri8** 语义8 字节步长)
if _is_ptr_element_annotation(annot) != 0:
HandlesVar.set_var_ptr_element(
self.Trans.SymTab, nm.id)
return 0
return 0
@@ -193,7 +258,7 @@ class AnnAssignHandle(HandlesBase.Mixin):
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, annot, self.Trans._imported_modules, self.Trans._from_imports)
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is not None:
var_ty = resolved
@@ -243,6 +308,10 @@ class AnnAssignHandle(HandlesBase.Mixin):
if cls_nm_hd is not None:
HandlesVar.set_var_annot_class_name(
self.Trans.SymTab, nm.id, cls_nm_hd)
# 检查是否为 bytes|t.CPtr 或 str|t.CPtri8** 语义8 字节步长)
if _is_ptr_element_annotation(annot) != 0:
HandlesVar.set_var_ptr_element(
self.Trans.SymTab, nm.id)
new_vars: int = 0
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(

View File

@@ -6,6 +6,7 @@ import memhub
import string
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
@@ -44,12 +45,12 @@ class AssignHandle(HandlesBase.Mixin):
def Handle(self, node: ast.AST | t.CPtr) -> int:
asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
if asgn is None:
stdio.printf("[ASGN] cast failed\n")
VLogger.error("cast failed", "ASGN")
return 0
targets: list[ast.AST | t.CPtr] | t.CPtr = asgn.targets
if targets is None:
stdio.printf("[ASGN] targets is None\n")
VLogger.error("targets is None", "ASGN")
return 0
# 从 self.Trans 取共享状态(替代 11 个参数)
@@ -61,7 +62,23 @@ class AssignHandle(HandlesBase.Mixin):
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, asgn.value, None, 0, self.Trans)
if rhs_val is None:
stdio.printf("[ASGN] rhs_val is None\n")
# 增强错误信息:包含 sha1 + lineno + AST 节点类型,便于定位
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None and asgn.value is not None:
sha1: str = self.Trans.ModuleSha1
val_kind: int = asgn.value.kind()
val_line: t.CInt = asgn.value.lineno
if sha1 is not None:
viperlib.snprintf(fb, 1024,
"rhs_val is None [sha1=%s lineno=%d kind=%d]",
sha1, val_line, val_kind)
else:
viperlib.snprintf(fb, 1024,
"rhs_val is None [lineno=%d kind=%d]",
val_line, val_kind)
VLogger.error(fb, "ASGN")
else:
VLogger.error("rhs_val is None", "ASGN")
return 0
new_vars: int = 0
@@ -116,23 +133,115 @@ class AssignHandle(HandlesBase.Mixin):
sub_pointee: llvmlite.LLVMType | t.CPtr = sub_alloca.Ty.Pointee
if sub_pointee is not None:
cls_nm_set: str = HandlesStruct.get_class_name_by_type(pool, sub_pointee)
if cls_nm_set is not None:
obj_val_set: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, sub_pointee, sub_alloca)
if obj_val_set is not None:
key_val_set: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
if key_val_set is not None:
arg_vals_set: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_set is not None:
arg_vals_set[0] = t.CSizeT(key_val_set)
arg_vals_set[1] = t.CSizeT(rhs_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_set, "__setitem__",
obj_val_set, arg_vals_set, 2, self.Trans)
setitem_done = 1
obj_val_set: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, sub_pointee, sub_alloca)
# 指针类型变量 (X|t.CPtr): sub_pointee 是 Ptr(Struct),
# 需 load 获取 Ptr(Struct) 再检查 inner struct
if cls_nm_set is None and obj_val_set is not None:
if HandlesExpr.is_ptr_type(sub_pointee) != 0:
inner_ty_set: llvmlite.LLVMType | t.CPtr = sub_pointee.Pointee
if inner_ty_set is not None:
cls_nm_set = HandlesStruct.get_class_name_by_type(pool, inner_ty_set)
if cls_nm_set is not None and obj_val_set is not None:
key_val_set: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
if key_val_set is not None:
arg_vals_set: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_set is not None:
arg_vals_set[0] = t.CSizeT(key_val_set)
arg_vals_set[1] = t.CSizeT(rhs_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_set, "__setitem__",
obj_val_set, arg_vals_set, 2, self.Trans)
setitem_done = 1
elif sub_asgn.value.kind() == ast.ASTKind.Attribute:
# self._ht[key] = val → self._ht.__setitem__(key, val)
# 通过 get_attribute_ptr 获取字段指针,再调用 __setitem__
field_ptr_set: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
builder, pool, mod, sub_asgn.value, self.Trans)
if field_ptr_set is not None and field_ptr_set.Ty is not None:
if HandlesExpr.is_ptr_type(field_ptr_set.Ty) != 0:
field_pointee_set: llvmlite.LLVMType | t.CPtr = field_ptr_set.Ty.Pointee
if field_pointee_set is not None:
cls_nm_attr: str = HandlesStruct.get_class_name_by_type(pool, field_pointee_set)
obj_val_attr: llvmlite.Value | t.CPtr = field_ptr_set
# field_ptr 是 Ptr(Ptr(Struct)) (X|t.CPtr 字段):
# load 解引用获取 Ptr(Struct)
if cls_nm_attr is None and HandlesExpr.is_ptr_type(field_pointee_set) != 0:
inner_struct_attr: llvmlite.LLVMType | t.CPtr = field_pointee_set.Pointee
if inner_struct_attr is not None:
cls_nm_attr = HandlesStruct.get_class_name_by_type(pool, inner_struct_attr)
if cls_nm_attr is not None:
obj_val_attr = llvmlite.build_load(
builder, field_pointee_set, field_ptr_set)
if cls_nm_attr is not None and obj_val_attr is not None:
key_val_attr: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
if key_val_attr is not None:
arg_vals_attr: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_attr is not None:
arg_vals_attr[0] = t.CSizeT(key_val_attr)
arg_vals_attr[1] = t.CSizeT(rhs_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_attr, "__setitem__",
obj_val_attr, arg_vals_attr, 2, self.Trans)
setitem_done = 1
if setitem_done == 0:
HandlesType.fatal_error(target, "subscript ptr is None")
# 通用 fallback: 翻译 sub.value 并尝试 __setitem__ 或直接 GEP+store
# 处理 Name/Attribute 之外的节点以及 SymTab 查找失败的情况
if sub_asgn is not None and sub_asgn.value is not None:
obj_val_gen: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.value, None, 0, self.Trans)
key_val_gen: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, self.Trans)
if obj_val_gen is not None and key_val_gen is not None \
and obj_val_gen.Ty is not None:
# 内联 _get_custom_struct_cls_nm 逻辑(避免依赖新函数)
cls_nm_gen: str = HandlesStruct.get_class_name_by_type(
pool, obj_val_gen.Ty)
if cls_nm_gen is None and HandlesExpr.is_ptr_type(obj_val_gen.Ty) != 0:
inner_gen: llvmlite.LLVMType | t.CPtr = obj_val_gen.Ty.Pointee
if inner_gen is not None:
cls_nm_gen = HandlesStruct.get_class_name_by_type(pool, inner_gen)
if cls_nm_gen is not None:
# 自定义结构体: 调用 __setitem__
arg_vals_gen: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_gen is not None:
arg_vals_gen[0] = t.CSizeT(key_val_gen)
arg_vals_gen[1] = t.CSizeT(rhs_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_gen, "__setitem__",
obj_val_gen, arg_vals_gen, 2, self.Trans)
setitem_done = 1
else:
# 非自定义结构体: 直接 GEP + store
# 处理 get_subscript_ptr 因边界情况返回 None 的指针下标
if HandlesExpr.is_ptr_type(obj_val_gen.Ty) != 0:
elem_gen: llvmlite.LLVMType | t.CPtr = obj_val_gen.Ty.Pointee
if elem_gen is not None:
store_val_gen: llvmlite.Value | t.CPtr = rhs_val
elem_ptr_gen: llvmlite.Value | t.CPtr = None
match elem_gen:
case llvmlite.LLVMType.Array(arr_elem_gen, _):
elem_ptr_gen = llvmlite.build_gep_array(
builder, elem_gen, arr_elem_gen,
obj_val_gen, key_val_gen)
case _:
elem_ptr_gen = llvmlite.build_gep(
builder, elem_gen, obj_val_gen, key_val_gen)
if elem_ptr_gen is not None and elem_ptr_gen.Ty is not None:
pt_gen: llvmlite.LLVMType | t.CPtr = elem_ptr_gen.Ty.Pointee
if pt_gen is not None:
store_val_gen = HandlesExpr.coerce_to_type(
builder, rhs_val, pt_gen)
llvmlite.build_store(builder, store_val_gen, elem_ptr_gen)
setitem_done = 1
if setitem_done == 0:
sub_vk: int = sub_asgn.value.kind()
stdio.printf("[ASGN-SUB] fallback failed: val_kind=%d\n", sub_vk)
stdio.fflush(0)
if setitem_done == 0:
HandlesType.fatal_error(target, "subscript ptr is None")
continue
# Attribute 赋值: obj.field = val
@@ -140,14 +249,25 @@ class AssignHandle(HandlesBase.Mixin):
field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
builder, pool, mod, target, self.Trans)
if field_ptr is not None:
stdio.printf("[ASGN-ATTR] field_ptr ok ty_not_null=%d\n",
1 if field_ptr.Ty is not None else 0)
stdio.fflush(0)
# 获取字段类型,对 rhs_val 进行类型转换(如 i32 → i64
store_val: llvmlite.Value | t.CPtr = rhs_val
if field_ptr.Ty is not None:
field_ty: llvmlite.LLVMType | t.CPtr = field_ptr.Ty.Pointee
if field_ty is not None:
stdio.printf("[ASGN-ATTR] coerce rhs_ty=%d field_ty=%d\n",
HandlesExpr.get_llvm_type_bits(rhs_val.Ty),
HandlesExpr.get_llvm_type_bits(field_ty))
stdio.fflush(0)
store_val = HandlesExpr.coerce_to_type(
builder, rhs_val, field_ty)
stdio.printf("[ASGN-ATTR] pre_store\n")
stdio.fflush(0)
llvmlite.build_store(builder, store_val, field_ptr)
stdio.printf("[ASGN-ATTR] post_store\n")
stdio.fflush(0)
else:
# 构造详细错误信息
attr_node: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(target)
@@ -213,7 +333,10 @@ class AssignHandle(HandlesBase.Mixin):
if existing is None:
new_vars += 1
else:
stdio.printf("[ASGN] alloca failed for %s\n", nm.id)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "alloca failed for %s", nm.id)
VLogger.error(fb, "ASGN")
return new_vars

View File

@@ -5,6 +5,8 @@ import llvmlite
import memhub
import stdio
import string
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
@@ -58,7 +60,7 @@ class AugAssignHandle(HandlesBase.Mixin):
field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr(
builder, pool, mod, target, self.Trans)
if field_ptr is None:
stdio.printf("[AUGASGN] attribute ptr is None\n")
VLogger.error("attribute ptr is None", "AUGASGN")
return 0
# 2. 确定字段类型并加载当前值
@@ -68,21 +70,24 @@ class AugAssignHandle(HandlesBase.Mixin):
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, target_ty, field_ptr)
if cur_val is None:
stdio.printf("[AUGASGN] cannot load attribute\n")
VLogger.error("cannot load attribute", "AUGASGN")
return 0
# 3. 翻译 RHS 值
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, aug.value, None, 0, self.Trans)
if rhs_val is None:
stdio.printf("[AUGASGN] rhs is None\n")
VLogger.error("rhs is None", "AUGASGN")
return 0
# 4. 应用二元运算
result: llvmlite.Value | t.CPtr = _apply_aug_op(
pool, builder, aug.op, cur_val, rhs_val)
if result is None:
stdio.printf("[AUGASGN] binop failed for attr op=%d\n", aug.op)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "binop failed for attr op=%d", aug.op)
VLogger.error(fb, "AUGASGN")
return 0
# 5. 类型对齐并存储
@@ -92,8 +97,56 @@ class AugAssignHandle(HandlesBase.Mixin):
llvmlite.build_store(builder, result, field_ptr)
return 0
# Subscript 目标: self.state[i] += x / arr[i] += x
# 流程: get_subscript_ptr → load → binop → store
if tk == ast.ASTKind.Subscript:
pool: memhub.MemBuddy | t.CPtr = self.Trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module
# 1. 获取元素指针
elem_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_subscript_ptr(
builder, pool, mod, target, self.Trans)
if elem_ptr is None:
VLogger.error("subscript ptr is None", "AUGASGN")
return 0
# 2. 确定元素类型并加载当前值
target_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
target_ty = elem_ptr.Ty.Pointee
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, target_ty, elem_ptr)
if cur_val is None:
VLogger.error("cannot load subscript element", "AUGASGN")
return 0
# 3. 翻译 RHS 值
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, aug.value, None, 0, self.Trans)
if rhs_val is None:
VLogger.error("rhs is None", "AUGASGN")
return 0
# 4. 应用二元运算
result: llvmlite.Value | t.CPtr = _apply_aug_op(
pool, builder, aug.op, cur_val, rhs_val)
if result is None:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "binop failed for sub op=%d", aug.op)
VLogger.error(fb, "AUGASGN")
return 0
# 5. 类型对齐并存储
result = HandlesExpr.coerce_to_type(builder, result, target_ty)
if result is None:
return 0
llvmlite.build_store(builder, result, elem_ptr)
return 0
if tk != ast.ASTKind.Name:
stdio.printf("[AUGASGN] only Name/Attribute target supported\n")
VLogger.error("only Name/Attribute/Subscript target supported", "AUGASGN")
return 0
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
@@ -139,14 +192,17 @@ class AugAssignHandle(HandlesBase.Mixin):
cur_val = llvmlite.build_load(builder, target_ty, target_alloca)
if cur_val is None:
stdio.printf("[AUGASGN] cannot load target %s\n", nm.id)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "cannot load target %s", nm.id)
VLogger.error(fb, "AUGASGN")
return 0
# 3. 翻译 RHS 值
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, aug.value, None, 0, self.Trans)
if rhs_val is None:
stdio.printf("[AUGASGN] rhs is None\n")
VLogger.error("rhs is None", "AUGASGN")
return 0
# 4. 类型对齐 + 应用二元运算
@@ -154,7 +210,10 @@ class AugAssignHandle(HandlesBase.Mixin):
result: llvmlite.Value | t.CPtr = _apply_aug_op(
pool, builder, aug.op, cur_val, rhs_val)
if result is None:
stdio.printf("[AUGASGN] binop failed for op=%d\n", aug.op)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "binop failed for op=%d", aug.op)
VLogger.error(fb, "AUGASGN")
return 0
# 5. 存储结果
@@ -226,6 +285,8 @@ def _apply_aug_op(pool: memhub.MemBuddy | t.CPtr,
return llvmlite.build_mul(builder, lhs, rhs)
elif op == ast.OpKind.Div:
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv:
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod:
return llvmlite.build_srem(builder, lhs, rhs)
elif op == ast.OpKind.BitAnd:

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,8 @@ import memhub
import string
import llvmlite
import stdio
import viperlib
import lib.core.VLogger as VLogger
# ============================================================
@@ -110,7 +112,10 @@ def register_enum(pool: memhub.MemBuddy | t.CPtr,
return existing
if _enum_count >= ENUM_MAX:
stdio.printf("[ENUM] table full, cannot register %s\n", name)
fb_et: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_et is not None:
viperlib.snprintf(fb_et, 1024, "table full, cannot register %s", name)
VLogger.error(fb_et, "ENUM")
return None
entry: EnumEntry | t.CPtr = _get_enum_entry(_enum_count)
@@ -151,7 +156,10 @@ def add_enum_member(pool: memhub.MemBuddy | t.CPtr,
return -1
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
stdio.printf("[ENUM] member table full for %s\n", enum_entry.Name)
fb_em: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_em is not None:
viperlib.snprintf(fb_em, 1024, "member table full for %s", enum_entry.Name)
VLogger.error(fb_em, "ENUM")
return -1
idx: int = enum_entry.MemberCount

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -272,6 +272,8 @@ def translate_binop(pool: memhub.MemBuddy | t.CPtr,
return llvmlite.build_mul(builder, lhs, rhs)
elif op == ast.OpKind.Div:
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv:
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod:
return llvmlite.build_srem(builder, lhs, rhs)
elif op == ast.OpKind.BitAnd:

View File

@@ -138,7 +138,7 @@ class ForHandle(HandlesBase.Mixin):
trans.SymTab, var_name)
new_vars: int = 0
if var_alloca is None:
var_alloca = llvmlite.build_alloca(builder, i32_ty)
var_alloca = HandlesVar._alloca_at_entry(builder, i32_ty)
if HandlesVar.define_var(
trans.SymTab, var_name, var_alloca) == 0:
new_vars = 1
@@ -294,13 +294,13 @@ class ForHandle(HandlesBase.Mixin):
trans.SymTab, var_name)
new_vars: int = 0
if var_alloca is None:
var_alloca = llvmlite.build_alloca(builder, elem_ty)
var_alloca = HandlesVar._alloca_at_entry(builder, elem_ty)
if HandlesVar.define_var(
trans.SymTab, var_name, var_alloca) == 0:
new_vars = 1
# 创建隐式 index 变量,初始为 0
idx_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i32_ty)
idx_alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, i32_ty)
llvmlite.build_store(builder, llvmlite.const_int32(pool, 0), idx_alloca)
# 创建基本块: cond / body / incr / end

View File

@@ -7,6 +7,7 @@ import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExprCall as HandlesExprCall
@@ -17,6 +18,10 @@ import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesImports as HandlesImports
# 作用域类型常量(本地副本,避免旧编译器跨模块 CDefine 查找 bug
SCOPE_FUNCTION: t.CDefine = 1
# ============================================================
# extract_func_attrs - 从 decorator_list 提取 c.Attribute 属性
#
@@ -311,7 +316,7 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr,
ret_ty: llvmlite.LLVMType | t.CPtr = None
if fd.returns is not None:
ret_ty = HandlesType.resolve_annotation_type(
pool, fd.returns, imported_modules, from_imports)
pool, fd.returns, imported_modules, from_imports, trans)
if ret_ty is None and fd.returns is not None:
if HandlesType.has_decorator_marker(fd.returns, "State") != 0:
ret_ty = llvmlite.Void(pool)
@@ -347,10 +352,24 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr,
if func is None:
continue
# 提取默认参数信息
fd_args_node: ast.Arguments | t.CPtr = fd.args
fd_defaults: list[ast.AST | t.CPtr] | t.CPtr = None
fd_default_count: int = 0
fd_param_count: int = 0
if fd_args_node is not None:
fd_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd_args_node)
if fd_ags.args is not None:
fd_param_count = fd_ags.args.__len__()
if fd_ags.defaults is not None:
fd_defaults = fd_ags.defaults
fd_default_count = fd_ags.defaults.__len__()
# 注册到函数表(用裸名 fd.name不是 mangled_name
max_funcs: int = 256
cur_count: int = trans._func_count
if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs) == 0:
if HandlesExprCall.add_func_to_table(funcs_ptr, cur_count, fd.name, func, max_funcs,
fd_defaults, fd_default_count, fd_param_count) == 0:
trans._func_count = cur_count + 1
# 注册 CExport 函数到全局表
@@ -375,7 +394,7 @@ def forward_declare_functions(trans: HT.Translator | t.CPtr,
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
if arg.annotation is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, arg.annotation, imported_modules, from_imports)
pool, arg.annotation, imported_modules, from_imports, trans)
if resolved is not None:
param_ty = resolved
pname: t.CChar | t.CPtr = pool.alloc(32)
@@ -412,7 +431,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
ret_ty: llvmlite.LLVMType | t.CPtr = None
if fd.returns is not None:
ret_ty = HandlesType.resolve_annotation_type(
pool, fd.returns, imported_modules, from_imports)
pool, fd.returns, imported_modules, from_imports, trans)
# 如果返回类型注解纯装饰器标记(如 t.State无实际类型使用 void
# 注意:必须在 infer_return_type 之前检测,因为 infer_return_type 至少返回 i32
@@ -490,7 +509,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
param_ty_e: llvmlite.LLVMType | t.CPtr = i32_ty
if arg_e.annotation is not None:
resolved_e: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, arg_e.annotation, imported_modules, from_imports)
pool, arg_e.annotation, imported_modules, from_imports, trans)
if resolved_e is not None:
param_ty_e = resolved_e
pname_e: t.CChar | t.CPtr = pool.alloc(32)
@@ -521,7 +540,10 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
# 创建新的 LLVM 函数(使用 SHA1 混淆名)
func = llvmlite.create_function(pool, mod, mangled_name, ret_ty)
if func is None:
stdio.printf("[FUNC] create_function %s failed\n", fd.name)
fb_cf: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_cf is not None:
viperlib.snprintf(fb_cf, 1024, "create_function %s failed", fd.name)
VLogger.error(fb_cf, "FUNC")
return 0
# 提取 @c.Attribute 装饰器属性并设置到函数
@@ -529,9 +551,22 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
if func_attrs is not None:
llvmlite.function_set_attrs(func, func_attrs)
# 提取默认参数信息
tfd_defaults: list[ast.AST | t.CPtr] | t.CPtr = None
tfd_default_count: int = 0
tfd_param_count: int = 0
if args_node is not None:
tfd_ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
if tfd_ags.args is not None:
tfd_param_count = tfd_ags.args.__len__()
if tfd_ags.defaults is not None:
tfd_defaults = tfd_ags.defaults
tfd_default_count = tfd_ags.defaults.__len__()
# 注册到函数表
max_funcs: int = 256
if HandlesExprCall.add_func_to_table(funcs_ptr, func_count, fd.name, func, max_funcs) == 0:
if HandlesExprCall.add_func_to_table(funcs_ptr, func_count, fd.name, func, max_funcs,
tfd_defaults, tfd_default_count, tfd_param_count) == 0:
trans._func_count = func_count + 1
# 添加参数(支持类型注解)
@@ -550,7 +585,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
if arg.annotation is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, arg.annotation, imported_modules, from_imports)
pool, arg.annotation, imported_modules, from_imports, trans)
if resolved is not None:
param_ty = resolved
pname: t.CChar | t.CPtr = pool.alloc(32)
@@ -569,7 +604,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
llvmlite.position_at_end(func_builder, entry_blk)
# 进入函数作用域(嵌套符号表)
HandlesVar.enter_scope(trans.SymTab, HandlesVar.SCOPE_FUNCTION)
HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION)
# 为参数创建 alloca
if args_node is not None:
@@ -587,7 +622,7 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
param_ty2: llvmlite.LLVMType | t.CPtr = i32_ty
if arg2.annotation is not None:
resolved2: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, arg2.annotation, imported_modules, from_imports)
pool, arg2.annotation, imported_modules, from_imports, trans)
if resolved2 is not None:
param_ty2 = resolved2
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, param_ty2)
@@ -633,6 +668,8 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
for bi2 in range(bn2):
stmt2: ast.AST | t.CPtr = body.get(bi2)
if stmt2 is not None:
stdio.printf("[DBG] TR body stmt=%d kind=%d\n", bi2, stmt2.kind())
stdio.fflush(0)
HandlesBody.translate_stmt(trans, stmt2)
# 如果函数体最后一条语句不是 Return添加隐式 ret
@@ -644,8 +681,24 @@ def translate_function_def(trans: HT.Translator | t.CPtr,
if last_stmt is not None and last_stmt.kind() == ast.ASTKind.Return:
last_is_return = 1
if last_is_return == 0:
zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
llvmlite.build_ret(func_builder, zero_val)
if llvmlite.builder_cur_block_is_terminated(func_builder) == 0:
# 根据返回类型生成正确的零值返回
_is_void: int = 0
_is_ptr: int = 0
if ret_ty is not None:
match ret_ty:
case llvmlite.LLVMType.Void():
_is_void = 1
case llvmlite.LLVMType.Ptr(_pe):
_is_ptr = 1
if _is_void != 0:
llvmlite.build_ret_void(func_builder)
elif _is_ptr != 0:
_null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null")
llvmlite.build_ret(func_builder, _null_val)
else:
_zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
llvmlite.build_ret(func_builder, _zero_val)
# 恢复模块级作用域(退出函数作用域)
HandlesVar.exit_scope(trans.SymTab)
@@ -706,7 +759,7 @@ def translate_nested_function_def(trans: HT.Translator | t.CPtr,
llvmlite.position_at_end(func_builder, entry_blk)
# 4. 进入嵌套函数作用域(嵌套符号表)
HandlesVar.enter_scope(trans.SymTab, HandlesVar.SCOPE_FUNCTION)
HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION)
# 5. 创建 _env_ptr alloca 并存储 %env 参数
env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty)
@@ -927,7 +980,7 @@ def create_function(pool: memhub.MemBuddy | t.CPtr,
param_ty: llvmlite.LLVMType | t.CPtr = i32_ty
if arg.annotation is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, arg.annotation, None, None)
pool, arg.annotation, None, None, None)
if resolved is not None:
param_ty = resolved
pname: t.CChar | t.CPtr = pool.alloc(32)

View File

@@ -3,6 +3,7 @@ from stdint import *
import ast
import memhub
import string
import stdlib
import viperlib
import stdio
import lib.core.Handles.HandlesBase as HandlesBase
@@ -88,14 +89,24 @@ def is_module_imported(imported_modules: str, name: str) -> int:
def add_from_import(pool: memhub.MemBuddy | t.CPtr,
from_imports: str,
local_name: str,
module_name: str) -> str:
"""添加 from-import 映射 "name:module",返回新的 from_imports 字符串"""
module_name: str,
original_name: str = None) -> str:
"""添加 from-import 映射,返回新的 from_imports 字符串
格式:
无别名: "local_name:module_name"
有别名: "local_name:module_name:original_name"
original_name 为源模块中的真实函数名,用于跨模块 SHA1 修饰)
"""
if local_name is None or module_name is None:
return from_imports
entry: t.CChar | t.CPtr = pool.alloc(128)
entry: t.CChar | t.CPtr = pool.alloc(256)
if entry is None:
return from_imports
viperlib.snprintf(entry, 128, "%s:%s", local_name, module_name)
if original_name is not None and original_name != local_name:
viperlib.snprintf(entry, 256, "%s:%s:%s", local_name, module_name, original_name)
else:
viperlib.snprintf(entry, 256, "%s:%s", local_name, module_name)
if from_imports is None:
return entry
else:
@@ -111,6 +122,75 @@ def add_from_import(pool: memhub.MemBuddy | t.CPtr,
return from_imports
# ============================================================
# 查找 from-import 名称 → 返回原始函数名(别名场景)
#
# 对于 from X import Y as Z返回 Y源模块中的真实函数名
# 无别名时返回 Nonelocal_name 即为原始名)。
# ============================================================
def lookup_from_import_original(from_imports: str,
local_name: str) -> str:
"""查找 from-import 别名对应的原始函数名,返回 None=无别名或未找到"""
if local_name is None or from_imports is None:
return None
name_len: t.CSizeT = string.strlen(local_name)
cur: t.CChar | t.CPtr = from_imports
ci: t.CSizeT = 0
total_len: t.CSizeT = string.strlen(from_imports)
while ci < total_len:
# 跳过前导空格
while ci < total_len and cur[ci] == ' ':
ci += 1
if ci >= total_len:
break
# 找到第一个 ':' 的位置
colon1: t.CSizeT = ci
while colon1 < total_len and cur[colon1] != ':' and cur[colon1] != ' ':
colon1 += 1
if colon1 >= total_len or cur[colon1] != ':':
break
entry_name_len: t.CSizeT = colon1 - ci
# 跳过 star import
if entry_name_len == 1 and cur[ci] == '*':
ci = colon1
while ci < total_len and cur[ci] != ' ':
ci += 1
continue
# 比较名称
if entry_name_len == name_len:
match: int = 1
ei: t.CSizeT = 0
while ei < name_len:
if cur[ci + ei] != local_name[ei]:
match = 0
break
ei += 1
if match == 1:
# 找到匹配,检查是否有第三个字段(原始名)
pos: t.CSizeT = colon1 + 1
# 跳过模块名
while pos < total_len and cur[pos] != ':' and cur[pos] != ' ' and cur[pos] != '\0':
pos += 1
if pos < total_len and cur[pos] == ':':
# 有第三个字段: original_name
orig_start: t.CSizeT = pos + 1
orig_end: t.CSizeT = orig_start
while orig_end < total_len and cur[orig_end] != ' ' and cur[orig_end] != '\0':
orig_end += 1
orig_len: t.CSizeT = orig_end - orig_start
if orig_len > 0:
orig_buf: str = cur + orig_start
# 返回指向内部的指针(调用方需在使用期内保持 from_imports 有效)
return orig_buf
return None
return None
# 跳到下一个条目
ci = colon1
while ci < total_len and cur[ci] != ' ':
ci += 1
return None
# ============================================================
# 查找 from-import 名称 → 返回模块名或 None
#
@@ -150,7 +230,7 @@ def lookup_from_import(from_imports: str, name: str,
if entry_name_len == 1 and cur[ci] == '*':
mod_start: t.CSizeT = colon_pos + 1
mod_end: t.CSizeT = mod_start
while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0':
while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0' and cur[mod_end] != ':':
mod_end += 1
star_mod = cur + mod_start
# 比较名称
@@ -165,7 +245,8 @@ def lookup_from_import(from_imports: str, name: str,
if match == 1:
mod_start2: t.CSizeT = colon_pos + 1
mod_end2: t.CSizeT = mod_start2
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0':
# 模块名结束于: 空格、null、或第二个':'(别名格式的分隔符)
while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':':
mod_end2 += 1
return cur + mod_start2
# 跳到下一个条目
@@ -178,6 +259,93 @@ def lookup_from_import(from_imports: str, name: str,
return star_mod
# ============================================================
# 全局 re-export 映射表module_sha1 + func_name → source_module
#
# 当 ast/__init__.py 中有 from .lexer import _lexer_init 时,
# _lexer_init 被 re-export 为 ast._lexer_init。
# 跨模块调用 ast._lexer_init(...) 需使用 ast.lexer 的 SHA1 混淆,
# 而非 ast 的 SHA1。
#
# 数据布局:
# _g_reexport_sha1s: 每条 17 字节SHA1 16字符 + null
# _g_reexport_funcs: 每条 64 字节(函数名 + null
# _g_reexport_srcs: 每条 64 字节(源模块名 + null
# ============================================================
MAX_REEXPORT: t.CDefine = 512
_g_reexport_sha1s: bytes = None
_g_reexport_funcs: bytes = None
_g_reexport_srcs: bytes = None
_g_reexport_count: int = 0
# ============================================================
# register_reexport - 注册 re-export 映射
#
# 在 HandleImportFromNames 中调用:当处理 from .X import Y 时,
# 记录当前模块(通过 ModuleSha1re-export 了函数 Y源模块为 X。
#
# 幂等:重复注册相同映射不会增加条目。
# 内存用 stdlib.malloc 分配(全局存储器,跨 Phase 持久化)。
# ============================================================
def register_reexport(mod_sha1: str, func_name: str, source_module: str) -> int:
"""注册 re-export 映射(幂等),返回 0 成功,-1 失败"""
global _g_reexport_sha1s, _g_reexport_funcs, _g_reexport_srcs, _g_reexport_count
if mod_sha1 is None or func_name is None or source_module is None:
return -1
# 懒初始化
if _g_reexport_sha1s is None:
_g_reexport_sha1s = stdlib.malloc(MAX_REEXPORT * 17)
_g_reexport_funcs = stdlib.malloc(MAX_REEXPORT * 64)
_g_reexport_srcs = stdlib.malloc(MAX_REEXPORT * 64)
if _g_reexport_sha1s is None or _g_reexport_funcs is None or _g_reexport_srcs is None:
return -1
string.memset(_g_reexport_sha1s, 0, MAX_REEXPORT * 17)
string.memset(_g_reexport_funcs, 0, MAX_REEXPORT * 64)
string.memset(_g_reexport_srcs, 0, MAX_REEXPORT * 64)
if _g_reexport_count >= MAX_REEXPORT:
return -1
# 幂等检查:查找是否已存在相同映射
for i in range(_g_reexport_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
fidx: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0:
if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0:
# 已存在,更新 source_module以防变化
string.strcpy(_g_reexport_srcs + fidx, source_module)
return 0
# 添加新条目
idx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 17
fidx2: t.CSizeT = t.CSizeT(_g_reexport_count) * 64
string.strcpy(_g_reexport_sha1s + idx2, mod_sha1)
string.strcpy(_g_reexport_funcs + fidx2, func_name)
string.strcpy(_g_reexport_srcs + fidx2, source_module)
_g_reexport_count += 1
return 0
# ============================================================
# lookup_reexport - 查找 re-export 映射
#
# 给定模块 SHA1 和函数名,返回源模块名(如 "ast.lexer")或 None。
# 调用方通过 _lookup_module_sha1(source_module) 获取源模块的 SHA1。
# ============================================================
def lookup_reexport(mod_sha1: str, func_name: str) -> str:
"""查找 re-export 映射,返回源模块名或 None"""
if mod_sha1 is None or func_name is None:
return None
if _g_reexport_sha1s is None or _g_reexport_count <= 0:
return None
for i in range(_g_reexport_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
fidx: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(_g_reexport_sha1s + sidx, mod_sha1) == 0:
if string.strcmp(_g_reexport_funcs + fidx, func_name) == 0:
return _g_reexport_srcs + fidx
return None
# ============================================================
# _resolve_relative_module - 解析相对导入为完整模块名
#
@@ -335,6 +503,24 @@ class ImportsHandle(HandlesBase.Mixin):
if alias.asname is not None:
self.Trans._imported_modules = add_imported_module(
pool_val, self.Trans._imported_modules, alias.asname)
# 别名也加入 from_importsasname:fullname
# 使 HT.func() 跨模块调用能通过 from_imports 查找别名→完整模块名→SHA1
# 否则 _lookup_module_sha1("HT") 找不到mod_arr 只存文件名 HandlesTranslator
self.Trans._from_imports = add_from_import(
pool_val, self.Trans._from_imports, alias.asname, alias.name)
else:
# 无别名时import a.b.c将顶层包名 "a" 也加入 _imported_modules
# 使 a.b.c 作为属性访问的 base 被翻译时能通过 is_module_imported("a") 检查
dot_ptr: str = string.strstr(alias.name, '.')
if dot_ptr is not None:
pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_ptr) - t.CUInt64T(alias.name))
if pkg_len > 0 and pkg_len < 256:
pkg_buf: bytes = pool_val.alloc(pkg_len + 1)
if pkg_buf is not None:
string.strncpy(pkg_buf, alias.name, pkg_len)
pkg_buf[pkg_len] = '\0'
self.Trans._imported_modules = add_imported_module(
pool_val, self.Trans._imported_modules, pkg_buf)
return 0
# ============================================================
@@ -381,13 +567,19 @@ class ImportsHandle(HandlesBase.Mixin):
alias: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names.get(ni))
if alias is not None and alias.name is not None:
local_name: str = alias.name
orig_name: str = None
if alias.asname is not None:
local_name = alias.asname
orig_name = alias.name
self.Trans._from_imports = add_from_import(
self.Trans.Pool, self.Trans._from_imports,
local_name, resolved)
local_name, resolved, orig_name)
# 命名空间隔离from-import 的名称标记为可见结构体
HandlesStruct.add_visible_struct(self.Trans.Pool, local_name)
# 注册 re-export 映射:当前模块 re-export 了 local_name源模块为 resolved
# 使跨模块调用 module.func() 能解析到正确的源模块 SHA1
if self.Trans.ModuleSha1 is not None:
register_reexport(self.Trans.ModuleSha1, local_name, resolved)
return 0

View File

@@ -6,6 +6,7 @@ import memhub
import string
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExprCall as HandlesExprCall
@@ -96,6 +97,10 @@ def translate_children(trans: HT.Translator | t.CPtr,
# Phase 1a 声明模式:只注册 CExport/State 函数到全局表(解决翻译顺序依赖)
# Phase 1b 全量翻译:正常翻译函数体
if trans._declare_only == 0:
fd_dbg: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
if fd_dbg is not None and fd_dbg.name is not None:
stdio.printf("[DBG] TR func=%s\n", fd_dbg.name)
stdio.fflush(0)
added: int = HandlesFunctions.translate_function_def(trans, child)
added_total += added
elif trans._declare_only == 1:
@@ -104,6 +109,10 @@ def translate_children(trans: HT.Translator | t.CPtr,
# ClassDef 在模块级直接处理(不需要 builder
# _declare_only=2import扫描模式时跳过只处理 import 依赖
if trans._declare_only != 2:
cd_dbg: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
if cd_dbg is not None and cd_dbg.name is not None:
stdio.printf("[DBG] TR class=%s\n", cd_dbg.name)
stdio.fflush(0)
HandlesClassDef.translate_class_def(trans, child)
elif trans._declare_only == 0 and trans._cur_builder is not None:
# 有 builder → 委托 HandlesBody 分派
@@ -171,7 +180,7 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
# 解析类型
if aa.annotation is not None:
resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, aa.annotation, trans._imported_modules, trans._from_imports)
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
if resolved is not None:
var_ty = resolved
# 解析初始值
@@ -208,6 +217,11 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
return 0
# 创建全局变量 @var_name
# 先确保 var_ty 中的跨模块结构体类型在模块中有 opaque 声明
# 否则 @var_name = global %"sha1.ClassName"* zeroinitializer 会报
# "use of undefined type named 'sha1.ClassName'" 错误
# (如 _mbuddy: memhub.MemManager | t.CPtr → Ptr(Struct("aeb3...MemManager"))
llvmlite.module_ensure_opaque_for_type(mod, pool, var_ty)
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, var_name, var_ty)
if gv is None:
return 0
@@ -221,7 +235,9 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
viperlib.snprintf(init_buf, 48, "%lld", init_val)
gv.Initializer = init_buf
else:
gv.Initializer = "0"
# 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer
# 整数/聚合类型也兼容 zeroinitializer避免 "integer constant must have integer type"
gv.Initializer = "zeroinitializer"
# 创建 Value 引用(@var_name, 类型为 var_ty*
var_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, var_ty)
@@ -232,6 +248,16 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
# 注册到模块作用域
if HandlesVar.define_module_var(trans.SymTab, var_name, gv_ref) == 0:
# 设置 AnnotClassName联合类型如 Logger|t.CPtr 简化为 Ptr(i8) 时,
# 属性访问需要通过 AnnotClassName 回退到类名查找结构体)
if k == ast.ASTKind.AnnAssign:
aa_ml: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
if aa_ml is not None and aa_ml.annotation is not None:
cls_nm_ml: str = HandlesType.extract_class_name_from_annotation(
aa_ml.annotation, trans._imported_modules)
if cls_nm_ml is not None:
HandlesVar.set_var_annot_class_name(
trans.SymTab, var_name, cls_nm_ml)
return 1
return 0
@@ -262,18 +288,18 @@ def create_wrapper_main(trans: HT.Translator | t.CPtr,
main_func: llvmlite.Function | t.CPtr = llvmlite.create_function(
pool, mod, "main", i32_ty)
if main_func is None:
stdio.printf("[TR] CreateFunction main returned NULL\n")
VLogger.error("CreateFunction main returned NULL", "TR")
return 0
entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(
pool, main_func, "entry")
if entry_blk is None:
stdio.printf("[TR] CreateBlock returned NULL\n")
VLogger.error("CreateBlock returned NULL", "TR")
return 0
builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, main_func)
if builder is None:
stdio.printf("[TR] NewBuilder returned NULL\n")
VLogger.error("NewBuilder returned NULL", "TR")
return 0
llvmlite.position_at_end(builder, entry_blk)

View File

@@ -55,31 +55,47 @@ class ReturnHandle(HandlesBase.Mixin):
self.Trans._funcs, self.Trans._func_count, self.Trans)
# val 为 None 时(裸 return检查当前函数返回类型
# void 函数(如 __init__/__before_init__生成 ret void否则 ret i32 0
# void 函数(如 __init__/__before_init__生成 ret void
# ptr 函数生成 ret ptr null否则 ret i32 0
if val is None:
cur_func: llvmlite.Function | t.CPtr = self.Trans._cur_func
is_void_ret: int = 0
is_ptr_ret: int = 0
ret_ty: llvmlite.LLVMType | t.CPtr = None
if cur_func is not None:
ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cur_func)
ret_ty = llvmlite.function_get_ret_ty(cur_func)
if ret_ty is not None:
match ret_ty:
case llvmlite.LLVMType.Void():
is_void_ret = 1
case llvmlite.LLVMType.Ptr(_pe):
is_ptr_ret = 1
if is_void_ret != 0:
llvmlite.build_ret_void(builder)
return 0
if is_ptr_ret != 0:
null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null")
llvmlite.build_ret(builder, null_val)
return 0
val = llvmlite.const_int32(pool, 0)
# 类型转换:确保 val 类型与函数返回类型匹配
# 处理 i1bool 比较结果)→ i8t.CBool等情况
# i1 → 更宽整数用 zextbool 语义1 保持 1而非 sext 的 0xFF
# 注意:当 val 是整数而 ret_ty 是 ptr 时(即使 bits 相同,如 i64 vs ptr
# 也必须 inttoptr 转换,否则 llc 报 "value doesn't match function result type"
cur_func_rt: llvmlite.Function | t.CPtr = self.Trans._cur_func
if cur_func_rt is not None and val is not None and val.Ty is not None:
ret_ty_rt: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cur_func_rt)
if ret_ty_rt is not None:
val_bits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
ret_bits: int = HandlesExpr.get_llvm_type_bits(ret_ty_rt)
if val_bits != 0 and ret_bits != 0 and val_bits != ret_bits:
val_is_ptr: int = HandlesExpr.is_ptr_type(val.Ty)
ret_is_ptr: int = HandlesExpr.is_ptr_type(ret_ty_rt)
# ptr 类型不一致性val 是整数但 ret 是 ptr或反之必须转换
if val_is_ptr != ret_is_ptr:
val = HandlesExpr.coerce_to_type(builder, val, ret_ty_rt)
elif val_bits != 0 and ret_bits != 0 and val_bits != ret_bits:
if val_bits == 1 and val_bits < ret_bits:
val = llvmlite.build_zext(builder, val, ret_ty_rt)
else:

View File

@@ -6,6 +6,8 @@ import llvmlite
import stdio
import ast
import hashtable
import viperlib
import lib.core.VLogger as VLogger
# ============================================================
@@ -19,7 +21,7 @@ import hashtable
# ============================================================
STRUCT_MAX: t.CDefine = 512
FIELD_MAX: t.CDefine = 32
FIELD_MAX: t.CDefine = 48
FIELD_NAME_MAX: t.CDefine = 64
@@ -196,7 +198,10 @@ def register_struct(pool: memhub.MemBuddy | t.CPtr,
return existing
if _struct_count >= STRUCT_MAX:
stdio.printf("[STRUCT] table full, cannot register %s\n", name)
fb_st: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_st is not None:
viperlib.snprintf(fb_st, 1024, "table full, cannot register %s", name)
VLogger.error(fb_st, "STRUCT")
return None
entry: StructEntry | t.CPtr = _get_struct_entry(_struct_count)
@@ -290,7 +295,10 @@ def add_field(pool: memhub.MemBuddy | t.CPtr,
return -1
if struct_entry.FieldCount >= FIELD_MAX:
stdio.printf("[STRUCT] field table full for %s\n", struct_entry.Name)
fb_sf: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_sf is not None:
viperlib.snprintf(fb_sf, 1024, "field table full for %s", struct_entry.Name)
VLogger.error(fb_sf, "STRUCT")
return -1
idx: int = struct_entry.FieldCount
@@ -314,18 +322,41 @@ def add_field(pool: memhub.MemBuddy | t.CPtr,
return idx
# ============================================================
# get_struct_count — 获取已注册结构体数量(供 Phase2 多遍预注册判断收敛)
# ============================================================
def get_struct_count() -> int:
"""返回当前已注册的结构体数量"""
return _struct_count
# ============================================================
# find_struct — 按类名查找结构体
# ============================================================
def find_struct(name: str) -> StructEntry | t.CPtr:
"""按类名查找结构体,返回 StructEntry 或 None"""
"""按类名查找结构体,返回 StructEntry 或 None
支持模块限定名(如 "HT.Translator"
第一遍精确匹配,第二遍按类名(最后一个 '.' 之后的部分)回退
"""
if name is None or _struct_table is None:
return None
# 第一遍:精确匹配
for i in range(_struct_count):
entry: StructEntry | t.CPtr = _get_struct_entry(i)
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
# 第二遍:类名回退(处理模块限定名 "HT.Translator" → "Translator"
dot_pos: str = string.strrchr(name, '.')
if dot_pos is not None:
class_name: str = dot_pos + 1
if class_name is not None and class_name[0] != '\0':
for i in range(_struct_count):
entry: StructEntry | t.CPtr = _get_struct_entry(i)
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, class_name) == 0:
return entry
return None
@@ -455,12 +486,27 @@ def lookup_field_by_class(class_name: str,
return None
# 优先用 SHA1 匹配(跨模块同名类区分)
# 支持模块限定名class_name 可能是 "HT.Translator"entry.Name 是 "Translator"
# 先精确匹配,失败后用 strrchr 提取类名部分回退匹配
if sha1 is not None:
sha1_matched: int = 0
# 计算类名回退部分("HT.Translator" → "Translator"
bare_name: str = class_name
dot_pos_lfbc: str = string.strrchr(class_name, '.')
if dot_pos_lfbc is not None:
bare_name = dot_pos_lfbc + 1
if bare_name is None or bare_name[0] == '\0':
bare_name = class_name
for i in range(_struct_count):
entry: StructEntry | t.CPtr = _get_struct_entry(i)
if entry is not None and entry.Name is not None:
# 精确匹配或类名回退匹配
name_match: int = 0
if string.strcmp(entry.Name, class_name) == 0:
name_match = 1
elif bare_name is not class_name and string.strcmp(entry.Name, bare_name) == 0:
name_match = 1
if name_match != 0:
if entry.ModuleSha1 is not None:
if string.strcmp(entry.ModuleSha1, sha1) == 0:
sha1_matched = 1
@@ -475,12 +521,65 @@ def lookup_field_by_class(class_name: str,
# 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个
entry = find_struct(class_name)
if entry is None:
# 诊断:遍历打印所有已注册结构体名,确认目标类是否在注册表中
stdio.printf("[LFBC-DIAG] FAIL class=%s field=%s sha1=%s count=%d\n",
class_name, field_name,
sha1 if sha1 is not None else "(null)", _struct_count)
for diag_i in range(_struct_count):
diag_entry: StructEntry | t.CPtr = _get_struct_entry(diag_i)
if diag_entry is not None and diag_entry.Name is not None:
stdio.printf("[LFBC-DIAG] [%d] %s sha1=%s fc=%d\n",
diag_i, diag_entry.Name,
diag_entry.ModuleSha1 if diag_entry.ModuleSha1 is not None else "(null)",
diag_entry.FieldCount)
return None
for fi in range(entry.FieldCount):
fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi)
if fe is not None and fe.Name is not None:
if string.strcmp(fe.Name, field_name) == 0:
return fe
# 诊断:找到结构体但字段未找到
stdio.printf("[LFBC-DIAG] NOFIELD class=%s field=%s sname=%s fc=%d\n",
class_name, field_name, entry.Name, entry.FieldCount)
return None
# ============================================================
# find_subclass_with_field — 在基类的所有子类中搜索包含指定字段的结构体
#
# 用于处理 "注解类型是基类但实际值是派生类" 的场景:
# node: AST | t.CPtr = If(...)
# node.orelse = orelse ← orelse 在 If 上,不在 AST 上
#
# 遍历所有已注册结构体,通过 ParentName 链检查继承关系,
# 返回第一个包含 field_name 的子类 StructEntry。
# ============================================================
def find_subclass_with_field(base_class_name: str,
field_name: str) -> StructEntry | t.CPtr:
"""在 base_class_name 的所有子类中搜索包含 field_name 的结构体
返回第一个找到的 StructEntry包含正确的 TyNone=未找到
"""
if base_class_name is None or field_name is None:
return None
sc_i: int
for sc_i in range(_struct_count):
sc_entry: StructEntry | t.CPtr = _get_struct_entry(sc_i)
if sc_entry is None or sc_entry.Name is None:
continue
# 检查 sc_entry 是否是 base_class_name 的子类(传递性)
cur_parent: str = get_parent_name(sc_entry.Name)
while cur_parent is not None:
if string.strcmp(cur_parent, base_class_name) == 0:
# 是子类,检查是否有该字段
sc_fi: int
for sc_fi in range(sc_entry.FieldCount):
sc_fe: FieldEntry | t.CPtr = _get_field_entry(sc_entry, sc_fi)
if sc_fe is not None and sc_fe.Name is not None:
if string.strcmp(sc_fe.Name, field_name) == 0:
return sc_entry
break
cur_parent = get_parent_name(cur_parent)
return None
@@ -546,6 +645,28 @@ def _is_struct_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
return 0
def _is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Ptr 类型(独立函数,规避嵌套 match 编译器 BUG"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Ptr(pointee):
return 1
case _:
return 0
def _get_ptr_pointee(ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""获取 Ptr 类型的 pointee独立函数规避嵌套 match 编译器 BUG"""
if ty is None:
return None
match ty:
case llvmlite.LLVMType.Ptr(pointee):
return pointee
case _:
return None
def get_struct_type_from_value(val: llvmlite.Value | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""从 Value 的类型推断结构体类型"""
if val is None or val.Ty is None:
@@ -935,6 +1056,119 @@ def get_field_count(name: str) -> int:
return entry.FieldCount
# ============================================================
# ensure_struct_def_in_module — 确保跨模块结构体的完整定义在当前模块中可用
#
# 当局部变量声明为值类型(如 f: Filealloca 需要完整类型定义。
# 如果结构体定义在另一个模块(名称含 SHA1 前缀,如 "0035c95a18d4f8e8.File"
# 将其完整定义复制到当前模块。module_add_named_type 会用完整定义替换
# 已存在的 opaque 声明,使 alloca 能正确分配空间。
#
# Args:
# pool: 内存池
# mod: 当前 LLVM 模块
# ty: 变量类型Struct/Ptr/其他)
# ============================================================
def ensure_struct_def_in_module(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr):
"""确保跨模块结构体的完整定义在当前模块中可用(供 alloca 使用)
处理两种情况:
1. Struct 值类型:需要完整定义(供 alloca 分配空间)
2. Ptr(Struct) 指针类型:需要 pointee 至少有 opaque 声明
"""
if ty is None or mod is None or pool is None:
return
# 指针类型:确保 pointee 至少有 opaque 声明
# 用 _is_ptr_type 检查(独立函数,规避嵌套 match BUG
if _is_ptr_type(ty) != 0:
llvmlite.module_ensure_opaque_for_type(mod, pool, ty)
# 如果 pointee 是 Struct递归复制完整定义
# GEP 访问字段需要 sized 类型,仅 opaque 会导致 llc 报错)
pointee: llvmlite.LLVMType | t.CPtr = _get_ptr_pointee(ty)
if pointee is not None:
ensure_struct_def_in_module(pool, mod, pointee)
return
# 获取结构体名称(非 Struct 类型返回 None
sname: t.CChar | t.CPtr = llvmlite.get_struct_name(ty)
if sname is None:
return
# 检查是否为跨模块引用(名称包含 '.'
if string.strchr(sname, 46) is None:
return # 非跨模块引用,无需处理
# 提取类名(第一个 '.' 之后的部分)
# 必须用 strchr 而非 strrchr泛型特化名如 "list[ast.AST|t.CPtr]"
# 内部含 '.'ast.ASTstrrchr 会错误地把类名解析为 "AST|t.CPtr]"
dot_pos: str = string.strchr(sname, '.')
if dot_pos is None:
return
class_name: str = dot_pos + 1
if class_name is None or class_name[0] == '\0':
return
# 提取 SHA1sname 到 dot_pos 之间,用 strlen 避免指针减法)
total_len: t.CSizeT = string.strlen(sname)
class_name_len: t.CSizeT = string.strlen(class_name)
sha1_len: t.CSizeT = total_len - class_name_len - 1
sha1_buf: t.CChar | t.CPtr = pool.alloc(32)
if sha1_buf is None:
return
string.strncpy(sha1_buf, sname, sha1_len)
sha1_buf[sha1_len] = '\0'
# 按类名 + SHA1 查找结构体定义
entry: StructEntry | t.CPtr = find_struct_by_module(class_name, sha1_buf)
if entry is None:
# 回退到按类名查找(可能找到不同模块的同名类,但总比没有好)
entry = find_struct(class_name)
if entry is None:
return
if entry.Ty is None:
return
# 将完整定义添加到当前模块(会替换已存在的 opaque 声明)
llvmlite.module_add_named_type(mod, pool, entry.Ty)
# 递归扫描字段类型,确保值类型子结构体(如 WIN32_FIND_DATAA 中的 FILETIME
# 也有完整定义。指针类型字段只需 opaque 声明(已由 module_ensure_opaque_for_type 处理)。
_ensure_field_types_in_module(pool, mod, entry.Ty)
# ============================================================
# _ensure_field_types_in_module - 递归扫描结构体字段类型
#
# 遍历 Struct 的 Fields 链表,对每个值类型子结构体调用
# ensure_struct_def_in_module。指针/数组/基本类型递归到 pointee/element。
# ============================================================
def _ensure_field_types_in_module(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr):
"""递归扫描类型树,确保值类型子结构体有完整定义"""
if ty is None or mod is None or pool is None:
return
# 用 match 遍历类型(独立函数,规避嵌套 match BUG
match ty:
case llvmlite.LLVMType.Struct(fields, fcount, sname):
# 遍历字段链表
if fields is not None:
fld: llvmlite.ParamNode | t.CPtr = fields
while fld is not None:
if fld.Ty is not None:
# 值类型子结构体(如 FILETIME需要完整定义
if _is_struct_type(fld.Ty) != 0:
ensure_struct_def_in_module(pool, mod, fld.Ty)
_ensure_field_types_in_module(pool, mod, fld.Ty)
fld = fld.Next
case llvmlite.LLVMType.Ptr(pointee):
# 指针类型pointee 只需 opaque 声明,不需要完整定义
# 但如果 pointee 是 Struct确保 opaque 声明存在
if pointee is not None:
llvmlite.module_ensure_opaque_for_type(mod, pool, ty)
case llvmlite.LLVMType.Array(elem_ty, acount):
# 数组类型:元素如果是值类型结构体,需要完整定义
if elem_ty is not None:
_ensure_field_types_in_module(pool, mod, elem_ty)
case _:
pass
# ============================================================
# get_field_name_ptr — 获取 FieldEntry 的字段名指针(访问器)
# ============================================================

View File

@@ -6,6 +6,7 @@ import memhub
import string
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesMain as HandlesMain
@@ -212,7 +213,7 @@ class Translator:
# 创建 LLVM 模块
mod: llvmlite.LLVMModule | t.CPtr = llvmlite.new_module(pool, "main")
if mod is None:
stdio.printf("[TR] NewModule returned NULL\n")
VLogger.error("NewModule returned NULL", "TR")
return 1
self.Module = mod
@@ -231,7 +232,7 @@ class Translator:
printf_func: llvmlite.Function | t.CPtr = llvmlite.create_declare(
pool, mod, "printf", i32_ty)
if printf_func is None:
stdio.printf("[TR] CreateDeclare printf returned NULL\n")
VLogger.error("CreateDeclare printf returned NULL", "TR")
return 1
llvmlite.add_param(pool, printf_func, i8_ptr_ty, "fmt")
printf_func.IsVarArg = 1
@@ -305,6 +306,21 @@ class Translator:
elif kd == ast.ASTKind.ImportFrom:
self.ImportsH.HandleImportFromModule(child)
self.ImportsH.HandleImportFromNames(child)
# 预注册模块级变量AnnAssign/Assign到 SymTab
# 解决时序问题class Logger 的 __init__ 引用 _mbuddy 时,
# _mbuddy 的 AnnAssign 在 AST 中位于 class Logger 之后,
# 按 AST 顺序翻译时 _mbuddy 尚未注册 → lookup_var 返回 None → rhs_val is None
# 预处理先于 translate_children 注册所有模块级变量,
# 后续 translate_children 遍历到同一 AnnAssign 时 handle_module_level_var
# 通过 lookup_module_var 检测已存在并跳过HandlesMain.py:206-209
if ch is not None:
for ci in range(cn_count):
child: ast.AST | t.CPtr = ch.get(ci)
if child is None:
continue
kd_mv: int = child.kind()
if kd_mv == ast.ASTKind.AnnAssign or kd_mv == ast.ASTKind.Assign:
HandlesMain.handle_module_level_var(self, child)
# 创建前向声明
HandlesFunctions.forward_declare_functions(self, tree)
added: int = HandlesMain.translate_children(self, tree)

View File

@@ -26,6 +26,27 @@ import lib.core.Handles.HandlesStruct as HandlesStruct
_g_current_file: str = None
# ============================================================
# 全局 temp_dirpyi 文件所在目录,由 BuildPipeline/Phase1 设置)
#
# 用于跨模块 CDefine 常量查找:当当前模块的 CDefine 表中找不到
# 某个 Name 时,从 from_imports 解析出源模块,再从该模块的 pyi
# 文件中解析 CDefine 常量值。
# ============================================================
_g_temp_dir: str = None
def set_temp_dir(temp_dir: str) -> None:
"""设置全局 temp_dir每个项目编译开始时由 BuildPipeline/Phase1 设置)"""
global _g_temp_dir
_g_temp_dir = temp_dir
def get_temp_dir() -> str:
"""获取全局 temp_dir"""
return _g_temp_dir
# ============================================================
# 全局泛型特化上下文(由 _specialize_generic_class 设置/清除)
#
@@ -46,11 +67,55 @@ _g_generic_type_args: list[str] | t.CPtr = None
_g_cdefine_names: list[str] | t.CPtr = None
_g_cdefine_values: list[str] | t.CPtr = None
# ============================================================
# 全局跨模块 CDefine 表(按模块 SHA1 索引,跨模块持久化)
#
# 本地表在模块切换时被 clear_cdefine_constants 清空,
# 导致跨模块引用(如 HandlesVar.SCOPE_FUNCTION找不到常量。
# 全局表在模块切换时不清空,跨模块查找直接查表,无需文件 I/O。
#
# 数据布局:
# _g_gcdef_sha1s: 每条 17 字节(模块 SHA1 16字符 + null
# _g_gcdef_names: 每条 64 字节(常量名 + null
# _g_gcdef_values: 每条 32 字节(值字符串 + null
# ============================================================
MAX_GLOBAL_CDEFINE: t.CDefine = 1024
_g_gcdef_sha1s: bytes = None
_g_gcdef_names: bytes = None
_g_gcdef_values: bytes = None
_g_gcdef_count: int = 0
_g_current_module_sha1: str = None
def set_current_module_sha1(sha1: str) -> None:
"""设置当前正在编译的模块 SHA1Phase1 在模块开始翻译前调用)"""
global _g_current_module_sha1
_g_current_module_sha1 = sha1
def _global_cdefine_init(pool: memhub.MemBuddy | t.CPtr) -> int:
"""懒初始化全局 CDefine 表"""
global _g_gcdef_sha1s, _g_gcdef_names, _g_gcdef_values
if _g_gcdef_sha1s is None:
_g_gcdef_sha1s = stdlib.malloc(MAX_GLOBAL_CDEFINE * 17)
_g_gcdef_names = stdlib.malloc(MAX_GLOBAL_CDEFINE * 64)
_g_gcdef_values = stdlib.malloc(MAX_GLOBAL_CDEFINE * 32)
if _g_gcdef_sha1s is None or _g_gcdef_names is None or _g_gcdef_values is None:
return 1
string.memset(_g_gcdef_sha1s, 0, MAX_GLOBAL_CDEFINE * 17)
string.memset(_g_gcdef_names, 0, MAX_GLOBAL_CDEFINE * 64)
string.memset(_g_gcdef_values, 0, MAX_GLOBAL_CDEFINE * 32)
return 0
def register_cdefine_constant(pool: memhub.MemBuddy | t.CPtr,
name: str, value: int) -> None:
"""注册 CDefine 编译期常量到全局表"""
global _g_cdefine_names, _g_cdefine_values
"""注册 CDefine 编译期常量到本地表和全局表
值以十进制字符串存储。注意value 是 32 位有符号整数,
0xFFFFFFFF 会存储为 "-1"lookup 时用 found 标志区分"未找到"和值为 -1。
"""
global _g_cdefine_names, _g_cdefine_values, _g_gcdef_count
if _g_cdefine_names is None:
_g_cdefine_names = list[str](pool, 64)
_g_cdefine_values = list[str](pool, 64)
@@ -61,12 +126,54 @@ def register_cdefine_constant(pool: memhub.MemBuddy | t.CPtr,
_g_cdefine_values.append(val_buf)
else:
_g_cdefine_values.append("0")
# 同时注册到全局跨模块表(用当前模块 SHA1 索引)
if _g_current_module_sha1 is not None and _g_gcdef_count < MAX_GLOBAL_CDEFINE:
if _global_cdefine_init(pool) == 0:
sidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 17
nidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 64
vidx: t.CSizeT = t.CSizeT(_g_gcdef_count) * 32
string.strcpy(_g_gcdef_sha1s + sidx, _g_current_module_sha1)
string.strcpy(_g_gcdef_names + nidx, name)
vbuf: str = _g_gcdef_values + vidx
viperlib.snprintf(vbuf, 32, "%d", value)
_g_gcdef_count += 1
def lookup_global_cdefine(module_sha1: str, name: str) -> int:
"""从全局跨模块表查找 CDefine 常量
用 module_sha1 + name 精确匹配。
找到时设置 _g_cdefine_found=1 并返回值;未找到返回 0。
"""
global _g_cdefine_found
if module_sha1 is None or name is None:
return 0
if _g_gcdef_sha1s is None or _g_gcdef_count <= 0:
return 0
i: t.CSizeT = 0
while i < _g_gcdef_count:
sidx: t.CSizeT = t.CSizeT(i) * 17
nidx: t.CSizeT = t.CSizeT(i) * 64
vidx: t.CSizeT = t.CSizeT(i) * 32
if string.strcmp(_g_gcdef_sha1s + sidx, module_sha1) == 0:
if string.strcmp(_g_gcdef_names + nidx, name) == 0:
_g_cdefine_found = 1
return string.atoi(_g_gcdef_values + vidx)
i += 1
return 0
def lookup_cdefine_constant(name: str) -> int:
"""查找 CDefine 编译期常量,返回值或 -1未找到"""
"""查找 CDefine 编译期常量
返回找到的值。由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1
用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。
调用方应在调用后检查 _g_cdefine_found。
"""
global _g_cdefine_found
_g_cdefine_found = 0
if _g_cdefine_names is None:
return -1
return 0
n: t.CSizeT = _g_cdefine_names.__len__()
i: t.CSizeT
for i in range(n):
@@ -74,8 +181,23 @@ def lookup_cdefine_constant(name: str) -> int:
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = _g_cdefine_values.get(i)
if val_str is not None:
_g_cdefine_found = 1
return string.atoi(val_str)
return -1
return 0
_g_cdefine_found: int = 0
def is_cdefine_found() -> int:
"""检查上次 lookup_cdefine_constant 是否找到值"""
return _g_cdefine_found
def set_cdefine_found(val: int) -> None:
"""设置 cdefine_found 标志(供跨模块查找函数使用)"""
global _g_cdefine_found
_g_cdefine_found = val
def clear_cdefine_constants() -> None:
@@ -228,6 +350,20 @@ def _parse_type_string(pool: memhub.MemBuddy | t.CPtr,
return ast.Name(pool, type_str, ctx)
# ============================================================
# _parse_type_string_ptr - _parse_type_string 的 t.CPtr 返回类型包装
#
# 旧编译器对 ast.AST | t.CPtr 跨模块返回类型推断有 bug解析为 i32
# 用 t.CPtr 包装绕过,外部模块应调用此函数而非 _parse_type_string
# ============================================================
def _parse_type_string_ptr(pool: memhub.MemBuddy | t.CPtr,
type_str: str,
ctx: t.CInt) -> t.CPtr:
"""_parse_type_string 的 t.CPtr 返回类型包装(绕过旧编译器类型推断 bug"""
result: ast.AST | t.CPtr = _parse_type_string(pool, type_str, ctx)
return result
# ============================================================
# set_current_file - 设置当前编译的文件名
#
@@ -709,7 +845,8 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr,
def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
imported_modules: str,
from_imports: str) -> llvmlite.LLVMType | t.CPtr:
from_imports: str,
trans: HT.Translator | t.CPtr = None) -> llvmlite.LLVMType | t.CPtr:
"""解析类型注解节点为 LLVMType
Args:
@@ -717,6 +854,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
node: 类型注解 AST 节点Name 或 Attribute
imported_modules: 已导入模块名字符串(空格分隔)
from_imports: from-import 映射字符串("name:module name:module" 格式)
trans: 翻译器对象(可选,用于触发泛型特化)
"""
if node is None:
return None
@@ -727,9 +865,9 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_STR and cn.str_val is not None:
sub_node: ast.AST | t.CPtr = _parse_type_string(pool, cn.str_val, ast.Ctx.Load)
sub_node: ast.AST | t.CPtr = _parse_type_string(pool, cn.str_val, ast.ASTCtx.Load)
if sub_node is not None:
return resolve_annotation_type(pool, sub_node, imported_modules, from_imports)
return resolve_annotation_type(pool, sub_node, imported_modules, from_imports, trans)
return None
# Name 节点: str, int, bytes, from-import 名称, class 名称等
@@ -749,7 +887,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
# 用具体类型名递归解析(支持 BinOp/Attribute 类型实参)
gp_sub_node: ast.AST | t.CPtr = _parse_type_string(pool, gp_ta, nm.ctx)
if gp_sub_node is not None:
return resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports)
return resolve_annotation_type(pool, gp_sub_node, imported_modules, from_imports, trans)
# str = bytes = t.CChar | t.CPtr = i8*
if string.strcmp(nm.id, "str") == 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
@@ -817,7 +955,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
if other_is_cvoid != 0:
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
other_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
pool, other_node, imported_modules, from_imports)
pool, other_node, imported_modules, from_imports, trans)
if other_ty is None:
# CPtr 单独使用或其他无效类型 → i8*
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
@@ -825,11 +963,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
# 无 CPtr 标记:返回第一个非 None装饰器标记如 CExport/CExtern 被忽略)
left_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
pool, bop.left, imported_modules, from_imports)
pool, bop.left, imported_modules, from_imports, trans)
if left_ty is not None:
return left_ty
right_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
pool, bop.right, imported_modules, from_imports)
pool, bop.right, imported_modules, from_imports, trans)
if right_ty is not None:
return right_ty
return None
@@ -891,6 +1029,22 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
gen_spec_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(gen_mangled)
if gen_spec_ty is not None:
return gen_spec_ty
# 未特化:尝试主动触发泛型特化(与 TPC _HandleSubscript 对齐)
# 当 trans 可用时,检查是否是泛型模板并触发特化
if trans is not None:
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesExprCall as HandlesExprCall
template_cd_chk: ast.ClassDef | t.CPtr = HandlesClassDef._find_generic_template(gen_nm.id)
if template_cd_chk is not None:
type_args_chk: list[str] | t.CPtr = HandlesExprCall._extract_type_args_from_slice(
pool, sub.slice)
if type_args_chk is not None and type_args_chk.__len__() > 0:
spec_name_chk: str = HandlesClassDef._specialize_generic_class(
trans, gen_nm.id, type_args_chk)
if spec_name_chk is not None:
gen_spec_ty = HandlesStruct.get_struct_type(gen_mangled)
if gen_spec_ty is not None:
return gen_spec_ty
# 未特化:返回 None不报错让后续流程处理
return None
# 检查 value 是否是 Attribute(Name('t'), 'CArray')
@@ -912,7 +1066,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
# 相当于 t.CArray[elem_ty, None],表示静态推导长度数组
if sub.slice.kind() != ast.ASTKind.Tuple:
elem_ty_single: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
pool, sub.slice, imported_modules, from_imports)
pool, sub.slice, imported_modules, from_imports, trans)
if elem_ty_single is None:
fatal_type_error(sub.slice, "t.CArray[?] 元素类型解析失败")
return llvmlite.Ptr(pool, elem_ty_single)
@@ -930,7 +1084,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
fatal_type_error(node, "t.CArray Tuple 元素为 None")
# 递归解析元素类型
elem_ty: llvmlite.LLVMType | t.CPtr = resolve_annotation_type(
pool, elem_node, imported_modules, from_imports)
pool, elem_node, imported_modules, from_imports, trans)
if elem_ty is None:
fatal_type_error(elem_node, "t.CArray 元素类型解析失败")
# 解析 count必须是整数常量、None 或 CDefine 常量名)
@@ -950,7 +1104,7 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
if cnt_nm.id is None:
fatal_type_error(count_node, "t.CArray count Name 节点 id 为 None")
looked_up: int = lookup_cdefine_constant(cnt_nm.id)
if looked_up < 0:
if is_cdefine_found() == 0:
fatal_type_error(count_node, "t.CArray count 不是已注册的 CDefine 常量")
count_val = looked_up
else:
@@ -1180,6 +1334,11 @@ def _strappend(dst: t.CChar | t.CPtr, dst_size: t.CSizeT, src: t.CChar | t.CPtr)
remain: t.CSizeT = dst_size - dlen
if remain <= 0:
return
for i in range(min(slen, remain - 1)):
# 内联 min(slen, remain - 1) 避免 builtin min 未定义TransPyV 无 builtin min
limit: t.CSizeT = slen
cap: t.CSizeT = remain - 1
if cap < limit:
limit = cap
for i in range(limit):
dst[dlen + i] = src[i]
dst[dlen + i] = '\0'

View File

@@ -36,6 +36,7 @@ class VarEntry:
Alloca: llvmlite.Value | t.CPtr
Used: t.CInt
AnnotClassName: t.CChar | t.CPtr # 原始类型注解的类名str 别名在结构体字段中触发编译器 bug改用显式联合类型
IsPtrElement: t.CInt # 标志: 1=注解为 bytes|t.CPtr 或 str|t.CPtr, 下标按 8 字节步长
# ============================================================
@@ -154,6 +155,25 @@ def set_var_annot_class_name(symtab: SymbolTable | t.CPtr,
return 0
# ============================================================
# set_var_ptr_element — 标记变量为"指针到 str/bytes"类型
#
# 注解为 bytes|t.CPtr 或 str|t.CPtr 的变量,其 alloca 类型是 i8*
# 但下标访问应按 8 字节步长i8** 语义),而非 1 字节步长i8* 语义)。
# 此标志在 HandlesAnnAssign 中根据注解形式设置。
# ============================================================
def set_var_ptr_element(symtab: SymbolTable | t.CPtr,
name: str) -> int:
"""标记变量为 ptr_element 类型,返回 0=成功 / 1=失败"""
if symtab is None or name is None:
return 1
entry: VarEntry | t.CPtr = lookup_var_entry(symtab, name)
if entry is None:
return 1
entry.IsPtrElement = 1
return 0
# ============================================================
# add_var — 添加变量到变量表
# ============================================================
@@ -175,6 +195,27 @@ def add_var(vars_ptr: VarEntry | t.CPtr,
return 0
# ============================================================
# _alloca_at_entry - 在函数入口块生成 alloca确保支配性
#
# LLVM IR 要求指令支配所有使用点。如果 alloca 在条件分支内生成,
# 但在其他分支使用会违反支配性Instruction does not dominate all uses
# 标准做法:所有 alloca 在函数入口块生成。
#
# 临时切换 builder.CurBlock 到入口块,生成 alloca然后恢复。
# ============================================================
def _alloca_at_entry(builder: llvmlite.IRBuilder | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""在函数入口块生成 alloca返回 alloca 值
使用 llvmlite.build_alloca_at_entry 在入口块终止指令之前插入 alloca
确保所有 alloca 在入口块,避免支配性违规。
"""
if builder is None or ty is None:
return None
return llvmlite.build_alloca_at_entry(builder, ty)
# ============================================================
# get_or_create_var — 查找或创建变量 alloca旧版兼容
# ============================================================
@@ -188,7 +229,7 @@ def get_or_create_var(pool: memhub.MemBuddy | t.CPtr,
existing: llvmlite.Value | t.CPtr = find_var(vars_ptr, var_count, name)
if existing is not None:
return existing
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, ty)
alloca: llvmlite.Value | t.CPtr = _alloca_at_entry(builder, ty)
if alloca is None:
return None
if add_var(vars_ptr, var_count, name, alloca) != 0:
@@ -358,7 +399,7 @@ def get_or_create_sym(symtab: SymbolTable | t.CPtr,
existing: llvmlite.Value | t.CPtr = lookup_current(symtab, name)
if existing is not None:
return existing
alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, ty)
alloca: llvmlite.Value | t.CPtr = _alloca_at_entry(builder, ty)
if alloca is None:
return None
define_var(symtab, name, alloca)

View File

@@ -9,6 +9,7 @@ import w32.win32base as win32base
import w32.fileio as fileio
import hashlib
import viperlib
import lib.core.VLogger as VLogger
# ============================================================
@@ -312,7 +313,10 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr,
sha1: str = compute_file_sha1(pool, full_path)
if sha1 is not None:
add_file_entry(result, pool, full_path, rel_path, sha1)
stdio.printf(" [scan] %s -> %s\n", rel_path, sha1)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "扫描: %s -> %s", rel_path, sha1)
VLogger.debug(fb, "scan")
# 继续搜索下一个文件
if win32file.FindNextFileA(handle, find_data) == 0:

View File

@@ -16,6 +16,7 @@ import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.IncludesScanner as IncludesScanner
import lib.core.StubMerger as StubMerger
import lib.core.BuildPipeline as BuildPipeline
@@ -31,6 +32,431 @@ SRC_BUF_SIZE: t.CDefine = 1048576
# pyi 缓冲区大小256KB
PYI_BUF_SIZE: t.CSizeT = 262144
# 拓扑排序相关常量
MAX_CLASSES: t.CDefine = 512
MAX_FILE_DEPS: t.CDefine = 256
MAX_DEP_EDGES: t.CDefine = 16
SHA1_LEN: t.CDefine = 17
CLASS_NAME_LEN: t.CDefine = 64
# 拓扑排序使用扁平字节数组存储类信息和文件依赖,
# 避免结构体中的 str 指针字段在 mb.alloc 零填充时为 NULL 导致崩溃。
# 类名/父类名存储在 class_names_buf/parent_names_buf每个 CLASS_NAME_LEN 字节),
# 定义 SHA1 存储在 def_sha1s_buf每个 SHA1_LEN 字节)。
# ============================================================
# _ScanClassInheritance - 预扫描 includes 文件,收集类继承关系
#
# 遍历所有可达 includes 文件,解析 AST 收集 ClassDef 的类名和父类名。
# 结果存入扁平字节数组,返回收集到的类数量。
#
# Args:
# mb: 内存池
# result: IncludesScanner 扫描结果
# reachable_set: 可达 SHA1 集合
# reachable_count: 可达 SHA1 数量
# use_reachable: 是否使用可达集合过滤
# sha1_set: SHA1 集合use_reachable=0 时使用)
# set_count: SHA1 集合数量
# class_names_buf: 类名扁平数组(每个 CLASS_NAME_LEN 字节,连续内存)
# parent_names_buf: 父类名扁平数组(每个 CLASS_NAME_LEN 字节,无父类填 '\0'
# def_sha1s_buf: 定义 SHA1 扁平数组(每个 SHA1_LEN 字节)
#
# Returns:
# 收集到的类数量,-1 失败
# ============================================================
def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
result: IncludesScanner.ScanResult | t.CPtr,
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) -> int:
"""预扫描 includes 文件,收集类继承关系"""
if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
return -1
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
class_count: int = 0
for i in range(result.Count):
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
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 = StubMerger._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else:
in_set = StubMerger._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0:
continue
# 读取文件
file_path: str = entry.Path
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
if f.closed:
continue
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
if src_buf is None:
f.close()
continue
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
f.close()
if bytes_read <= 0:
stdlib.free(src_buf)
continue
if bytes_read < SRC_BUF_SIZE:
src_buf[bytes_read] = 0
else:
src_buf[SRC_BUF_SIZE - 1] = 0
# 解析 AST
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if lx is None:
stdlib.free(src_buf)
continue
ast._lexer_init(lx, src_buf, mb)
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
if tree is None:
stdlib.free(src_buf)
continue
# 遍历 AST 顶层节点,收集 ClassDef
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
if ch is not None:
cn: t.CSizeT = ch.__len__()
for ci in range(cn):
child: ast.AST | t.CPtr = ch.get(ci)
if child is None:
continue
kd: int = child.kind()
if kd != ast.ASTKind.ClassDef:
continue
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
if cd is None or cd.name is None:
continue
if class_count >= MAX_CLASSES:
break
# 填充扁平数组:类名(偏移 = class_count * CLASS_NAME_LEN
name_dst: str = class_names_buf + class_count * CLASS_NAME_LEN
nm_len: t.CSizeT = string.strlen(cd.name)
if nm_len >= CLASS_NAME_LEN:
string.strncpy(name_dst, cd.name, CLASS_NAME_LEN - 1)
name_dst[CLASS_NAME_LEN - 1] = '\0'
else:
string.strcpy(name_dst, cd.name)
# 填充扁平数组:父类名(无父类填 '\0'
parent_dst: str = parent_names_buf + class_count * CLASS_NAME_LEN
parent_dst[0] = '\0'
parent_nm: str = HandlesClassDef._get_parent_class(cd, None)
# parent_nm 是 AST 节点内部指针,不需要释放
if parent_nm is not None:
pnm_len: t.CSizeT = string.strlen(parent_nm)
if pnm_len >= CLASS_NAME_LEN:
string.strncpy(parent_dst, parent_nm, CLASS_NAME_LEN - 1)
parent_dst[CLASS_NAME_LEN - 1] = '\0'
else:
string.strcpy(parent_dst, parent_nm)
# 填充扁平数组:定义 SHA1偏移 = class_count * SHA1_LEN
sha1_dst: str = def_sha1s_buf + class_count * SHA1_LEN
string.strcpy(sha1_dst, sha1_e)
class_count += 1
stdlib.free(src_buf)
return class_count
# ============================================================
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
#
# Args:
# class_names_buf: 类名扁平数组
# def_sha1s_buf: 定义 SHA1 扁平数组
# class_count: 类数量
# class_name: 要查找的类名
#
# Returns:
# SHA1 字符串指针(指向 def_sha1s_buf 内部偏移None 未找到
# ============================================================
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes,
class_count: int, class_name: str) -> str:
"""查找类名对应的定义文件 SHA1"""
if class_names_buf is None or def_sha1s_buf is None or class_name is None:
return None
for i in range(class_count):
name_ptr: str = class_names_buf + i * CLASS_NAME_LEN
if string.strcmp(name_ptr, class_name) == 0:
return def_sha1s_buf + i * SHA1_LEN
return None
# ============================================================
# _TopoSortFiles - 根据类继承关系对文件进行拓扑排序
#
# 算法Kahn
# 1. 从扁平类信息数组构建"文件 SHA1 → 依赖文件 SHA1 集合"映射
# 2. 计算每个文件的入度(依赖数量)
# 3. 入度为 0 的文件先处理,处理后减少依赖它的文件入度
# 4. 重复直到所有文件处理完成
#
# 注意:依赖方向是"父类所在文件 → 子类所在文件"
# 即父类文件必须先处理。Kahn 用 DepCount 作为剩余依赖计数,
# 依赖为 0 的先处理,初始化 InDegree = DepCount。
#
# Args:
# mb: 内存池
# result: IncludesScanner 扫描结果
# reachable_set: 可达 SHA1 集合
# reachable_count: 可达 SHA1 数量
# use_reachable: 是否使用可达集合过滤
# sha1_set: SHA1 集合
# set_count: SHA1 集合数量
# class_names_buf: 类名扁平数组
# parent_names_buf: 父类名扁平数组
# def_sha1s_buf: 定义 SHA1 扁平数组
# class_count: 类数量
# out_order: 输出拓扑顺序的文件索引数组调用者分配int 数组)
# out_count: 输出文件数量
#
# Returns:
# 0 成功,-1 失败
# ============================================================
def _TopoSortFiles(mb: memhub.MemBuddy | t.CPtr,
result: IncludesScanner.ScanResult | t.CPtr,
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) -> int:
"""根据类继承关系对文件进行拓扑排序"""
if result is None or out_order is None or out_count is None:
return -1
entry_size: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
# 1. 分配扁平数组:文件 SHA1、依赖 SHA1 列表、依赖数量、入度、处理状态
# 用扁平数组代替 FileDepEntry 结构体,避免 str 指针字段 NULL 崩溃
file_sha1s_buf: bytes = mb.alloc(MAX_FILE_DEPS * SHA1_LEN)
file_deps_buf: bytes = mb.alloc(MAX_FILE_DEPS * MAX_DEP_EDGES * SHA1_LEN)
file_dep_counts: t.CPtr = mb.alloc(MAX_FILE_DEPS * 4)
file_indegrees: t.CPtr = mb.alloc(MAX_FILE_DEPS * 4)
file_processed: t.CPtr = mb.alloc(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, MAX_FILE_DEPS * SHA1_LEN)
string.memset(file_deps_buf, 0, MAX_FILE_DEPS * MAX_DEP_EDGES * SHA1_LEN)
string.memset(file_dep_counts, 0, MAX_FILE_DEPS * 4)
string.memset(file_indegrees, 0, MAX_FILE_DEPS * 4)
string.memset(file_processed, 0, MAX_FILE_DEPS * 4)
file_count: int = 0
# sha1→file_index 映射(简单线性查找,文件数不多)
for i in range(result.Count):
entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
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 = StubMerger._is_in_sha1_set(sha1_e, reachable_set, reachable_count)
else:
in_set = StubMerger._is_in_sha1_set(sha1_e, sha1_set, set_count)
if in_set == 0:
continue
if file_count >= MAX_FILE_DEPS:
break
# 初始化扁平数组:文件 SHA1偏移 = file_count * SHA1_LEN
# DepCount/InDegree/Processed 已由 memset 清零
sha1_dst: str = file_sha1s_buf + file_count * SHA1_LEN
string.strcpy(sha1_dst, sha1_e)
file_count += 1
# 2. 构建依赖边:对每个类,找到父类定义所在文件,建立 子文件→父文件 依赖
for ci in range(class_count):
# 读取父类名(偏移 = ci * CLASS_NAME_LEN
parent_ptr: str = parent_names_buf + ci * CLASS_NAME_LEN
if parent_ptr[0] == '\0':
continue
# 读取子类定义 SHA1偏移 = ci * SHA1_LEN
child_def_sha1: str = def_sha1s_buf + ci * SHA1_LEN
# 查找父类定义所在文件的 SHA1
parent_sha1: str = _FindClassDefSha1(class_names_buf, def_sha1s_buf, class_count, parent_ptr)
if parent_sha1 is None:
continue # 父类不在 includes 中(如 Object、CEnum 等标记基类)
# 父类文件 SHA1 不能等于子类文件 SHA1同文件内继承不需要依赖
if string.strcmp(parent_sha1, child_def_sha1) == 0:
continue
# 找到子类文件对应的索引,添加父文件 SHA1 到依赖列表
for fi in range(file_count):
fsha1: str = file_sha1s_buf + fi * SHA1_LEN
if string.strcmp(fsha1, child_def_sha1) != 0:
continue
# 读取当前依赖数量
cnt_addr: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi * 4
cnt_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr, t.CPtr))
if cnt_ptr is None:
break
cur_cnt: int = cnt_ptr[0]
# 检查依赖是否已添加(去重)
already: int = 0
for di in range(cur_cnt):
dep_slot: str = file_deps_buf + fi * MAX_DEP_EDGES * SHA1_LEN + di * SHA1_LEN
if string.strcmp(dep_slot, parent_sha1) == 0:
already = 1
break
if already != 0:
break
if cur_cnt < MAX_DEP_EDGES:
dep_dst: str = file_deps_buf + fi * MAX_DEP_EDGES * SHA1_LEN + cur_cnt * SHA1_LEN
string.strcpy(dep_dst, parent_sha1)
cnt_ptr[0] = cur_cnt + 1
break
# 3. Kahn 算法InDegree = DepCount依赖多少个父文件
# 依赖为 0 的先处理,处理后减少依赖它的文件入度
for fi in range(file_count):
cnt_addr3: t.CUInt64T = t.CUInt64T(file_dep_counts) + fi * 4
cnt_ptr3: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr3, t.CPtr))
ind_addr3: t.CUInt64T = t.CUInt64T(file_indegrees) + fi * 4
ind_ptr3: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr3, t.CPtr))
if cnt_ptr3 is not None and ind_ptr3 is not None:
ind_ptr3[0] = cnt_ptr3[0]
# 4. 拓扑排序输出
out_idx: int = 0
changed: int = 1
while changed != 0:
changed = 0
for fi in range(file_count):
# 检查处理状态
proc_addr4: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4
proc_ptr4: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr4, t.CPtr))
if proc_ptr4 is None or proc_ptr4[0] != 0:
continue
# 检查入度
ind_addr4: t.CUInt64T = t.CUInt64T(file_indegrees) + fi * 4
ind_ptr4: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr4, t.CPtr))
if ind_ptr4 is None or ind_ptr4[0] > 0:
continue
# 入度为 0可以处理
# 找到对应的 result.Entries 索引
target_sha1: str = file_sha1s_buf + fi * SHA1_LEN
for ri in range(result.Count):
ea_r: t.CUInt64T = t.CUInt64T(result.Entries) + ri * entry_size
ent_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_r, t.CPtr))
if ent_r is not None and ent_r.Sha1 is not None:
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
# 写入输出索引数组
out_idx_addr: t.CUInt64T = t.CUInt64T(out_order) + out_idx * 4
out_int_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_idx_addr, t.CPtr))
if out_int_ptr is not None:
out_int_ptr[0] = ri
out_idx += 1
break
# 标记已处理
proc_ptr4[0] = 1
changed = 1
# 减少依赖该文件的其他文件的入度
for fj in range(file_count):
proc_addr5: t.CUInt64T = t.CUInt64T(file_processed) + fj * 4
proc_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr5, t.CPtr))
if proc_ptr5 is None or proc_ptr5[0] != 0:
continue
# 读取 fj 的依赖数量
cnt_addr5: t.CUInt64T = t.CUInt64T(file_dep_counts) + fj * 4
cnt_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(cnt_addr5, t.CPtr))
if cnt_ptr5 is None:
continue
fj_dep_cnt: int = cnt_ptr5[0]
for di in range(fj_dep_cnt):
dep_slot5: str = file_deps_buf + fj * MAX_DEP_EDGES * SHA1_LEN + di * SHA1_LEN
if string.strcmp(dep_slot5, target_sha1) == 0:
ind_addr5: t.CUInt64T = t.CUInt64T(file_indegrees) + fj * 4
ind_ptr5: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(ind_addr5, t.CPtr))
if ind_ptr5 is not None:
ind_ptr5[0] = ind_ptr5[0] - 1
break
# 处理循环依赖(未处理的文件按原顺序追加)
if out_idx < file_count:
for fi in range(file_count):
proc_addr6: t.CUInt64T = t.CUInt64T(file_processed) + fi * 4
proc_ptr6: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(proc_addr6, t.CPtr))
if proc_ptr6 is None or proc_ptr6[0] != 0:
continue
target_sha1_2: str = file_sha1s_buf + fi * SHA1_LEN
for ri in range(result.Count):
ea_r2: t.CUInt64T = t.CUInt64T(result.Entries) + ri * entry_size
ent_r2: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_r2, t.CPtr))
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: t.CUInt64T = t.CUInt64T(out_order) + out_idx * 4
out_int_ptr2: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_idx_addr2, t.CPtr))
if out_int_ptr2 is not None:
out_int_ptr2[0] = ri
out_idx += 1
break
proc_ptr6[0] = 1
# 写入输出数量
out_count_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(out_count, t.CPtr))
if out_count_ptr is not None:
out_count_ptr[0] = out_idx
# 释放扁平数组
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
# ============================================================
# RunPhase1 - Phase1: 扫描 includes 目录,按需翻译并生成 stub
@@ -51,9 +477,12 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
log: VLogger.Logger | t.CPtr) -> int:
"""Phase1: 扫描 includes 目录,按需翻译并生成 stub"""
if includes_dir is None or temp_dir is None:
stdio.printf("[Phase1] includes_dir 或 temp_dir 为空,跳过\n")
VLogger.warning("includes_dir 或 temp_dir 为空,跳过", "Phase1")
return 1
# 设置全局 temp_dir供跨模块 CDefine 查找使用)
HandlesType.set_temp_dir(temp_dir)
# 确保 temp 目录存在build_dir/temp 可能尚未创建)
BuildPipeline.ensure_dir(temp_dir)
@@ -63,25 +492,35 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# 扫描 includes 目录
result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir)
if result is None:
stdio.printf("[Phase1] 扫描失败\n")
VLogger.error("扫描失败", "Phase1")
return 1
# 读取 _sha1_map.txt 获取需要的 includes SHA1 集合
# 优先使用 Projectrans.py 生成的 _sha1_map.txt含依赖分析只包含需要的 includes
# 若不存在,则从扫描结果生成(包含所有 includes可能导致结构体表溢出
# 从扫描结果直接构建 SHA1 集合(不依赖 _sha1_map.txt 文件)
# Phase1 作为编译器自身,应从内存中的扫描结果获取 SHA1避免外部文件依赖
sha1_set: str = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17)
if sha1_set is None:
stdio.printf("[Phase1] sha1_set 分配失败\n")
VLogger.error("sha1_set 分配失败", "Phase1")
return 1
string.memset(sha1_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
set_count: int = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set)
set_count: int = 0
p1_entry_size_init: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
for se_i in range(result.Count):
if set_count >= StubMerger.MAX_INCLUDES_SHA1:
break
se_addr: t.CUInt64T = t.CUInt64T(result.Entries) + se_i * p1_entry_size_init
se_ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(se_addr, t.CPtr))
if se_ent is None or se_ent.Sha1 is None:
continue
string.strcpy(sha1_set + set_count * 17, se_ent.Sha1)
set_count += 1
if set_count <= 0:
stdio.printf("[Phase1] _sha1_map.txt 不存在或为空,从扫描结果生成\n")
StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0)
set_count = StubMerger._load_includes_sha1_set(mb, temp_dir, sha1_set)
if set_count < 0:
stdio.printf("[Phase1] 无法加载 _sha1_map.txt跳过 Phase1\n")
return 1
VLogger.warning("扫描结果无有效 SHA1跳过 Phase1项目可能无 includes 依赖)", "Phase1")
stdlib.free(sha1_set)
return 0
# 写入 _sha1_map.txt人类可读输出程序内部不读取机器分析使用 PopulateSha1MapStore 内存存储器)
StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0)
# 填充全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
StubMerger.PopulateSha1MapStore(result)
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用)
td_len_p1map: t.CSizeT = string.strlen(temp_dir)
p1_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
@@ -172,6 +611,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
tr_a._declare_only = 2
tr_a.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry_a.RelPath)
HandlesType.set_current_file(file_path_a)
HandlesType.set_current_module_sha1(sha1_a)
HandlesType.clear_cdefine_constants()
HandlesStruct.reset_visible_structs(mb, 0)
ret_a: int = tr_a.translate(tree_a)
@@ -235,13 +675,69 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
# 前面就需要用。Phase 1a 先注册所有 struct/enum/union 到全局表,
# Phase 1b 全量翻译时 struct 已注册,走 existing 路径只翻译方法体。
# 只处理可达文件,避免翻译不需要的 includes如 Test 不依赖 ast 模块)。
#
# 拓扑排序:预扫描所有文件收集类继承关系,按依赖顺序处理文件,
# 确保父类所在文件先于子类所在文件处理(如 base.py 先于 astaux.py
# ============================================================
p1a_reg: int = 0
p1a_skp: int = 0
p1a_fl: int = 0
for i in range(result.Count):
entry_addr_r: t.CUInt64T = t.CUInt64T(result.Entries) + i * entry_size
# 预扫描类继承关系,构建拓扑顺序(用扁平字节数组,避免结构体 str 指针 NULL 崩溃)
class_names_buf: bytes = mb.alloc(MAX_CLASSES * CLASS_NAME_LEN)
parent_names_buf: bytes = mb.alloc(MAX_CLASSES * CLASS_NAME_LEN)
def_sha1s_buf: bytes = mb.alloc(MAX_CLASSES * SHA1_LEN)
topo_order: t.CPtr = mb.alloc(4 * MAX_FILE_DEPS)
topo_count_box: t.CPtr = mb.alloc(4)
topo_count: int = 0
if class_names_buf is not None and parent_names_buf is not None and def_sha1s_buf is not None and topo_order is not None and topo_count_box is not None:
string.memset(class_names_buf, 0, MAX_CLASSES * CLASS_NAME_LEN)
string.memset(parent_names_buf, 0, MAX_CLASSES * CLASS_NAME_LEN)
string.memset(def_sha1s_buf, 0, MAX_CLASSES * SHA1_LEN)
string.memset(topo_order, 0, 4 * MAX_FILE_DEPS)
string.memset(topo_count_box, 0, 4)
class_count_scanned: int = _ScanClassInheritance(
mb, result, reachable_set, reachable_count,
use_reachable, sha1_set, set_count,
class_names_buf, parent_names_buf, def_sha1s_buf)
if class_count_scanned > 0:
ret_topo: int = _TopoSortFiles(
mb, result, reachable_set, reachable_count,
use_reachable, sha1_set, set_count,
class_names_buf, parent_names_buf, def_sha1s_buf, class_count_scanned,
topo_order, topo_count_box)
if ret_topo == 0:
tc_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(topo_count_box, t.CPtr))
if tc_ptr is not None:
topo_count = tc_ptr[0]
fb_topo: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_topo is not None:
viperlib.snprintf(fb_topo, 1024,
"拓扑排序: %d 个类, %d 个文件按依赖顺序处理",
class_count_scanned, topo_count)
VLogger.info(fb_topo, "Phase1")
else:
VLogger.warning("拓扑排序失败,回退到字母序", "Phase1")
topo_count = 0
else:
VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1")
topo_count = 0
# 遍历文件:优先使用拓扑顺序,回退到字母序
iter_count: int = topo_count if topo_count > 0 else result.Count
for i in range(iter_count):
# 获取文件索引:拓扑顺序或字母序
file_idx: int = i
if topo_count > 0:
od_addr: t.CUInt64T = t.CUInt64T(topo_order) + i * 4
od_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(od_addr, t.CPtr))
if od_ptr is not None:
file_idx = od_ptr[0]
entry_addr_r: t.CUInt64T = t.CUInt64T(result.Entries) + file_idx * entry_size
entry_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(entry_addr_r, t.CPtr))
if entry_r is None:
p1a_fl += 1
@@ -252,15 +748,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_fl += 1
continue
# 按需翻译过滤:只处理可达文件
if use_reachable != 0:
if StubMerger._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0:
p1a_skp += 1
continue
else:
if StubMerger._is_in_sha1_set(sha1_r, sha1_set, set_count) == 0:
p1a_skp += 1
continue
# 按需翻译过滤(拓扑排序已过滤,但字母序回退需要检查)
if topo_count == 0:
if use_reachable != 0:
if StubMerger._is_in_sha1_set(sha1_r, reachable_set, reachable_count) == 0:
p1a_skp += 1
continue
else:
if StubMerger._is_in_sha1_set(sha1_r, sha1_set, set_count) == 0:
p1a_skp += 1
continue
# 读取文件内容
file_path_r: str = entry_r.Path
@@ -310,6 +807,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
tr_r._declare_only = 1
tr_r.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry_r.RelPath)
HandlesType.set_current_file(file_path_r)
HandlesType.set_current_module_sha1(sha1_r)
HandlesType.clear_cdefine_constants()
HandlesStruct.reset_visible_structs(mb, 0)
ret_r: int = tr_r.translate(tree_r)
@@ -325,6 +823,18 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(tr_r._nonlocal_names)
stdlib.free(src_buf_r)
# 释放拓扑排序资源
if class_names_buf is not None:
mb.free(class_names_buf)
if parent_names_buf is not None:
mb.free(parent_names_buf)
if def_sha1s_buf is not None:
mb.free(def_sha1s_buf)
if topo_order is not None:
mb.free(topo_order)
if topo_count_box is not None:
mb.free(topo_count_box)
# ============================================================
# Phase 1b: 全量翻译struct 已注册,走 existing 路径翻译方法体)
# ============================================================
@@ -380,12 +890,17 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(text_path)
# text.ll 不存在,需要重新翻译
rp: str = entry.RelPath
stdio.printf("[Phase1] text.ll 不存在,重新翻译: %s (sha1=%s)\n", rp, sha1)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "text.ll 不存在,重新翻译: %s (sha1=%s)", rp, sha1)
VLogger.info(fb, "Phase1")
else:
# stub 不存在,需要翻译
rp: str = entry.RelPath
stdio.printf("[Phase1] 翻译: %s (sha1=%s)\n", rp, sha1)
stdio.fflush(0)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "翻译: %s (sha1=%s)", rp, sha1)
VLogger.info(fb, "Phase1")
stdlib.free(stub_path)
@@ -393,13 +908,17 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
file_path: str = entry.Path
f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R)
if f is None:
stdio.printf("[Phase1] 无法打开(None): %s\n", file_path)
stdio.fflush(0)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "无法打开(None): %s", file_path)
VLogger.error(fb, "Phase1")
failed += 1
continue
if f.closed:
stdio.printf("[Phase1] 无法打开: %s\n", file_path)
stdio.fflush(0)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "无法打开: %s", file_path)
VLogger.error(fb, "Phase1")
failed += 1
continue
@@ -412,8 +931,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
f.close()
if bytes_read <= 0:
stdio.printf("[Phase1] 读取失败: %s\n", file_path)
stdio.fflush(0)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "读取失败: %s", file_path)
VLogger.error(fb, "Phase1")
stdlib.free(src_buf)
failed += 1
continue
@@ -423,6 +944,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
src_buf[SRC_BUF_SIZE - 1] = 0
# 解析 AST
stdio.printf("[P1B-1] parse %s\n", entry.RelPath)
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if lx is None:
stdlib.free(src_buf)
@@ -431,14 +953,18 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
ast._lexer_init(lx, src_buf, mb)
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
stdio.printf("[P1B-1a] parse-done %s\n", entry.RelPath)
if tree is None:
stdio.printf("[Phase1] AST 解析失败: %s\n", file_path)
stdio.fflush(0)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "AST 解析失败: %s", file_path)
VLogger.error(fb, "Phase1")
stdlib.free(src_buf)
failed += 1
continue
# 生成 .pyi 存根文件(直接遍历 AST不依赖 PythonToStubConverter
stdio.printf("[P1B-2] pyi %s\n", entry.RelPath)
pyi_buf: bytes = stdlib.malloc(PYI_BUF_SIZE)
if pyi_buf is not None:
pyi_pos: t.CSizeT = StubConverter._GeneratePyiFromAst(mb, tree, entry.RelPath, pyi_buf, PYI_BUF_SIZE)
@@ -453,6 +979,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(pyi_buf)
# 翻译 AST → LLVM IR
stdio.printf("[P1B-3] translate %s\n", entry.RelPath)
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
if tr is None:
stdlib.free(src_buf)
@@ -461,11 +988,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
tr.ModuleSha1 = sha1
tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, entry.RelPath)
HandlesType.set_current_file(file_path)
HandlesType.set_current_module_sha1(sha1)
HandlesType.clear_cdefine_constants()
HandlesStruct.reset_visible_structs(mb, 0)
ret: int = tr.translate(tree)
stdio.printf("[P1B-3a] translate-done %s ret=%d\n", entry.RelPath, ret)
if ret != 0:
stdio.printf("[Phase1] 翻译失败: %s\n", file_path)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "翻译失败: %s", file_path)
VLogger.error(fb, "Phase1")
stdlib.free(src_buf)
if tr._global_names is not None:
stdlib.free(tr._global_names)
@@ -475,7 +1007,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
continue
# dump stub IR (declarations only)
PHASE1_IR_SIZE: t.CSizeT = 262144
stdio.printf("[P1B-4] stub %s\n", entry.RelPath)
PHASE1_IR_SIZE: t.CSizeT = 1048576
stub_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE)
if stub_buf is None:
stdlib.free(src_buf)
@@ -500,6 +1033,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
stdlib.free(stub_buf)
# dump text IR (definitions only)
stdio.printf("[P1B-5] text %s\n", entry.RelPath)
text_buf: bytes = stdlib.malloc(PHASE1_IR_SIZE)
if text_buf is None:
stdlib.free(src_buf)
@@ -544,7 +1078,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
translated += 1
stdio.printf("[Phase1] 完成: 翻译=%d 跳过=%d 失败=%d\n", translated, skipped, failed)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "完成: 翻译=%d 跳过=%d 失败=%d", translated, skipped, failed)
VLogger.info(fb, "Phase1")
# 释放可达 SHA1 集合(如果分配了)
if reachable_set is not None:

View File

@@ -17,6 +17,7 @@ import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.BuildPipeline as BuildPipeline
import lib.core.StubMerger as StubMerger
import lib.Projectrans.Utils as Utils
@@ -138,7 +139,10 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy | t.CPtr,
ent.Path = full_path
ent.Sha1 = sha1_val
file_count += 1
stdio.printf("[project] %s (sha1=%s)\n", fname, sha1_val)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "%s (sha1=%s)", fname, sha1_val)
VLogger.info(fb, "project")
stdlib.free(sbuf)
# full_path 不释放ent.Path 引用它
@@ -165,10 +169,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
Returns: 0 成功,非 0 失败
"""
if source_dir is None:
stdio.printf("[project] source_dir 为空\n")
VLogger.error("source_dir 为空", "project")
return 1
stdio.printf("[project] 多文件项目编译: %s\n", source_dir)
stdio.printf("[DBG] RunMultiFileProject enter src=%s\n", source_dir)
stdio.fflush(0)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "多文件项目编译: %s", source_dir)
VLogger.info(fb, "project")
# === 1. 递归扫描 source_dir 下的 .py 文件(包括子目录)===
entry_size: t.CSizeT = SrcFileEntry.__sizeof__()
@@ -178,7 +188,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
string.memset(entries, 0, MAX_SRC_FILES * entry_size)
file_count: int = _ScanDirForPyFiles(mb, source_dir, entries, entry_size, 0, MAX_SRC_FILES)
stdio.printf("[project] 共 %d 个源文件\n", file_count)
stdio.printf("[DBG] scan done file_count=%d\n", file_count)
stdio.fflush(0)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "%d 个源文件", file_count)
VLogger.info(fb, "project")
if file_count == 0:
stdlib.free(entries)
@@ -191,34 +206,47 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
BuildPipeline.ensure_dir(temp_dir)
BuildPipeline.ensure_dir(output_dir)
# 追加 App 源文件 SHA1 到 _sha1_map.txt供日志转存和符号查找使用
# 格式: {sha1}:App/{filename}\n (与 includes 条目格式对应
# 追加 App 源文件 SHA1 到 _sha1_map.txt人类可读输出,程序内部不读取此文件
# 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py
# 同步追加到内存存储器AppendToSha1MapStore供跨模块 CDefine 查找)
td_len_am: t.CSizeT = string.strlen(temp_dir)
src_dir_len_am: t.CSizeT = string.strlen(source_dir)
map_path_am: bytes = stdlib.malloc(td_len_am + 32)
if map_path_am is not None:
viperlib.snprintf(map_path_am, td_len_am + 32, "%s/_sha1_map.txt", temp_dir)
mf: fileio.File | t.CPtr = fileio.File(map_path_am, fileio.MODE.A)
line_am: bytes = stdlib.malloc(512)
# 先遍历一次填充内存存储器(不依赖文件 I/O再写入 _sha1_map.txt
app_filled: int = 0
for i in range(file_count):
ea_am: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent_am: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_am, t.CPtr))
if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None:
continue
# 计算相对路径(去除 source_dir 前缀,保留目录结构)
p_str: str = ent_am.Path
p_len: t.CSizeT = string.strlen(p_str)
rel_path_am: str = p_str
if p_len > src_dir_len_am + 1:
if string.strncmp(p_str, source_dir, src_dir_len_am) == 0:
rel_path_am = p_str + src_dir_len_am + 1
# 同步追加到内存存储器(供跨模块 CDefine 查找)
# rel_path_am 格式如 "lib/core/StubMerger.py",与 _sha1_map.txt 一致
if StubMerger.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) == 0:
app_filled += 1
# 写入 _sha1_map.txt人类可读输出
if not mf.closed and line_am is not None:
viperlib.snprintf(line_am, 512, "%s:%s\n", ent_am.Sha1, rel_path_am)
ll_am: t.CSizeT = string.strlen(line_am)
mf.write(line_am, ll_am)
if line_am is not None:
stdlib.free(line_am)
if not mf.closed:
line_am: bytes = stdlib.malloc(512)
if line_am is not None:
for i in range(file_count):
ea_am: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent_am: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_am, t.CPtr))
if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None:
continue
# 从路径提取文件名(最后一个 / 或 \\ 之后的部分)
p_str: str = ent_am.Path
p_len: t.CSizeT = string.strlen(p_str)
f_start: t.CSizeT = 0
for j in range(p_len):
if p_str[j] == '/' or p_str[j] == '\\':
f_start = j + 1
viperlib.snprintf(line_am, 512, "%s:App/%s\n", ent_am.Sha1, p_str + f_start)
ll_am: t.CSizeT = string.strlen(line_am)
mf.write(line_am, ll_am)
stdlib.free(line_am)
mf.close()
stdio.printf("[project] 已追加 %d 个 App 文件到 _sha1_map.txt\n", file_count)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "已追加 %d 个 App 文件到 _sha1_map.txt 和内存存储器", app_filled)
VLogger.info(fb, "project")
stdlib.free(map_path_am)
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用)
@@ -231,6 +259,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# 追加用户源文件的 (模块名, SHA1) 到全局映射
# 使跨模块方法调用能通过 from_imports + _lookup_module_sha1 找到正确的 SHA1
if pb_sha1_arr is not None and pb_mod_arr is not None:
src_dir_len_us: t.CSizeT = string.strlen(source_dir)
for i in range(file_count):
ea_us: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent_us: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_us, t.CPtr))
@@ -238,24 +267,26 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
continue
if pb_inc_count >= StubMerger.MAX_INCLUDES:
break
# 从路径提取模块名(文件名去掉 .py 后缀
# 计算相对路径并转换为点分模块名(保留完整包路径
path_str: str = ent_us.Path
path_len: t.CSizeT = string.strlen(path_str)
fname_start: t.CSizeT = 0
for j in range(path_len):
if path_str[j] == '/' or path_str[j] == '\\':
fname_start = j + 1
mod_len: t.CSizeT = path_len - fname_start
if mod_len < 4:
path_len_us: t.CSizeT = string.strlen(path_str)
rel_path_us: str = path_str
if path_len_us > src_dir_len_us + 1:
if string.strncmp(path_str, source_dir, src_dir_len_us) == 0:
rel_path_us = path_str + src_dir_len_us + 1
# 使用 _PathToModuleName 转换为点分模块名(处理 __init__.py → 包名)
mod_name_us: str = StubMerger._PathToModuleName(rel_path_us)
if mod_name_us is None:
continue
mod_len -= 3
if mod_len >= 64:
mod_len = 63
# 写入模块名
mod_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 64
for k in range(mod_len):
pb_mod_arr[mod_idx + k] = path_str[fname_start + k]
pb_mod_arr[mod_idx + mod_len] = '\0'
mn_len_us: t.CSizeT = string.strlen(mod_name_us)
if mn_len_us >= 64:
string.strncpy(pb_mod_arr + mod_idx, mod_name_us, 63)
pb_mod_arr[mod_idx + 63] = '\0'
else:
string.strcpy(pb_mod_arr + mod_idx, mod_name_us)
stdlib.free(mod_name_us)
# 写入 SHA1
sha1_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 17
string.strcpy(pb_sha1_arr + sha1_idx, ent_us.Sha1)
@@ -265,36 +296,60 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# === 2. Phase A: 为每个文件生成 stub + text ===
if do_phase1 != 0:
stdio.printf("[DBG] before Phase A\n")
stdio.fflush(0)
if log is not None:
log.banner("Phase A: 生成 stub + text")
# === Phase A-pre: 预注册所有源文件的 struct/enum/union ===
# 解决循环引用问题circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义
# 仅注册 struct/enum/uniondeclare_only=1不翻译方法体
stdio.printf("[Phase A-pre] 预注册 struct/enum/union...\n")
# 多遍扫描:解决跨文件继承的字母序问题(如 HandlesAnnAssign.py 在 HandlesBase.py 之前,
# AnnAssignHandle 继承 Mixin 时 Mixin 未注册)。每遍注册新类后,下一遍子类可继承。
VLogger.info("预注册 struct/enum/union...", "PhaseA")
src_dir_len_pre: t.CSizeT = string.strlen(source_dir)
for i in range(file_count):
ea_pre: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent_pre: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_pre, t.CPtr))
if ent_pre is None or ent_pre.Path is None:
continue
pkg_pre: str = None
if string.strlen(ent_pre.Path) > src_dir_len_pre + 1:
rel_path_pre: str = ent_pre.Path + src_dir_len_pre + 1
pkg_pre = HandlesImports.compute_package_from_relpath(mb, rel_path_pre)
tr_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1)
if tr_pre is None:
stdio.printf("[Phase A-pre] 警告: 预注册失败: %s\n", ent_pre.Path)
stdio.printf("[Phase A-pre] 预注册完成\n")
PHASE_A_PRE_MAX_PASSES: t.CInt = 3
for pass_i in range(PHASE_A_PRE_MAX_PASSES):
struct_count_before: int = HandlesStruct.get_struct_count()
stdio.printf("[DBG] PhaseA-pre pass=%d struct_count_before=%d\n", pass_i, struct_count_before)
stdio.fflush(0)
for i in range(file_count):
ea_pre: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent_pre: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_pre, t.CPtr))
if ent_pre is None or ent_pre.Path is None:
continue
pkg_pre: str = None
if string.strlen(ent_pre.Path) > src_dir_len_pre + 1:
rel_path_pre: str = ent_pre.Path + src_dir_len_pre + 1
pkg_pre = HandlesImports.compute_package_from_relpath(mb, rel_path_pre)
stdio.printf("[DBG] PhaseA-pre pass=%d file=%s\n", pass_i, ent_pre.Path)
stdio.fflush(0)
tr_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1)
if tr_pre is None:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "预注册失败: %s", ent_pre.Path)
VLogger.warning(fb, "PhaseA")
struct_count_after: int = HandlesStruct.get_struct_count()
stdio.printf("[DBG] PhaseA-pre pass=%d done struct_count_after=%d\n", pass_i, struct_count_after)
stdio.fflush(0)
# 收敛检查:本遍没有新结构体注册 → 所有类已注册
if struct_count_after == struct_count_before:
break
VLogger.info("预注册完成", "PhaseA")
PHASE_A_IR_SIZE: t.CSizeT = 262144
PHASE_A_IR_SIZE: t.CSizeT = 1048576
td_len_pa: t.CSizeT = string.strlen(temp_dir)
stdio.printf("[DBG] PhaseA full-translate start\n")
stdio.fflush(0)
for i in range(file_count):
ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
if ent is None or ent.Path is None:
continue
stdio.printf("[DBG] PhaseA file=%s\n", ent.Path)
stdio.fflush(0)
# 计算源文件的包名(相对 source_dir 的目录部分)
src_dir_len_pa: t.CSizeT = string.strlen(source_dir)
@@ -352,12 +407,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
stdlib.free(deps_path_a)
if do_phase2 == 0:
stdio.printf("[project] Phase A 完成(仅 stub 生成)\n")
VLogger.info("Phase A 完成(仅 stub 生成)", "project")
stdlib.free(entries)
return 0
# === 3. Phase B: 编译每个文件为 .obj ===
if do_phase2 != 0:
stdio.printf("[DBG] before Phase B\n")
stdio.fflush(0)
if log is not None:
log.banner("Phase B: 编译 .obj")
@@ -384,28 +441,51 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
if ent is None or ent.Path is None or ent.Sha1 is None:
continue
stdio.printf("[DBG] PhaseB file=%s\n", ent.Path)
stdio.fflush(0)
# 组合本地 stub + 所有依赖 stub + 本地 text → 完整 IR
stdio.printf("[DBG] PhaseB step=alloc combined_ir\n")
stdio.fflush(0)
combined_ir: bytes = stdlib.malloc(COMBINED_IR_SIZE)
if combined_ir is None:
stdio.printf("[Phase B] combined_ir 分配失败: %s\n", ent.Path)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "combined_ir 分配失败: %s", ent.Path)
VLogger.error(fb, "PhaseB")
continue
stdio.printf("[DBG] PhaseB step=BuildCombinedIR sha1=%s\n", ent.Sha1)
stdio.fflush(0)
combined_len: t.CSizeT = StubMerger.BuildCombinedIR(temp_dir, ent.Sha1, combined_ir, COMBINED_IR_SIZE)
stdio.printf("[DBG] PhaseB step=BuildCombinedIR done len=%d\n", combined_len)
stdio.fflush(0)
if combined_len == 0:
stdio.printf("[Phase B] BuildCombinedIR 失败: %s\n", ent.Path)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", ent.Path)
VLogger.error(fb, "PhaseB")
stdlib.free(combined_ir)
continue
# 编译为 .obj
stdio.printf("[DBG] PhaseB step=compile_module_to_obj\n")
stdio.fflush(0)
cret: int = BuildPipeline.compile_module_to_obj(
combined_ir, combined_len, temp_dir, output_dir, ent.Sha1,
cc_cmd, cc_flags)
stdio.printf("[DBG] PhaseB step=compile_module_to_obj done cret=%d\n", cret)
stdio.fflush(0)
stdlib.free(combined_ir)
if cret != 0:
stdio.printf("[FATAL][Phase B] llc 编译失败,终止编译: %s\n", ent.Path)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "llc 编译失败,终止编译: %s", ent.Path)
VLogger.error(fb, "PhaseB")
sys.exit(1)
compiled_count += 1
stdio.printf("[DBG] PhaseB step=obj_path construct\n")
stdio.fflush(0)
# 构造 .obj 路径,检测是否是 main 模块test_main.py 或 main.py
od_len: t.CSizeT = string.strlen(output_dir)
@@ -424,7 +504,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
if op_sliced_len < 512:
string.strcpy(main_obj_path, obj_path_sliced)
else:
stdio.printf("[Phase B] 警告: main_obj_path 缓冲区不足\n")
VLogger.warning("main_obj_path 缓冲区不足", "PhaseB")
else:
# 其他模块: 追加到 obj_paths
if obj_pos + op_sliced_len + 2 < OBJ_PATHS_SIZE:
@@ -435,13 +515,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
obj_pos += op_sliced_len
obj_paths[obj_pos] = '\0'
else:
stdio.printf("[Phase B] 警告: .obj 路径缓冲区不足\n")
VLogger.warning(".obj 路径缓冲区不足", "PhaseB")
stdlib.free(obj_path_sliced)
stdio.printf("[DBG] PhaseB step=obj_path done\n")
stdio.fflush(0)
stdio.printf("[Phase B] 编译完成: %d/%d\n", compiled_count, file_count)
stdio.printf("[DBG] PhaseB loop done compiled_count=%d\n", compiled_count)
stdio.fflush(0)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "编译完成: %d/%d", compiled_count, file_count)
VLogger.success(fb, "PhaseB")
if compiled_count == 0:
stdio.printf("[Phase B] 无成功编译的文件\n")
VLogger.error("无成功编译的文件", "PhaseB")
stdlib.free(entries)
return 1
@@ -449,198 +536,191 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# includes.binary 可能缺少某些 includes .obj如 testcheck.py
# 这些文件被用户项目导入但未被 TransPyV 自身依赖Projectrans.py 未编译它们。
# 检测并编译缺失的 includes 文件到 output_dir加入链接命令。
# 数据来源StubMerger 全局内存存储器(不读取 _sha1_map.txt 文件)
if includes_dir is not None and includes_binary_dir is not None:
stdio.printf("[DBG] PhaseB+ start\n")
stdio.fflush(0)
inc_compiled: int = 0
td_len_mi: t.CSizeT = string.strlen(temp_dir)
map_path_mi: bytes = stdlib.malloc(td_len_mi + 32)
if map_path_mi is not None:
viperlib.snprintf(map_path_mi, td_len_mi + 32, "%s/_sha1_map.txt", temp_dir)
mapf_mi: fileio.File | t.CPtr = fileio.File(map_path_mi, fileio.MODE.R)
if not mapf_mi.closed:
map_buf_mi: bytes = stdlib.malloc(65536)
if map_buf_mi is not None:
map_br_mi: t.CInt64T = mapf_mi.read_all(map_buf_mi, 65536)
mapf_mi.close()
if map_br_mi > 0:
if map_br_mi < 65536:
map_buf_mi[map_br_mi] = '\0'
else:
map_buf_mi[65535] = '\0'
map_len_mi: t.CSizeT = map_br_mi
mpos_mi: t.CSizeT = 0
while mpos_mi < map_len_mi:
ml_start_mi: t.CSizeT = mpos_mi
while mpos_mi < map_len_mi:
if map_buf_mi[mpos_mi] == '\n':
break
mpos_mi += 1
ml_len_mi: t.CSizeT = mpos_mi - ml_start_mi
mpos_mi += 1
# 从全局存储器获取 SHA1/模块名/rel_path 数组(直接访问器,避免 box 解引用问题)
store_count_mi: int = StubMerger.GetSha1StoreCount()
stdio.printf("[DBG] PhaseB+ store_count=%d\n", store_count_mi)
stdio.fflush(0)
if store_count_mi > 0:
store_sha1_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
store_mod_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreModArrPtr()
store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
stdio.printf("[DBG] PhaseB+ arrs ok sha1=%p mod=%p rel=%p\n", store_sha1_arr_mi, store_mod_arr_mi, store_rel_arr_mi)
stdio.fflush(0)
for si_mi in range(store_count_mi):
# 获取当前条目的 SHA1 和 rel_path
inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(si_mi) * 17
inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_REL_PATH_LEN
stdio.printf("[DBG] PhaseB+ iter=%d sha1=%s rel=%s\n", si_mi, inc_sha1_mi, inc_rel_mi)
stdio.fflush(0)
# 最小长度: 16(sha1)+1(:)+9(includes/)+1+1+1 = 29
if ml_len_mi < 26:
continue
if map_buf_mi[ml_start_mi + 16] != ':':
continue
rp_start_mi: t.CSizeT = ml_start_mi + 17
if string.strncmp(map_buf_mi + rp_start_mi, "includes/", 9) != 0:
continue
# 检查 .obj 是否已存在于 includes.binary
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35)
if check_pat_mi is None:
continue
viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi)
check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
obj_exists_mi: int = 0
if check_fd_mi is not None:
string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE:
w32.win32file.FindClose(check_h_mi)
obj_exists_mi = 1
if obj_exists_mi != 0:
continue
# 提取 SHA1
inc_sha1_mi: str = stdlib.malloc(17)
if inc_sha1_mi is None:
continue
string.strncpy(inc_sha1_mi, map_buf_mi + ml_start_mi, 16)
inc_sha1_mi[16] = '\0'
# .obj 不存在,需要编译
# 构造源文件路径: {includes_dir}/{rel_path}
rel_path_len_mi: t.CSizeT = string.strlen(inc_rel_mi)
inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir)
src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1)
if src_fp_mi is None:
continue
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1,
"%s/%s", includes_dir, inc_rel_mi)
# 检查 .obj 是否已存在于 includes.binary
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35)
if check_pat_mi is None:
continue
viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi)
check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
obj_exists_mi: int = 0
if check_fd_mi is not None:
string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE:
w32.win32file.FindClose(check_h_mi)
obj_exists_mi = 1
if obj_exists_mi != 0:
continue
# 检查是否为声明文件(只有 declare 没有实质 define
# 判断方法: text.ll 中若有混淆函数 define含 @")则为实现文件
is_decl_mi: int = -1
tpath_mi: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
if tpath_mi is not None:
tf_mi: fileio.File | t.CPtr = fileio.File(tpath_mi, fileio.MODE.R)
if not tf_mi.closed:
is_decl_mi = 1
tbuf_mi: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
if tbuf_mi is not None:
tbr_mi: t.CInt64T = tf_mi.read_all(tbuf_mi, StubMerger.STUB_READ_BUF_SIZE)
if tbr_mi > 0:
if tbr_mi < StubMerger.STUB_READ_BUF_SIZE:
tbuf_mi[tbr_mi] = '\0'
else:
tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
# 逐行扫描: 找 define 行中含 @" 的(混淆函数名)
tpos_mi: t.CSizeT = 0
while tpos_mi < tbr_mi:
tls_mi: t.CSizeT = tpos_mi
while tpos_mi < tbr_mi:
if tbuf_mi[tpos_mi] == '\n':
break
tpos_mi += 1
tll_mi: t.CSizeT = tpos_mi - tls_mi
if tpos_mi < tbr_mi:
tpos_mi += 1
if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0:
# 临时在行尾加 \0 供 strstr 使用
saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi]
tbuf_mi[tls_mi + tll_mi] = '\0'
if string.strstr(tbuf_mi + tls_mi, "@\"") is not None:
tbuf_mi[tls_mi + tll_mi] = saved_mi
is_decl_mi = 0
break
tbuf_mi[tls_mi + tll_mi] = saved_mi
stdlib.free(tbuf_mi)
tf_mi.close()
stdlib.free(tpath_mi)
# is_decl_mi == -1: text.ll 不存在Phase1 未翻译此文件,可能不被项目导入)
# is_decl_mi == 1: 声明文件(仅 declare 无 define
# 两种情况都跳过:不编译未被 Phase1 翻译的 includes 文件
if is_decl_mi != 0:
continue
# .obj 不存在,需要编译
# 构造源文件路径: {includes_dir}/{rel_path_without_includes_prefix}
rel_path_len_mi: t.CSizeT = ml_len_mi - 26
inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir)
src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1)
if src_fp_mi is None:
continue
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1,
"%s/%s", includes_dir, map_buf_mi + rp_start_mi + 9)
# 尝试 BuildCombinedIRstub/text 应已由 Phase1 生成)
inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE)
inc_combined_len_mi: t.CSizeT = 0
if inc_combined_mi is not None:
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
# 检查是否为声明文件(只有 declare 没有实质 define
# 判断方法: text.ll 中若有混淆函数 define含 @\")则为实现文件
is_decl_mi: int = -1
tpath_mi: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
if tpath_mi is not None:
tf_mi: fileio.File | t.CPtr = fileio.File(tpath_mi, fileio.MODE.R)
if not tf_mi.closed:
is_decl_mi = 1
tbuf_mi: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
if tbuf_mi is not None:
tbr_mi: t.CInt64T = tf_mi.read_all(tbuf_mi, StubMerger.STUB_READ_BUF_SIZE)
if tbr_mi > 0:
if tbr_mi < StubMerger.STUB_READ_BUF_SIZE:
tbuf_mi[tbr_mi] = '\0'
else:
tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
# 逐行扫描: 找 define 行中含 @\" 的(混淆函数名)
tpos_mi: t.CSizeT = 0
while tpos_mi < tbr_mi:
tls_mi: t.CSizeT = tpos_mi
while tpos_mi < tbr_mi:
if tbuf_mi[tpos_mi] == '\n':
break
tpos_mi += 1
tll_mi: t.CSizeT = tpos_mi - tls_mi
if tpos_mi < tbr_mi:
tpos_mi += 1
if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0:
# 临时在行尾加 \0 供 strstr 使用
saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi]
tbuf_mi[tls_mi + tll_mi] = '\0'
if string.strstr(tbuf_mi + tls_mi, "@\"") is not None:
tbuf_mi[tls_mi + tll_mi] = saved_mi
is_decl_mi = 0
break
tbuf_mi[tls_mi + tll_mi] = saved_mi
stdlib.free(tbuf_mi)
tf_mi.close()
stdlib.free(tpath_mi)
if is_decl_mi == 1:
continue
# 如果 stub/text 不存在,翻译源文件并保存 stub + text然后重试
if inc_combined_len_mi == 0 and inc_combined_mi is not None:
# 计算 includes 文件的包名(相对 includes_dir 的目录部分)
inc_pkg_mi: str = None
if string.strlen(src_fp_mi) > inc_dir_len_mi + 1:
inc_rel_mi2: str = src_fp_mi + inc_dir_len_mi + 1
inc_pkg_mi = HandlesImports.compute_package_from_relpath(mb, inc_rel_mi2)
tr_mi: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, src_fp_mi, inc_sha1_mi, inc_pkg_mi)
if tr_mi is not None:
PBP_IR_SIZE: t.CSizeT = 1048576
# 保存 stub.ll (切片路径)
inc_stub_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
if inc_stub_buf is not None:
tr_mi.dump_ir(inc_stub_buf, PBP_IR_SIZE, llvmlite.OUTPUT_STUB)
inc_stub_len: t.CSizeT = string.strlen(inc_stub_buf)
inc_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll")
if inc_stub_path is not None:
isf: fileio.File | t.CPtr = fileio.File(inc_stub_path, fileio.MODE.W)
if not isf.closed:
isf.write(inc_stub_buf, inc_stub_len)
isf.close()
stdlib.free(inc_stub_path)
stdlib.free(inc_stub_buf)
# 保存 text.ll (切片路径)
inc_text_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
if inc_text_buf is not None:
tr_mi.dump_ir(inc_text_buf, PBP_IR_SIZE, llvmlite.OUTPUT_TEXT)
inc_text_len: t.CSizeT = string.strlen(inc_text_buf)
inc_text_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
if inc_text_path is not None:
itf: fileio.File | t.CPtr = fileio.File(inc_text_path, fileio.MODE.W)
if not itf.closed:
itf.write(inc_text_buf, inc_text_len)
itf.close()
stdlib.free(inc_text_path)
stdlib.free(inc_text_buf)
# 重试 BuildCombinedIR
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
# 尝试 BuildCombinedIRstub/text 应已由 Phase1 生成)
inc_combined_mi: bytes = stdlib.malloc(COMBINED_IR_SIZE)
inc_combined_len_mi: t.CSizeT = 0
if inc_combined_mi is not None:
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
if inc_combined_len_mi == 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", src_fp_mi)
VLogger.error(fb, "PhaseB+")
if inc_combined_mi is not None:
stdlib.free(inc_combined_mi)
continue
# 如果 stub/text 不存在,翻译源文件并保存 stub + text然后重试
if inc_combined_len_mi == 0 and inc_combined_mi is not None:
# 计算 includes 文件的包名(相对 includes_dir 的目录部分)
inc_pkg_mi: str = None
if string.strlen(src_fp_mi) > inc_dir_len_mi + 1:
inc_rel_mi: str = src_fp_mi + inc_dir_len_mi + 1
inc_pkg_mi = HandlesImports.compute_package_from_relpath(mb, inc_rel_mi)
tr_mi: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, src_fp_mi, inc_sha1_mi, inc_pkg_mi)
if tr_mi is not None:
PBP_IR_SIZE: t.CSizeT = 262144
# 保存 stub.ll (切片路径)
inc_stub_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
if inc_stub_buf is not None:
tr_mi.dump_ir(inc_stub_buf, PBP_IR_SIZE, llvmlite.OUTPUT_STUB)
inc_stub_len: t.CSizeT = string.strlen(inc_stub_buf)
inc_stub_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll")
if inc_stub_path is not None:
isf: fileio.File | t.CPtr = fileio.File(inc_stub_path, fileio.MODE.W)
if not isf.closed:
isf.write(inc_stub_buf, inc_stub_len)
isf.close()
stdlib.free(inc_stub_path)
stdlib.free(inc_stub_buf)
# 保存 text.ll (切片路径)
inc_text_buf: bytes = stdlib.malloc(PBP_IR_SIZE)
if inc_text_buf is not None:
tr_mi.dump_ir(inc_text_buf, PBP_IR_SIZE, llvmlite.OUTPUT_TEXT)
inc_text_len: t.CSizeT = string.strlen(inc_text_buf)
inc_text_path: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
if inc_text_path is not None:
itf: fileio.File | t.CPtr = fileio.File(inc_text_path, fileio.MODE.W)
if not itf.closed:
itf.write(inc_text_buf, inc_text_len)
itf.close()
stdlib.free(inc_text_path)
stdlib.free(inc_text_buf)
# 重试 BuildCombinedIR
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
# 编译为 .obj
inc_cret_mi: int = BuildPipeline.compile_module_to_obj(
inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi,
cc_cmd, cc_flags)
stdlib.free(inc_combined_mi)
if inc_cret_mi != 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "llc 编译失败,终止编译: %s", src_fp_mi)
VLogger.error(fb, "PhaseB+")
sys.exit(1)
if inc_combined_len_mi == 0:
stdio.printf("[Phase B+] BuildCombinedIR 失败: %s\n", src_fp_mi)
if inc_combined_mi is not None:
stdlib.free(inc_combined_mi)
continue
inc_compiled += 1
# 编译为 .obj
inc_cret_mi: int = BuildPipeline.compile_module_to_obj(
inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi,
cc_cmd, cc_flags)
stdlib.free(inc_combined_mi)
if inc_cret_mi != 0:
stdio.printf("[FATAL][Phase B+] llc 编译失败,终止编译: %s\n", src_fp_mi)
sys.exit(1)
# 添加到 obj_paths (切片路径)
od_len_mi: t.CSizeT = string.strlen(output_dir)
inc_obj_sliced: str = StubMerger._sliced_path(output_dir, od_len_mi, inc_sha1_mi, "obj")
if inc_obj_sliced is not None:
inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced)
if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE:
if obj_pos > 0:
obj_paths[obj_pos] = ' '
obj_pos += 1
string.strcpy(obj_paths + obj_pos, inc_obj_sliced)
obj_pos += inc_obj_len
obj_paths[obj_pos] = '\0'
stdlib.free(inc_obj_sliced)
inc_compiled += 1
# 添加到 obj_paths (切片路径)
od_len_mi: t.CSizeT = string.strlen(output_dir)
inc_obj_sliced: str = StubMerger._sliced_path(output_dir, od_len_mi, inc_sha1_mi, "obj")
if inc_obj_sliced is not None:
inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced)
if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE:
if obj_pos > 0:
obj_paths[obj_pos] = ' '
obj_pos += 1
string.strcpy(obj_paths + obj_pos, inc_obj_sliced)
obj_pos += inc_obj_len
obj_paths[obj_pos] = '\0'
stdlib.free(inc_obj_sliced)
stdio.printf("[Phase B+] 编译缺失 includes: %d\n", inc_compiled)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "编译缺失 includes: %d", inc_compiled)
VLogger.info(fb, "PhaseB+")
# === 4. Phase C: 链接所有 .obj → .exe ===
stdio.printf("[DBG] PhaseC start\n")
stdio.fflush(0)
if log is not None:
log.banner("Phase C: 链接")
@@ -674,20 +754,33 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
final_obj_paths[fop_pos] = '\0'
obj_paths_len: t.CSizeT = fop_pos
stdio.printf("[DBG] PhaseC before link_objs_to_exe len=%d\n", obj_paths_len)
stdio.fflush(0)
lret: int = BuildPipeline.link_objs_to_exe(
final_obj_paths, obj_paths_len,
linker_cmd, linker_flags, exe_path,
includes_binary_dir)
stdio.printf("[DBG] PhaseC link_objs_to_exe done lret=%d\n", lret)
stdio.fflush(0)
if lret == 0:
stdio.printf("输出: %s\n", exe_path)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "输出: %s", exe_path)
VLogger.success(fb, "PhaseC")
if args.get_bool("run"):
stdio.printf("[run] 执行: %s\n", exe_path)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "执行: %s", exe_path)
VLogger.info(fb, "run")
rp: subprocess.CompletedProcess | t.CPtr = subprocess.run(exe_path, False, False)
if rp is not None:
stdio.printf("[run] 退出码: %d\n", rp.returncode)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "退出码: %d", rp.returncode)
VLogger.info(fb, "run")
else:
stdio.printf("[Phase C] 链接失败\n")
VLogger.error("链接失败", "PhaseC")
stdlib.free(entries)
return 1

View File

@@ -11,6 +11,7 @@ import w32.fileio as fileio
import viperlib
import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesImports as HandlesImports
@@ -39,10 +40,20 @@ _mbuddy: memhub.MemManager | t.CPtr
# 最大 includes SHA1 数(用于过滤)
MAX_INCLUDES_SHA1: t.CDefine = 256
# stub 读取缓冲区大小
STUB_READ_BUF_SIZE: t.CDefine = 262144
STUB_READ_BUF_SIZE: t.CDefine = 1048576
# 最大 includes 条目数
MAX_INCLUDES: t.CDefine = 256
# 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
# Phase1 扫描完成后填充,供所有函数使用
_sha1_store_arr: bytes # SHA1 数组(每个 17 字节MAX_INCLUDES 个)
_sha1_store_mod: bytes # 模块名数组(每个 64 字节MAX_INCLUDES 个)
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节MAX_INCLUDES 个)
_sha1_store_count: int # 条目数
# rel_path 最大长度
MAX_REL_PATH_LEN: t.CDefine = 256
# ============================================================
# _sliced_path - 构建切片路径stdlib.malloc 分配,调用者负责 free
@@ -217,77 +228,31 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
# ============================================================
# _load_includes_sha1_set - 读取 _sha1_map.txt收集 includes/ 开头的 SHA1
# _load_includes_sha1_set - 从全局内存存储器收集 includes 的 SHA1
#
# _sha1_map.txt 格式:{sha16}:{rel_path}
# 仅收集 rel_path 以 "includes/" 开头的 SHA1用于过滤 stub 文件,
# 避免加载 TransPyV 自身源文件lib/*)的 stub 导致链接失败。
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt
# 用于过滤 stub 文件,避免加载 TransPyV 自身源文件lib/*)的 stub 导致链接失败。
# ============================================================
def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr,
temp_dir: str,
sha1_set: str) -> int:
"""读取 _sha1_map.txt将 includes/ 开头的 SHA1 写入 sha1_set
"""从全局内存存储器将 includes 的 SHA1 写入 sha1_set
pool/temp_dir 参数保留以兼容现有调用者,但不再使用。
数据来源是 PopulateSha1MapStore 填充的全局存储器。
sha1_set 大小为 MAX_INCLUDES_SHA1 * 17每个 SHA1 16字符+null
返回找到的 includes SHA1 数量,-1 表示错误"""
if pool is None or temp_dir is None or sha1_set is None:
if sha1_set is None:
return -1
# 构造路径 temp_dir/_sha1_map.txt使用 stdlib.malloc 避免 mbuddy 池耗尽)
dir_len: t.CSizeT = string.strlen(temp_dir)
map_path: bytes = stdlib.malloc(dir_len + 32)
if map_path is None:
if _sha1_store_arr is None:
return -1
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
# 打开文件
f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R)
if f.closed:
return -1
# 读取内容(使用 stdlib.malloc 避免 mbuddy 池耗尽)
MAP_BUF_SIZE: t.CSizeT = 65536
content: str = stdlib.malloc(MAP_BUF_SIZE)
if content is None:
f.close()
return -1
bytes_read: t.CInt64T = f.read_all(content, MAP_BUF_SIZE)
f.close()
if bytes_read <= 0:
return -1
if bytes_read < MAP_BUF_SIZE:
content[bytes_read] = '\0'
else:
content[MAP_BUF_SIZE - 1] = '\0'
# 解析行: {sha1}:{rel_path}
count: int = 0
pos: t.CSizeT = 0
content_len: t.CSizeT = bytes_read
while pos < content_len:
# 找行尾
line_start: t.CSizeT = pos
while pos < content_len:
if content[pos] == '\n':
break
pos += 1
line_len: t.CSizeT = pos - line_start
pos += 1 # skip \n
# 最小长度: 16(sha1) + 1(:) + 9(includes/) = 26
if line_len < 26:
continue
# 检查第17个字符是 ':'
if content[line_start + 16] != ':':
continue
# 检查 rel_path 是否以 "includes/" 开头
rel_start: t.CSizeT = line_start + 17
if string.strncmp(content + rel_start, "includes/", 9) == 0:
if count < MAX_INCLUDES_SHA1:
string.strncpy(sha1_set + count * 17, content + line_start, 16)
sha1_set[count * 17 + 16] = '\0'
count += 1
for i in range(_sha1_store_count):
if count >= MAX_INCLUDES_SHA1:
break
idx: t.CSizeT = t.CSizeT(i) * 17
string.strcpy(sha1_set + t.CSizeT(count) * 17, _sha1_store_arr + idx)
count += 1
return count
@@ -305,10 +270,10 @@ def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
# ============================================================
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt
# WriteIncludesSha1Map - 将 includes 扫描结果写入 _sha1_map.txt(人类可读输出)
#
# TransPyV.exe 自己生成 _sha1_map.txt不再依赖 Projectrans.py 预生成),
# 确保所有 includes 文件的 SHA1 都被记录,供 StubMerger 加载 stub 时过滤
# _sha1_map.txt 仅作为人类可读的调试输出,程序内部不读取此文件。
# 机器分析使用 PopulateSha1MapStore 填充的全局内存存储器
#
# Args:
# mb: 内存池
@@ -322,7 +287,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
scan_result: IncludesScanner.ScanResult | t.CPtr,
filter_set: t.CChar | t.CPtr,
filter_count: int) -> int:
"""将 includes 扫描结果写入 _sha1_map.txt供 StubMerger 使用
"""将 includes 扫描结果写入 _sha1_map.txt人类可读输出,程序内部使用内存存储器
若 filter_set 不为 None 且 filter_count > 0只写入 filter_set 中的条目。
"""
@@ -367,6 +332,212 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str,
return 0
# ============================================================
# PopulateSha1MapStore - 从扫描结果填充全局 SHA1 映射存储器
#
# 遍历 ScanResult.Entries为每个条目构建 sha1→module_name 映射。
# 模块名通过 _PathToModuleName(includes/rel_path) 计算。
# 内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
#
# Args:
# scan_result: IncludesScanner 扫描结果
#
# Returns:
# 填充的条目数,-1 表示失败
# ============================================================
def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> int:
"""从扫描结果填充全局 SHA1 映射存储器
遍历 ScanResult.Entries为每个条目构建 sha1→module_name 映射。
模块名通过 _PathToModuleName(includes/rel_path) 计算。
内存用 stdlib.malloc 分配(全局存储器,不随 mbuddy 释放)。
"""
global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count
if scan_result is None:
return -1
# 释放旧存储器
if _sha1_store_arr is not None:
stdlib.free(_sha1_store_arr)
if _sha1_store_mod is not None:
stdlib.free(_sha1_store_mod)
if _sha1_store_rel is not None:
stdlib.free(_sha1_store_rel)
_sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17)
_sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64)
_sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN)
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
return -1
string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17)
string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64)
string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN)
_sha1_store_count = 0
entry_size_p: t.CSizeT = IncludesScanner.FileEntry.__sizeof__()
for i in range(scan_result.Count):
if _sha1_store_count >= MAX_INCLUDES:
break
ea: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_p
ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
if ent is None or ent.Sha1 is None or ent.RelPath is None:
continue
# 构建完整路径 includes/{RelPath}_PathToModuleName 需要)
rp_len: t.CSizeT = string.strlen(ent.RelPath)
full_path_buf: bytes = stdlib.malloc(rp_len + 10)
if full_path_buf is None:
continue
viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath)
mod_name: str = _PathToModuleName(full_path_buf)
stdlib.free(full_path_buf)
if mod_name is not None:
idx: t.CSizeT = t.CSizeT(_sha1_store_count) * 17
string.strcpy(_sha1_store_arr + idx, ent.Sha1)
idx2: t.CSizeT = t.CSizeT(_sha1_store_count) * 64
mn_len: t.CSizeT = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(_sha1_store_mod + idx2, mod_name)
else:
string.strncpy(_sha1_store_mod + idx2, mod_name, 63)
_sha1_store_mod[idx2 + 63] = '\0'
stdlib.free(mod_name)
# 保存 rel_path供 Phase2 3.5 编译缺失 includes 使用)
idx3: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN
if rp_len < MAX_REL_PATH_LEN:
string.strcpy(_sha1_store_rel + idx3, ent.RelPath)
else:
string.strncpy(_sha1_store_rel + idx3, ent.RelPath, MAX_REL_PATH_LEN - 1)
_sha1_store_rel[idx3 + MAX_REL_PATH_LEN - 1] = '\0'
_sha1_store_count += 1
return _sha1_store_count
# ============================================================
# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器
#
# Phase1 调用 PopulateSha1MapStore 填充 includes 文件,
# Phase2 扫描 App 源文件后调用此函数追加 App 模块条目,
# 确保编译 App 自身时跨模块 CDefine 查找能命中 App 模块
# (如 StubMerger.MAX_INCLUDES 在编译 Phase2 时通过
# _build_source_path_from_sha1 查找 StubMerger 的源路径)。
#
# 若存储器未初始化PopulateSha1MapStore 未调用),自动初始化。
# 若 SHA1 已存在(去重),跳过。
#
# Args:
# sha1: SHA1 字符串16 字符 + null
# rel_path: 相对路径(如 "lib/core/StubMerger.py"
# 不含 includes/ 前缀,与 _sha1_map.txt 格式一致)
#
# Returns:
# 0 成功含去重跳过1 失败
# ============================================================
def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
"""追加条目到全局 SHA1 映射存储器Phase2 用)"""
global _sha1_store_arr, _sha1_store_mod, _sha1_store_rel, _sha1_store_count
if sha1 is None or rel_path is None:
return 1
# 存储器未初始化自动初始化Phase2 可能先于 Phase1 调用)
if _sha1_store_arr is None:
_sha1_store_arr = stdlib.malloc(MAX_INCLUDES * 17)
_sha1_store_mod = stdlib.malloc(MAX_INCLUDES * 64)
_sha1_store_rel = stdlib.malloc(MAX_INCLUDES * MAX_REL_PATH_LEN)
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
return 1
string.memset(_sha1_store_arr, 0, MAX_INCLUDES * 17)
string.memset(_sha1_store_mod, 0, MAX_INCLUDES * 64)
string.memset(_sha1_store_rel, 0, MAX_INCLUDES * MAX_REL_PATH_LEN)
_sha1_store_count = 0
if _sha1_store_count >= MAX_INCLUDES:
return 1
# 去重:若 SHA1 已存在则跳过
for i in range(_sha1_store_count):
sidx_d: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(_sha1_store_arr + sidx_d, sha1) == 0:
return 0
# 模块名rel_path "lib/core/StubMerger.py" → "lib.core.StubMerger"
full_path_buf: bytes = stdlib.malloc(string.strlen(rel_path) + 10)
if full_path_buf is None:
return 1
viperlib.snprintf(full_path_buf, string.strlen(rel_path) + 10, "includes/%s", rel_path)
mod_name: str = _PathToModuleName(full_path_buf)
stdlib.free(full_path_buf)
if mod_name is None:
return 1
# 写入 SHA1
idx_a: t.CSizeT = t.CSizeT(_sha1_store_count) * 17
string.strcpy(_sha1_store_arr + idx_a, sha1)
# 写入模块名
idx_m: t.CSizeT = t.CSizeT(_sha1_store_count) * 64
mn_len: t.CSizeT = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(_sha1_store_mod + idx_m, mod_name)
else:
string.strncpy(_sha1_store_mod + idx_m, mod_name, 63)
_sha1_store_mod[idx_m + 63] = '\0'
stdlib.free(mod_name)
# 写入相对路径
idx_r: t.CSizeT = t.CSizeT(_sha1_store_count) * MAX_REL_PATH_LEN
rp_len: t.CSizeT = string.strlen(rel_path)
if rp_len < MAX_REL_PATH_LEN:
string.strcpy(_sha1_store_rel + idx_r, rel_path)
else:
string.strncpy(_sha1_store_rel + idx_r, rel_path, MAX_REL_PATH_LEN - 1)
_sha1_store_rel[idx_r + MAX_REL_PATH_LEN - 1] = '\0'
_sha1_store_count += 1
return 0
# ============================================================
# GetSha1StoreCount - 获取全局存储器条目数
# ============================================================
def GetSha1StoreCount() -> int:
"""返回全局 SHA1 存储器的条目数"""
return _sha1_store_count
def GetSha1StoreArrPtr() -> bytes | t.CPtr:
"""返回 SHA1 数组指针(每个条目 17 字节)"""
return _sha1_store_arr
def GetSha1StoreModArrPtr() -> bytes | t.CPtr:
"""返回模块名数组指针(每个条目 64 字节)"""
return _sha1_store_mod
def GetSha1StoreRelArrPtr() -> bytes | t.CPtr:
"""返回相对路径数组指针(每个条目 MAX_REL_PATH_LEN 字节)"""
return _sha1_store_rel
# ============================================================
# GetSha1StoreArr - 获取全局存储器数组指针(直接遍历用)
#
# 返回 sha1_arr、mod_arr、rel_arr 三个数组的指针和条目数。
# 调用者直接遍历数组sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*MAX_REL_PATH_LEN]
# 内存归全局存储器所有,调用者不要 free
# ============================================================
def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int:
"""获取全局存储器数组指针
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针,
函数将存储器数组指针写入这些位置。
返回条目数,-1 失败
"""
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None:
return -1
if _sha1_store_arr is None or _sha1_store_mod is None or _sha1_store_rel is None:
return -1
sp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_sha1_arr, t.CPtr))
if sp is not None:
sp[0] = _sha1_store_arr
mp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_mod_arr, t.CPtr))
if mp is not None:
mp[0] = _sha1_store_mod
rp: t.CPtr = (t.CPtr | t.CPtr)(t.CVoid(out_rel_arr, t.CPtr))
if rp is not None:
rp[0] = _sha1_store_rel
return _sha1_store_count
# ============================================================
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
#
@@ -499,6 +670,39 @@ def _CommentOutOpaqueTypeInBuf(out_buf: bytes, type_name_prefix: str) -> int:
return 0
# ============================================================
# _is_submodule_of_imported - 检查模块名是否是已导入包的子模块
#
# deps.txt 只记录直接导入的模块名(如 "ast"),但包的 __init__.py
# 内部 from .lexer import Lexer 会让 IR 引用 ast.lexer 子模块类型。
# 当模块名包含 '.' 时,检查其顶层包名是否在 deps_buf 中。
#
# 例如:
# deps_buf = "ast stdio string"
# mod_name = "ast.lexer" → 顶层包 "ast" 在 deps_buf 中 → 返回 1
# mod_name = "ast.token" → 顶层包 "ast" 在 deps_buf 中 → 返回 1
# mod_name = "foo.bar" → 顶层包 "foo" 不在 deps_buf 中 → 返回 0
# ============================================================
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
"""检查 mod_name 是否是 deps_buf 中已导入包的子模块"""
if deps_buf is None or mod_name is None:
return 0
# 查找第一个 '.',提取顶层包名
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
if dot_pos is None:
return 0
pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(mod_name))
if pkg_len == 0 or pkg_len >= 256:
return 0
# 构造顶层包名(截断到 '.' 之前)
# 直接在 mod_name 上临时截断检查,避免分配
saved_ch: t.CChar = mod_name[pkg_len]
mod_name[pkg_len] = '\0'
result: int = HandlesImports.is_module_imported(deps_buf, mod_name)
mod_name[pkg_len] = saved_ch
return result
# ============================================================
# _HasFullTypeDefinition - 检查 out_buf 中是否已有完整类型定义(非 opaque
#
@@ -806,7 +1010,70 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str,
in_body = 1
continue
# 跳过其他行global 带初值定义、type 定义等stub 已有 external 声明
# 提取类型定义(%... = type { ... } 或 %... = type < ... >
# stub.ll 中只有 opaque 声明,需要从 text.ll 中提取完整定义
# 用于 alloca 和 getelementptr 等需要完整类型的指令
if ll > 0 and dep_buf[ls] == '%':
eq_td: t.CSizeT = ls + 1
while eq_td < ls + ll:
if dep_buf[eq_td] == '=':
break
eq_td += 1
if eq_td < ls + ll:
# 检查是否为完整定义(= type { 或 = type <
dep_is_full_td: int = 0
if eq_td + 8 <= ls + ll:
if dep_buf[eq_td + 2] == 't' and dep_buf[eq_td + 3] == 'y' \
and dep_buf[eq_td + 4] == 'p' and dep_buf[eq_td + 5] == 'e' \
and dep_buf[eq_td + 6] == ' ':
if dep_buf[eq_td + 7] == '{' or dep_buf[eq_td + 7] == '<':
dep_is_full_td = 1
if dep_is_full_td == 1:
# 临时截断到 '=' 之前(不含 '='),辅助函数期望纯类型名前缀
saved_eq_td: t.CChar = dep_buf[eq_td]
dep_buf[eq_td] = '\0'
# 1. 注释掉 out_buf 中的 opaque 声明(如果有)
_CommentOutOpaqueTypeInBuf(out_buf, dep_buf + ls)
# 2. 检查 out_buf 中是否已有完整定义(避免重复)
has_full_td: int = _HasFullTypeDefinition(out_buf, dep_buf + ls)
dep_buf[eq_td] = saved_eq_td
if has_full_td == 0:
# 追加完整类型定义
if out_pos + ll + 2 < out_size:
string.strncpy(out_buf + out_pos, dep_buf + ls, ll)
out_pos += ll
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
continue
# 提取全局变量定义(@... = ...
# 依赖 text.ll 中的全局变量带初值定义(如 @_mbuddy = global ...
# stub.ll 中只有 external 声明,需要从 text.ll 提取完整定义
# 注释掉 out_buf 中的同名行external 声明或旧定义),追加 text.ll 中的完整定义
if ll > 0 and dep_buf[ls] == '@':
eq_gv: t.CSizeT = ls + 1
while eq_gv < ls + ll:
if dep_buf[eq_gv] == '=':
break
eq_gv += 1
if eq_gv < ls + ll:
# 截断到 '=' 之前(不含 '='),得到全局变量名 @name
saved_gv: t.CChar = dep_buf[eq_gv]
dep_buf[eq_gv] = '\0'
# 注释掉 out_buf 中的同名行external 声明或旧定义)
_CommentOutGlobalInBuf(out_buf, dep_buf + ls)
dep_buf[eq_gv] = saved_gv
# 追加完整定义
if out_pos + ll + 2 < out_size:
string.strncpy(out_buf + out_pos, dep_buf + ls, ll)
out_pos += ll
out_buf[out_pos] = '\n'
out_pos += 1
out_buf[out_pos] = '\0'
continue
# 跳过其他行
return out_pos
@@ -826,13 +1093,14 @@ def _PathToModuleName(path: str) -> str:
if path is None:
return None
plen: t.CSizeT = string.strlen(path)
# 跳过 includes/ 前缀
prefix: str = "includes/"
# 跳过 includes/ 或 includes\ 前缀Windows 反斜杠兼容)
prefix_len: t.CSizeT = 9
path_start: t.CSizeT = 0
if plen > prefix_len and string.strncmp(path, prefix, prefix_len) == 0:
path_start = prefix_len
plen = plen - prefix_len
if plen > prefix_len and string.strncmp(path, "includes", 8) == 0:
psep_ch: t.CChar = path[8]
if psep_ch == '/' or psep_ch == '\\':
path_start = prefix_len
plen = plen - prefix_len
# 去掉 .py 后缀
if plen > 3 and path[path_start + plen - 3] == '.' and path[path_start + plen - 2] == 'p' and path[path_start + plen - 1] == 'y':
plen -= 3
@@ -869,94 +1137,33 @@ def _PathToModuleName(path: str) -> str:
# ============================================================
# _BuildIncludesSha1Map - 从 _sha1_map.txt 构建 sha1 → module_name 映射
# _BuildIncludesSha1Map - 从全局内存存储器构建 sha1 → module_name 映射
#
# _sha1_map.txt 格式: {sha1}:includes/{rel_path}\n
# 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt
# 返回两个数组sha1_list 和 mod_list长度存储在 count 中。
# 所有内存由 stdlib.malloc 分配,调用者负责释放。
# ============================================================
def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
sha1_list: t.CChar | t.CPtr,
mod_list: t.CChar | t.CPtr) -> int:
""" _sha1_map.txt 构建 sha1→module_name 映射,返回条目数"""
if temp_dir is None or sha1_list is None or mod_list is None:
return 0
"""全局内存存储器复制 sha1→module_name 映射到调用者数组
map_path: bytes = stdlib.malloc(td_len + 32)
if map_path is None:
temp_dir/td_len 参数保留以兼容现有调用者,但不再使用。
数据来源是 PopulateSha1MapStore 填充的全局存储器。
"""
if sha1_list is None or mod_list is None:
return 0
viperlib.snprintf(map_path, td_len + 32, "%s/_sha1_map.txt", temp_dir)
mf: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.R)
if mf.closed:
stdlib.free(map_path)
if _sha1_store_arr is None or _sha1_store_mod is None:
return 0
map_buf: bytes = stdlib.malloc(STUB_READ_BUF_SIZE)
if map_buf is None:
mf.close()
stdlib.free(map_path)
return 0
map_br: t.CInt64T = mf.read_all(map_buf, STUB_READ_BUF_SIZE)
mf.close()
stdlib.free(map_path)
if map_br <= 0:
stdlib.free(map_buf)
return 0
if map_br < STUB_READ_BUF_SIZE:
map_buf[map_br] = '\0'
else:
map_buf[STUB_READ_BUF_SIZE - 1] = '\0'
count: int = 0
pos: t.CSizeT = 0
# 在循环外分配 sha1 缓冲区,避免反复 malloc/free 导致堆碎片化
sha1: t.CChar | t.CPtr = stdlib.malloc(17)
if sha1 is None:
stdlib.free(map_buf)
return 0
while pos < map_br and count < MAX_INCLUDES:
# 提取 SHA116 hex
string.strncpy(sha1, map_buf + pos, 16)
sha1[16] = '\0'
pos += 16
# 跳过 ':'
if pos < map_br and map_buf[pos] == ':':
pos += 1
# 提取路径(到 \r、\n 或 \0
# 注意: Windows CRLF 换行符是 \r\n必须同时检查 \r 避免路径包含 \r
path_start: t.CSizeT = pos
while pos < map_br and map_buf[pos] != '\n' and map_buf[pos] != '\r' and map_buf[pos] != '\0':
pos += 1
path_len: t.CSizeT = pos - path_start
# 跳过 \r\n 或 \n
if pos < map_br and map_buf[pos] == '\r':
pos += 1
if pos < map_br and map_buf[pos] == '\n':
pos += 1
if path_len > 0:
# 仅收集 includes/ 前缀的条目(用户文件如 App/ 不应进入 includes 映射)
# 否则用户文件会被误判为 includes导致 is_module_imported 模块名不匹配而跳过加载
if path_len >= 9 and string.strncmp(map_buf + path_start, "includes/", 9) == 0:
path_buf: t.CChar | t.CPtr = stdlib.malloc(path_len + 1)
if path_buf is not None:
string.strncpy(path_buf, map_buf + path_start, path_len)
path_buf[path_len] = '\0'
mod_name: str = _PathToModuleName(path_buf)
stdlib.free(path_buf)
if mod_name is not None:
# 存储 sha1 和 mod_name 到数组中
idx: t.CSizeT = t.CSizeT(count) * 17
string.strcpy(sha1_list + idx, sha1)
idx2: t.CSizeT = t.CSizeT(count) * 64
mn_len: t.CSizeT = string.strlen(mod_name)
if mn_len < 64:
string.strcpy(mod_list + idx2, mod_name)
else:
string.strncpy(mod_list + idx2, mod_name, 63)
mod_list[idx2 + 63] = '\0'
stdlib.free(mod_name)
count += 1
stdlib.free(sha1)
stdlib.free(map_buf)
for i in range(_sha1_store_count):
if count >= MAX_INCLUDES:
break
idx: t.CSizeT = t.CSizeT(i) * 17
idx2: t.CSizeT = t.CSizeT(i) * 64
string.strcpy(sha1_list + t.CSizeT(count) * 17, _sha1_store_arr + idx)
string.strcpy(mod_list + t.CSizeT(count) * 64, _sha1_store_mod + idx2)
count += 1
return count
@@ -977,16 +1184,33 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: t.CSizeT,
# ============================================================
def _FindSha1ByModName(sha1_arr: t.CChar | t.CPtr, mod_arr: t.CChar | t.CPtr,
count: int, mod_name: str) -> str:
"""从模块名查找 SHA1精确匹配"""
"""从模块名查找 SHA1精确匹配 + 包名回退)
精确匹配失败时,尝试 "{mod_name}.__init__" 回退,
支持 import ast→ ast.__init__ 的 SHA1 查找。
"""
if sha1_arr is None or mod_arr is None or mod_name is None:
return None
if count <= 0:
return None
# 第一遍:精确匹配
for i in range(count):
idx: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(mod_arr + idx, mod_name) == 0:
sidx: t.CSizeT = t.CSizeT(i) * 17
return sha1_arr + sidx
# 第二遍:包名回退("ast" → "ast.__init__"
nm_len: t.CSizeT = string.strlen(mod_name)
init_buf: bytes = stdlib.malloc(nm_len + 12)
if init_buf is not None:
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
for i in range(count):
idx2: t.CSizeT = t.CSizeT(i) * 64
if string.strcmp(mod_arr + idx2, init_buf) == 0:
sidx2: t.CSizeT = t.CSizeT(i) * 17
stdlib.free(init_buf)
return sha1_arr + sidx2
stdlib.free(init_buf)
return None
@@ -1027,13 +1251,13 @@ def _AddSha1ToSet(set_buf: t.CChar | t.CPtr, count: int, sha1: str) -> int:
#
# 算法(工作列表):
# 1. 扫描 source_dir 下的 .py 文件,解析 AST 获取 _imported_modules
# 2. 构建 SHA1→module_name 映射(从 _sha1_map.txt
# 2. 构建 SHA1→module_name 映射(从全局内存存储器
# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名
#
# Args:
# mb: 内存池
# source_dir: 源文件目录Config.SourceDir
# temp_dir: 临时目录(_sha1_map.txt 和 .deps.txt 所在位置)
# temp_dir: 临时目录(.deps.txt 所在位置_sha1_map.txt 仅人类可读输出,不读取
# reachable_set: 输出参数,可达 SHA1 集合缓冲区MAX_INCLUDES_SHA1 * 17 字节)
#
# Returns:
@@ -1060,11 +1284,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
return -1
map_count: int = _BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr)
if map_count <= 0:
stdio.printf("[Reachable] 无法构建 SHA1 映射\n")
VLogger.error("无法构建 SHA1 映射", "Reachable")
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
stdio.printf("[Reachable] SHA1 映射: %d\n", map_count)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "SHA1 映射: %d", map_count)
VLogger.debug(fb, "Reachable")
# 2. 扫描 source_dir 下的 .py 文件,收集直接依赖
dir_len: t.CSizeT = string.strlen(source_dir)
@@ -1099,7 +1326,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
if handle == win32base.INVALID_HANDLE_VALUE:
stdio.printf("[Reachable] 未找到 .py 文件: %s\n", pattern)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "未找到 .py 文件: %s", pattern)
VLogger.warning(fb, "Reachable")
stdlib.free(pattern)
stdlib.free(find_data)
stdlib.free(sha1_arr)
@@ -1176,7 +1406,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
worklist[wl_len - 1] = '\0'
wl_len -= 1
stdio.printf("[Reachable] 源文件直接依赖: '%s'\n", worklist)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "源文件直接依赖: '%s'", worklist)
VLogger.debug(fb, "Reachable")
# 2.5. 全局预加入基础容器模块_list/_dict/json
#
@@ -1223,7 +1456,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
stdio.printf("[Reachable] 预加入容器模块后: '%s'\n", worklist)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "预加入容器模块后: '%s'", worklist)
VLogger.debug(fb, "Reachable")
# 3. 工作列表算法:递归收集可达 SHA1
processed: bytes = stdlib.malloc(8192)
@@ -1261,13 +1497,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
if HandlesImports.is_module_imported(processed, mod_buf) != 0:
already = 1
if already == 0:
# 加入 processed
if proc_len + name_len + 1 < 8192:
string.strcpy(processed + proc_len, mod_buf)
proc_len += name_len
processed[proc_len] = ' '
proc_len += 1
processed[proc_len] = '\0'
# 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug
if processed is not None:
if proc_len + name_len + 1 < 8192:
string.strcpy(processed + proc_len, mod_buf)
proc_len = proc_len + name_len
processed[proc_len] = ' '
proc_len = proc_len + 1
processed[proc_len] = '\0'
# 查找 SHA1
found_sha1: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf)
@@ -1289,17 +1526,18 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
deps_buf[dbr] = '\0'
else:
deps_buf[2047] = '\0'
# 追加到 worklist确保前面有空格分隔符,避免模块名合并
# 追加到 worklist嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug
dl: t.CSizeT = string.strlen(deps_buf)
if dl > 0 and wl_len + dl + 2 < 8192:
if wl_len > 0 and worklist[wl_len - 1] != ' ':
if worklist is not None:
if dl > 0 and wl_len + dl + 2 < 8192:
if wl_len > 0 and worklist[wl_len - 1] != ' ':
worklist[wl_len] = ' '
wl_len = wl_len + 1
string.strcpy(worklist + wl_len, deps_buf)
wl_len = wl_len + dl
worklist[wl_len] = ' '
wl_len += 1
string.strcpy(worklist + wl_len, deps_buf)
wl_len += dl
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
wl_len = wl_len + 1
worklist[wl_len] = '\0'
stdlib.free(deps_buf)
else:
df.close()
@@ -1310,15 +1548,19 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
stdlib.free(worklist)
stdlib.free(processed)
stdio.printf("[Reachable] 可达 SHA1: %d\n", reachable_count)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "可达 SHA1: %d", reachable_count)
VLogger.debug(fb, "Reachable")
return reachable_count
# ============================================================
# BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR
#
# 按需加载:读取 deps.txt 获取导入模块名,通过 _sha1_map.txt 查找 SHA1
# 按需加载:读取 deps.txt 获取导入模块名,通过内存存储器查找 SHA1
# 仅加载实际使用的依赖 stub而非全部扫描
# _sha1_map.txt 仅作为人类可读输出,程序内部不读取。
# ============================================================
def BuildCombinedIR(temp_dir: str, local_sha1: str,
out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT:
@@ -1387,7 +1629,7 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
# 2. 按需加载依赖 stubs根据 deps.txt 过滤,而非扫描全部)
# 先加载依赖 stub含 type 定义),确保 type 定义在本地 text.ll 的 define 块之前
# 2a. 构建 includes SHA1 → module_name 映射(从 _sha1_map.txt
# 2a. 构建 includes SHA1 → module_name 映射(从全局内存存储器
sha1_arr: bytes = stdlib.malloc(MAX_INCLUDES * 17)
mod_arr: bytes = stdlib.malloc(MAX_INCLUDES * 64)
inc_count: int = 0
@@ -1436,8 +1678,17 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
is_include = 1
if deps_loaded != 0:
idx_mi: t.CSizeT = t.CSizeT(ii) * 64
if HandlesImports.is_module_imported(deps_buf, mod_arr + idx_mi) != 0:
dep_mod_name: str = mod_arr + idx_mi
if HandlesImports.is_module_imported(deps_buf, dep_mod_name) != 0:
should_load = 1
else:
# 传递性依赖deps.txt 只记录直接导入(如 ast
# 但包的 __init__.py 内部 from .lexer import Lexer
# 会让 IR 引用 ast.lexer 子模块类型。
# 检查 dep_mod_name 是否是已导入包的子模块
# (如 ast.lexer 是 ast 的子模块)
if _is_submodule_of_imported(deps_buf, dep_mod_name) != 0:
should_load = 1
break
if is_include == 0:
# 非 includes stub用户文件总是加载
@@ -1492,7 +1743,10 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
if chk_path is not None:
chk_f: fileio.File | t.CPtr = fileio.File(chk_path, fileio.MODE.R)
if chk_f.closed:
stdio.printf("[FATAL][BuildCombinedIR] 依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译\n", mod_nm, found_s, chk_path)
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "依赖模块 '%s' (sha1=%s) 的 stub 文件不存在: %s,立即终止编译", mod_nm, found_s, chk_path)
VLogger.error(fb, "BuildCombinedIR")
sys.exit(1)
chk_f.close()
stdlib.free(chk_path)

View File

@@ -1,14 +1,29 @@
import t, c
from stdint import *
import stdio
import stdlib
import memhub
import sys
import w32.win32console as w32cmd
import w32.win32file as w32file
import w32.win32base as w32base
import w32.win32base as win32base
# ============================================================
# VLogger - 原生日志系统Windows 控制台彩色输出)
#
# 输出格式与 TPC (lib/core/VLogger.py) 对齐:
# ├ INFO: message
# ├ INFO[category]: message
# ├ WARN: message
# ├ ERROR: message
# ├ SUCCESS: message
# ├ DEBUG: message
# ╠ CRITICAL: message (红底白字,致命错误)
# ╠ CRITICAL[category]: message
#
# 分段着色:前缀符号(├/╠) 用级别色LEVEL 标签用亮色,消息用默认色
# CRITICAL 级别使用 ╠ 前缀和红底白字标签,与 TPC critical 对齐
# ============================================================
# 日志级别
@@ -18,6 +33,7 @@ class LogLevel(t.CEnum):
WARNING = 2
ERROR = 3
SUCCESS = 4
CRITICAL = 5
# Win32 控制台前景色属性
@@ -60,42 +76,72 @@ class Logger:
return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
return 0
def _log(self, level: int, prefix: str, msg: str, color: WORD,
# ============================================================
# _log - 核心日志输出(分段着色)
#
# 格式: ├ LEVEL: msg 或 ├ LEVEL[category]: msg
# 分三段着色:
# 1. 前缀符号 ├ (sym_color)
# 2. LEVEL 标签 (level_color)
# 3. 消息内容 (默认白色)
# ============================================================
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str,
sym_color: WORD, level_color: WORD,
category: str = "") -> int:
if level < self._level:
return 0
self._set_color(color)
# 前缀符号(如 ├)
self._set_color(sym_color)
stdio.printf("%s ", prefix_sym)
# LEVEL 标签(亮色)
self._set_color(level_color)
if category is not None:
if category[0] != 0:
stdio.printf("%s[%s] %s\n", prefix, category, msg)
stdio.printf("%s[%s]: ", level_str, category)
else:
stdio.printf("%s %s\n", prefix, msg)
stdio.printf("%s: ", level_str)
else:
stdio.printf("%s %s\n", prefix, msg)
stdio.printf("%s: ", level_str)
# 消息内容(默认色)
self._reset_color()
stdio.printf("%s\n", msg)
return 0
def debug(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.DEBUG, "[DEBUG]", msg,
FOREGROUND_INTENSITY, category)
return self._log(LogLevel.DEBUG, "", "DEBUG", msg,
FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
def info(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.INFO, "[INFO]", msg,
return self._log(LogLevel.INFO, "", "INFO", msg,
FOREGROUND_GREEN,
FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
def warning(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.WARNING, "[WARN]", msg,
return self._log(LogLevel.WARNING, "", "WARN", msg,
FOREGROUND_YELLOW,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
category)
def error(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.ERROR, "[ERROR]", msg,
FOREGROUND_RED | FOREGROUND_INTENSITY, category)
ret: int = self._log(LogLevel.ERROR, "", "ERROR", msg,
FOREGROUND_RED,
FOREGROUND_RED | FOREGROUND_INTENSITY, category)
# 错误立即终止进程(避免错误后继续执行导致连锁崩溃)
sys.exit(1)
return ret
def success(self, msg: str, category: str = "") -> int:
return self._log(LogLevel.SUCCESS, "[SUCCESS]", msg,
return self._log(LogLevel.SUCCESS, "", "SUCCESS", msg,
FOREGROUND_GREEN,
FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
def critical(self, msg: str, category: str = "") -> int:
# CRITICAL 使用 ╠ 前缀(红底白字标签,对齐 TPC critical 风格)
return self._log(LogLevel.CRITICAL, "", "CRITICAL", msg,
FOREGROUND_RED,
BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY,
category)
def set_level(self, level: int) -> int:
self._level = level
return 0
@@ -105,8 +151,8 @@ class Logger:
# ============================================================
def banner(self, msg: str) -> int:
"""输出分节标题(色高亮)。"""
self._set_color(FOREGROUND_CYAN | FOREGROUND_INTENSITY)
"""输出分节标题(色高亮,与 TPC banner 风格对齐)。"""
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
stdio.printf("\n=== %s ===\n\n", msg)
self._reset_color()
return 0
@@ -148,9 +194,28 @@ _mbuddy: memhub.MemManager | t.CPtr
# 全局 logger 指针
_g_logger: Logger | t.CPtr
# 全局格式化缓冲区用于带变量的日志消息1024 字节)
_fmt_buf: t.CChar | t.CPtr
def fmt_buf() -> t.CChar | t.CPtr:
"""获取全局格式化缓冲区1024 字节)。
用法:
buf: t.CChar | t.CPtr = VLogger.fmt_buf()
if buf is not None:
viperlib.snprintf(buf, 1024, "翻译: %s (sha1=%s)", rp, sha1)
log.info(buf, "Phase1")
"""
global _fmt_buf
if _fmt_buf is None:
_fmt_buf = stdlib.malloc(1024)
return _fmt_buf
def get_logger() -> Logger | t.CPtr:
"""获取全局 logger 实例。若不存在则通过 _mbuddy 分配并初始化。"""
global _g_logger
if _g_logger is None:
if _mbuddy is None:
return None
@@ -167,5 +232,63 @@ def get_logger() -> Logger | t.CPtr:
def set_logger(logger: Logger | t.CPtr) -> int:
"""设置全局 logger 实例。"""
global _g_logger
_g_logger = logger
return 0
# ============================================================
# 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐)
#
# 调用方式: VLogger.info("msg", "category")
# 无需先 get_logger(),方便各模块直接使用
# ============================================================
def info(msg: str, category: str = "") -> int:
"""输出 INFO 级别日志。"""
log: Logger | t.CPtr = get_logger()
if log is not None:
return log.info(msg, category)
return 0
def warning(msg: str, category: str = "") -> int:
"""输出 WARN 级别日志。"""
log: Logger | t.CPtr = get_logger()
if log is not None:
return log.warning(msg, category)
return 0
def error(msg: str, category: str = "") -> int:
"""输出 ERROR 级别日志并立即终止进程。"""
log: Logger | t.CPtr = get_logger()
if log is not None:
return log.error(msg, category)
# logger 不可用时也直接退出
sys.exit(1)
return 0
def success(msg: str, category: str = "") -> int:
"""输出 SUCCESS 级别日志。"""
log: Logger | t.CPtr = get_logger()
if log is not None:
return log.success(msg, category)
return 0
def critical(msg: str, category: str = "") -> int:
"""输出 CRITICAL 级别日志(致命错误,红底白字)。"""
log: Logger | t.CPtr = get_logger()
if log is not None:
return log.critical(msg, category)
return 0
def debug(msg: str, category: str = "") -> int:
"""输出 DEBUG 级别日志。"""
log: Logger | t.CPtr = get_logger()
if log is not None:
return log.debug(msg, category)
return 0

View File

@@ -16,6 +16,7 @@ import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HandlesTranslator
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesType as HandlesType
import lib.core.BuildPipeline as BuildPipeline
import lib.core.IncludesScanner as IncludesScanner
import lib.core.StubMerger as StubMerger
@@ -31,8 +32,9 @@ import hashlib
# 内存大小: fl_bytes=264 (33*8), POOL_SIZE-fl_bytes 必须是 2 的幂以避免浪费
# 1073742088 - 264 = 1073741824 (1024MB usable = 2^30)
# 注意: POOL_SIZE 必须是 2^30+264=1073742088否则 _largest_pow2_le 会取 2^29=512MB
# Phase1 翻译 87 个 includes 文件 + Phase2 翻译 30 个测试文件512MB 不足导致 Phase2 崩溃
POOL_SIZE: t.CDefine = 1073741824
POOL_SIZE: t.CDefine = 1073742088
# 语言编码页
CODE_PAGE: t.CDefine = 65001
# 源代码缓冲区大小1MB
@@ -47,18 +49,33 @@ SRC_BUF_SIZE: t.CDefine = 1048576
def main() -> int:
w32cmd.SetConsoleOutputCP(CODE_PAGE)
w32cmd.SetConsoleCP(CODE_PAGE)
# 用 fflush(0) 刷新所有流确保崩溃前输出可见0=NULL 刷新所有输出流)
stdio.printf("[DBG] main enter\n")
stdio.fflush(0)
# 初始化 mbuddy 内存池
stdio.printf("[DBG] before malloc\n")
stdio.fflush(0)
arena: bytes = stdlib.malloc(POOL_SIZE)
stdio.printf("[DBG] after malloc arena=%p\n", arena)
stdio.fflush(0)
if arena is None:
stdio.printf("FAIL: malloc for arena failed\n")
return 1
stdio.printf("[DBG] before MemBuddy\n")
stdio.fflush(0)
mb: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(arena, POOL_SIZE)
stdio.printf("[DBG] after MemBuddy mb=%p\n", mb)
stdio.fflush(0)
if mb is None:
stdio.printf("FAIL: MemBuddy init failed\n")
return 1
# 设置全局 mbuddy 指针sys 和 argparse 都需要)
stdio.printf("[DBG] before _mbuddy setup\n")
stdio.fflush(0)
sys._mbuddy = mb
stdio.printf("[DBG] sys._mbuddy ok\n")
stdio.fflush(0)
argparse._mbuddy = mb
ast._mbuddy = mb
lib._mbuddy = (memhub.MemManager | t.CPtr)(mb)
@@ -72,44 +89,87 @@ def main() -> int:
Phase2._mbuddy = (memhub.MemManager | t.CPtr)(mb)
subprocess._mbuddy = mb
hashlib._mbuddy = (memhub.MemManager | t.CPtr)(mb)
VLogger._mbuddy = (memhub.MemManager | t.CPtr)(mb)
stdio.printf("[DBG] before InitLib\n")
stdio.fflush(0)
lib.InitLib((memhub.MemManager | t.CPtr)(mb))
stdio.printf("[DBG] after InitLib\n")
stdio.fflush(0)
# 初始化 VLogger 并打印启动日志
log: VLogger.Logger | t.CPtr = VLogger.get_logger()
stdio.printf("[DBG] after get_logger log=%p\n", log)
stdio.fflush(0)
if log is not None:
log.info("TransPyV 启动")
stdio.printf("[DBG] after log.info\n")
stdio.fflush(0)
# 初始化命令行参数Windows: GetCommandLineA, POSIX: /proc/self/cmdline
stdio.printf("[DBG] before _init_argv\n")
stdio.fflush(0)
sys._init_argv()
stdio.printf("[DBG] after _init_argv\n")
stdio.fflush(0)
# 创建参数解析器
stdio.printf("[DBG] before ArgumentParser\n")
stdio.fflush(0)
parser: argparse.ArgumentParser | t.CPtr = argparse.ArgumentParser(
"TransPyV", "TransPyV 命令行参数解析", pool=mb)
stdio.printf("[DBG] after ArgumentParser parser=%p\n", parser)
stdio.fflush(0)
# 注册参数(与 Projectrans.py main() 一致)
stdio.printf("[DBG] before add_argument --project\n")
stdio.fflush(0)
parser.add_argument("--project", None, argparse.STRING, 0, None, False,
argparse.STORE, "project.json 路径(默认查找当前目录)")
stdio.printf("[DBG] after add_argument --project\n")
stdio.fflush(0)
parser.add_argument("--src", None, argparse.STRING, 0, None, False,
argparse.STORE, "源文件目录(覆盖 project.json")
stdio.printf("[DBG] after --src\n")
stdio.fflush(0)
parser.add_argument("--temp", None, argparse.STRING, 0, None, False,
argparse.STORE, "声明接口临时目录(覆盖 project.json")
stdio.printf("[DBG] after --temp\n")
stdio.fflush(0)
parser.add_argument("--output", None, argparse.STRING, 0, None, False,
argparse.STORE, "输出目录(覆盖 project.json")
stdio.printf("[DBG] after --output\n")
stdio.fflush(0)
parser.add_argument("--phase", None, argparse.STRING, 0, None, False,
argparse.STORE, "阶段: 1=生成声明, 2=翻译+编译, all=全部")
stdio.printf("[DBG] after --phase\n")
stdio.fflush(0)
parser.add_argument("--cc", None, argparse.STRING, 0, None, False,
argparse.STORE, "LLVM 编译器命令(覆盖 project.json")
stdio.printf("[DBG] after --cc\n")
stdio.fflush(0)
parser.add_argument("--clean", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "清理 output 和 temp 目录")
stdio.printf("[DBG] after --clean\n")
stdio.fflush(0)
parser.add_argument("--run", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "编译成功后立即执行生成的可执行文件")
stdio.printf("[DBG] after --run\n")
stdio.fflush(0)
parser.add_argument("--rebuild-includes", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "删除 includes.binary 预编译缓存并重新编译所有 includes")
stdio.printf("[DBG] after --rebuild-includes\n")
stdio.fflush(0)
parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False,
argparse.STORE_TRUE, "清除 .transpyc_cache 全局缓存")
stdio.printf("[DBG] after --clear-cache\n")
stdio.fflush(0)
# 解析命令行参数
stdio.printf("[DBG] before parse_args\n")
stdio.fflush(0)
args: argparse.ParsedArgs | t.CPtr = parser.parse_args(sys._argc, sys._argv)
stdio.printf("[DBG] after parse_args args=%p\n", args)
stdio.fflush(0)
if args is None:
if log is not None:
log.error("参数解析失败", "argparse")
@@ -184,12 +244,21 @@ def main() -> int:
proj_loaded: int = 0
proj_path: str = project
if proj_path is not None:
stdio.printf("[DBG] before Load_project_config path=%s\n", proj_path)
stdio.fflush(0)
if log is not None:
log.banner("工程配置")
if Config.Load_project_config(proj_path) == 0:
stdio.printf("[DBG] Load_project_config OK\n")
stdio.fflush(0)
proj_loaded = 1
else:
stdio.printf(" 警告: 无法加载 project.vpj: %s\n", proj_path)
if log is not None:
warn_buf: str = stdlib.malloc(256)
if warn_buf is not None:
viperlib.snprintf(warn_buf, 256, "无法加载 project.vpj: %s", proj_path)
log.warning(warn_buf, "config")
stdlib.free(warn_buf)
else:
# 尝试默认路径 project.vpj当前目录和上级目录
proj_path = "project.vpj"
@@ -215,18 +284,33 @@ def main() -> int:
if proj_dir is not None:
string.memcpy(proj_dir, proj_path, slash_pos)
proj_dir[slash_pos] = '\0'
stdio.printf("[DBG] before resolve_paths dir=%s\n", proj_dir)
stdio.fflush(0)
Config.resolve_paths(proj_dir)
stdio.printf("[DBG] resolve_paths OK\n")
stdio.fflush(0)
Config.print_config()
stdio.printf("[DBG] print_config OK\n")
stdio.fflush(0)
# === --clean: 清理 temp 和 output 目录 ===
if _gb_clean:
stdio.printf("\n[clean] 清理临时目录...\n")
if log is not None:
log.info("清理临时目录...", "clean")
if Config.TempDir is not None:
n: int = Utils.CleanDir(Config.TempDir)
stdio.printf("[clean] %s: 删除 %d 个文件\n", Config.TempDir, n)
if log is not None:
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None:
viperlib.snprintf(fb, 1024, "%s: 删除 %d 个文件", Config.TempDir, n)
log.info(fb, "clean")
if Config.OutputDir is not None:
n2: int = Utils.CleanDir(Config.OutputDir)
stdio.printf("[clean] %s: 删除 %d 个文件\n", Config.OutputDir, n2)
if log is not None:
fb2: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb2 is not None:
viperlib.snprintf(fb2, 1024, "%s: 删除 %d 个文件", Config.OutputDir, n2)
log.info(fb2, "clean")
# === --phase 参数控制 ===
# phase=1: 仅 stub 分离(生成 .stub.ll/.text.ll不编译
@@ -241,7 +325,11 @@ def main() -> int:
do_phase1 = 1
if phase_mode == "2" or phase_mode == "all":
do_phase2 = 1
stdio.printf("[phase] 模式: %s (phase1=%d phase2=%d)\n", phase_mode, do_phase1, do_phase2)
if log is not None:
fb3: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb3 is not None:
viperlib.snprintf(fb3, 1024, "模式: %s (phase1=%d phase2=%d)", phase_mode, do_phase1, do_phase2)
log.info(fb3, "phase")
# === Phase1: 扫描 includes按需翻译===
# 对 includes 目录中每个 .py 文件,若 stub 不存在则翻译并分离 stub
@@ -249,10 +337,15 @@ def main() -> int:
ph1_temp: str = Config.TempDir
if ph1_temp is None:
ph1_temp = "."
stdio.printf("[DBG] before Phase1.RunPhase1 inc=%s temp=%s\n", Config.IncludesDir, ph1_temp)
stdio.fflush(0)
Phase1.RunPhase1(mb, Config.IncludesDir, ph1_temp, log)
stdio.printf("[DBG] Phase1.RunPhase1 OK\n")
stdio.fflush(0)
# 如果仅 Phase1不执行 Phase2直接退出
if do_phase2 == 0:
stdio.printf("[phase] Phase1 完成,退出\n")
if log is not None:
log.info("Phase1 完成,退出", "phase")
return 0
# AST 解析:如果指定了 --src 或 --project读取文件并解析为 AST 树
@@ -270,10 +363,14 @@ def main() -> int:
mf_linker_out: str = Config.LinkerOutput if Config.LinkerOutput is not None else "app.exe"
mf_includes_bin: str = Config.get_includes_binary_dir()
stdio.printf("[DBG] before Phase2.RunMultiFileProject src=%s temp=%s out=%s\n", Config.SourceDir, mf_temp, mf_output)
stdio.fflush(0)
mf_ret: int = Phase2.RunMultiFileProject(
mb, Config.SourceDir, mf_temp, mf_output,
mf_cc, mf_cc_flags, mf_linker, mf_linker_flags, mf_linker_out,
mf_includes_bin, Config.IncludesDir, do_phase1, do_phase2, log, args)
stdio.printf("[DBG] Phase2.RunMultiFileProject OK ret=%d\n", mf_ret)
stdio.fflush(0)
argparse.release(args)
if log is not None:
log.success("TransPyV 完成")
@@ -288,7 +385,11 @@ def main() -> int:
if path_buf is not None:
viperlib.snprintf(path_buf, sd_len + 16, "%s/main.py", Config.SourceDir)
src_path = path_buf
stdio.printf("[project] 入口文件: %s\n", src_path)
if log is not None:
fb4: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb4 is not None:
viperlib.snprintf(fb4, 1024, "入口文件: %s", src_path)
log.info(fb4, "project")
if src_path is not None:
if log is not None:
log.banner("AST 解析")
@@ -296,8 +397,10 @@ def main() -> int:
f: fileio.File | t.CPtr = fileio.File(src_path, fileio.MODE.R)
if f.closed:
if log is not None:
log.error("无法打开文件", "fileio")
stdio.printf(" 路径: %s\n", src_path)
fb5: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb5 is not None:
viperlib.snprintf(fb5, 1024, "无法打开文件: %s", src_path)
log.error(fb5, "fileio")
argparse.release(args)
return 1
# 分配源代码缓冲区
@@ -313,8 +416,10 @@ def main() -> int:
f.close()
if bytes_read < 0:
if log is not None:
log.error("读取文件失败", "fileio")
stdio.printf(" 错误码: %d\n", bytes_read)
fb6: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb6 is not None:
viperlib.snprintf(fb6, 1024, "读取文件失败 (错误码: %d)", bytes_read)
log.error(fb6, "fileio")
argparse.release(args)
return 1
# 添加 null 终止符,确保 strlen 和 SHA1 计算正确
@@ -324,7 +429,10 @@ def main() -> int:
src_buf[SRC_BUF_SIZE - 1] = 0
if log is not None:
log.info("文件读取完成", "fileio")
stdio.printf("读取 %d 字节\n", bytes_read)
fb7: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb7 is not None:
viperlib.snprintf(fb7, 1024, "读取 %d 字节", bytes_read)
log.info(fb7, "fileio")
# 解析 AST拆分为词法+语法两阶段,便于调试)
ast._init_tables(mb)
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
@@ -396,6 +504,8 @@ def main() -> int:
# 使用 project.vpj 中的配置
temp_dir: str = Config.TempDir if Config.TempDir is not None else "."
output_dir: str = Config.OutputDir if Config.OutputDir is not None else "."
# 设置全局 temp_dir供跨模块 CDefine 查找使用)
HandlesType.set_temp_dir(temp_dir)
cc_cmd: str = Config.CompilerCmd if Config.CompilerCmd is not None else "llc"
cc_flags: str = "-filetype=obj -relocation-model=pic"
linker_cmd: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++"
@@ -468,7 +578,8 @@ def main() -> int:
final_ir = combined_buf
final_ir_len = combined_len
else:
stdio.printf("[stub] BuildCombinedIR 失败,使用原始 IR\n")
if log is not None:
log.warning("BuildCombinedIR 失败,使用原始 IR", "stub")
stdlib.free(combined_buf)
br: BuildPipeline.BuildResult | t.CPtr = BuildPipeline.run_pipeline(
@@ -479,21 +590,35 @@ def main() -> int:
if br is not None and br.Success == 1:
if log is not None:
log.success("编译管线完成")
stdio.printf("输出: %s/%s\n", output_dir, linker_output)
fb8: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb8 is not None:
viperlib.snprintf(fb8, 1024, "输出: %s/%s", output_dir, linker_output)
log.info(fb8)
# --run 模式:执行生成的可执行文件
if args.get_bool("run"):
exe_path: bytes = stdlib.malloc(string.strlen(output_dir) + string.strlen(linker_output) + 2)
if exe_path is not None:
viperlib.snprintf(exe_path, 256, "%s/%s", output_dir, linker_output)
stdio.printf("[run] 执行: %s\n", exe_path)
if log is not None:
fb9: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb9 is not None:
viperlib.snprintf(fb9, 1024, "执行: %s", exe_path)
log.info(fb9, "run")
r: subprocess.CompletedProcess | t.CPtr = subprocess.run(exe_path, False, False)
if r is not None:
stdio.printf("[run] 退出码: %d\n", r.returncode)
if log is not None:
fb10: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb10 is not None:
viperlib.snprintf(fb10, 1024, "退出码: %d", r.returncode)
log.info(fb10, "run")
else:
if log is not None:
log.error("编译管线失败", "pipeline")
if br is not None and br.ErrorMsg is not None:
stdio.printf("[FATAL] 编译管线失败: %s\n", br.ErrorMsg)
if br is not None and br.ErrorMsg is not None:
fb11: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb11 is not None:
viperlib.snprintf(fb11, 1024, "编译管线失败: %s", br.ErrorMsg)
log.error(fb11, "FATAL")
sys.exit(1)
else:
if log is not None:

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -21,7 +21,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -22,7 +22,7 @@
},
"options": {
"slice_level": 3,
"sha1_slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}

View File

@@ -1,49 +0,0 @@
271ea3decb810db2:includes/atom.py
57288496f7c2d1ad:includes/json/__init__.py
240a9a4157959a9f:includes/json/__parser.py
20cd49775c100a38:includes/json/__writer.py
d089e25a15c6f6e0:includes/memhub.py
f5522571bcce7bcb:includes/stdint.py
6f62fe05c5ea1ceb:includes/stdio.py
90c53dd6db8d41cf:includes/stdlib.py
ba12ed409d78139e:includes/string.py
14d33679f7fadf1f:includes/testcheck.py
c9f4be41ca1cc2b4:includes/viperio.py
7e529fe7a078cfef:includes/w32/win32base.py
bbdf3bbd4c3bc28c:includes/w32/win32console.py
e3e7b6de8d7d8b03:includes/_dict.py
668790e6c9efdbae:includes/_list.py
80241c7d6f357bd1:App/asm_test.py
17c0dfbcebfa3a0e:App/attr_test.py
0eccc2393466d940:App/augassign_test.py
ac47a9dc68e6834c:App/circ_a.py
04d33c721a603632:App/circ_b.py
5314f09055045035:App/circ_test.py
9d8e1c9631c28b1b:App/closure_test.py
b7bbe6a1c1e75ad8:App/deco_test.py
6f5e876273093314:App/deref_min_test.py
eb252050d92f8fb9:App/deref_test.py
b8cbe044a10adcca:App/eq_test.py
e74870eba3e6d774:App/float_test.py
969c589035910845:App/flow_test.py
1eb1c9fc7e85d9f8:App/for_test.py
3c2fbc9e35ad3bf2:App/func_test.py
a9df037d9178888f:App/func_vtable_test.py
97043f0d52da437f:App/inherit_test.py
c39f3e540adb7b4e:App/llvmir_test.py
b7eeff93aa63e70f:App/namespace_defs.py
ef45adce81bf5b2d:App/namespace_test.py
9d798d964b9d3411:App/new_test.py
1b9ab264a942c56b:App/oop_test.py
a7cf0ce4066332ae:App/opovl_test.py
ed1c124f3cb787c9:App/ptr_only_test.py
07f7da85af876124:App/ptr_test.py
b078e9c9c74a2a0c:App/simple_test.py
74ca31446cac2107:App/string_min_test.py
e0009679269c6b63:App/string_test.py
33a854a5c4f67d74:App/struct_test.py
d7662500f1c2c0ff:App/testcheck_test.py
ff744980c190e9b0:App/test_main.py
55cbbb5dec4fef2c:App/type_bit_test.py
cc3ad7f3bc880326:App/virtual_dispatch_test.py
2769dee537308b00:App/vtable_test.py

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More