feat(tpv): VLogger 迁移 f-string + 模块限定构造 SHA1 解析 + 包重导出别名注册
- VLogger 全面改用栈 f-string 传参,移除 fmtbuffer 静态缓冲(Phase1Scan/Config/Converter/StubMergerStore/StubMergerTopo/ClassGenDef/StmtGenDeclMixin/CallGenCtor 等) - CallGenMixin: Module.Class(...) 按模块名解析 SHA1 精确命中类(Scene3D 跨模块同名根因);import X as Y 别名三级还原(精确→后缀→LookupModNameByAlias) - CallGenCtor: lookup_field_by_class 传 kw 节点与 from_imports;REnum 错误 f-string 化;新增 [DET] ctor 入口 can 核对 - StructGenMixin/GenApi/VpiReader: 包重导出类以包 sha1 注册别名 entry(ast.Attribute 归属错位) - main: 版本号更新、VLogger API(get_logger→init_logger/logger)、新增 --show-args/--build-dir - TypeGenCType: 移除 CDEF-MISS 调试日志;.gitignore 忽略 output_dbg/
This commit is contained in:
1 parent
19171ee165
commit
b3a4be21d3
16 files changed
+417
-288
No files matched your search
@@ -10,6 +10,7 @@ __pycache__/
|
||||
# 构建产物(防御性)
|
||||
**/output/
|
||||
**/temp/
|
||||
**/output_dbg/
|
||||
**/*.deps.json
|
||||
**/*.doc.json
|
||||
**/*.pyi
|
||||
|
||||
@@ -341,8 +341,7 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy | t.CPtr,
|
||||
handled_count += 1
|
||||
stdlib.free(src_buf_pg)
|
||||
if handled_count > 0:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "mark1 前永久池预注册 %d 个 includes 泛型类模板", handled_count)
|
||||
VLogger.info(VLogger.fmtbuffer, "Phase1")
|
||||
VLogger.info(f"mark1 前永久池预注册 {handled_count} 个 includes 泛型类模板", "Phase1")
|
||||
return handled_count
|
||||
# ============================================================
|
||||
# _FindClassDefSha1 - 在扁平数组中查找类名对应的定义文件 SHA1
|
||||
@@ -384,9 +383,7 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr,
|
||||
# 递归深度守卫:AST 损坏成自引用环(字段/children 回指祖先)时无限
|
||||
# 递归直至栈耗尽(每个 includes 文件 parse 后必执行)。fail-fast 终止。
|
||||
if depth > 128:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 256, "容器扫描递归过深(AST 环或嵌套超过 128 层)")
|
||||
VLogger.error(VLogger.fmtbuffer, "Phase1")
|
||||
sys.exit(1)
|
||||
VLogger.error("容器扫描递归过深(AST 环或嵌套超过 128 层)", "Phase1")
|
||||
if node is None:
|
||||
return
|
||||
k_s: int = node.kind()
|
||||
|
||||
@@ -560,37 +560,23 @@ def get_includes_binary_dir() -> str:
|
||||
|
||||
def print_config():
|
||||
"""打印当前配置"""
|
||||
buf: str = VLogger.fmtbuffer
|
||||
if buf is None:
|
||||
return
|
||||
if ProjectName is not None:
|
||||
viperlib.snprintf(buf, 1024, "project: %s v%s", ProjectName,
|
||||
ProjectVersion if ProjectVersion is not None else "?")
|
||||
VLogger.info(buf, "config")
|
||||
VLogger.info(f"project: {ProjectName} v{ProjectVersion if ProjectVersion is not None else "?"}", "config")
|
||||
if SourceDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "source_dir: %s", SourceDir)
|
||||
VLogger.info(buf, "config")
|
||||
VLogger.info(f"source_dir: {SourceDir}", "config")
|
||||
if BuildDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "build_dir: %s", BuildDir)
|
||||
VLogger.info(buf, "config")
|
||||
VLogger.info(f"build_dir: {BuildDir}", "config")
|
||||
if TempDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "temp_dir: %s", TempDir)
|
||||
VLogger.info(buf, "config")
|
||||
VLogger.info(f"temp_dir: {TempDir}", "config")
|
||||
if OutputDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "output_dir: %s", OutputDir)
|
||||
VLogger.info(buf, "config")
|
||||
VLogger.info(f"output_dir: {OutputDir}", "config")
|
||||
if CompilerCmd is not None:
|
||||
viperlib.snprintf(buf, 1024, "compiler: %s", CompilerCmd)
|
||||
VLogger.info(buf, "config")
|
||||
VLogger.info(f"compiler: {CompilerCmd}", "config")
|
||||
if LinkerCmd is not None:
|
||||
viperlib.snprintf(buf, 1024, "linker: %s -> %s", LinkerCmd,
|
||||
LinkerOutput if LinkerOutput is not None else "?")
|
||||
VLogger.info(buf, "config")
|
||||
VLogger.info(f"linker: {LinkerCmd} -> {LinkerOutput if LinkerOutput is not None else "?"}", "config")
|
||||
if IncludesDir is not None:
|
||||
viperlib.snprintf(buf, 1024, "includes: %s", IncludesDir)
|
||||
VLogger.info(buf, "config")
|
||||
viperlib.snprintf(buf, 1024, "sha1_slice_level: %d", Sha1SliceLevel)
|
||||
VLogger.info(buf, "config")
|
||||
VLogger.info(f"includes: {IncludesDir}", "config")
|
||||
VLogger.info(f"sha1_slice_level: {Sha1SliceLevel}", "config")
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
@@ -253,8 +253,7 @@ def _PyiTypeStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
|
||||
# BinOp.left 等回指祖先)时无限递归直至栈耗尽(Phase1 vpi 生成最先
|
||||
# 执行,早于翻译阶段)。fail-fast 终止,不静默回退。
|
||||
if depth > 64:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 256, "vpi 类型注解递归过深(AST 环或嵌套超过 64 层)")
|
||||
VLogger.error(VLogger.fmtbuffer, "StubGen")
|
||||
VLogger.error("vpi 类型注解递归过深(AST 环或嵌套超过 64 层)", "StubGen")
|
||||
sys.exit(1)
|
||||
if annot is None:
|
||||
return _PyiEmit(buf, buf_size, pos, "t.CInt")
|
||||
@@ -370,8 +369,7 @@ def _PyiDefaultStr(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
|
||||
"""生成默认值表达式文本到 buf,返回新的 pos"""
|
||||
# 递归深度守卫:AST 损坏成自引用环时 fail-fast 终止(同 _PyiTypeStr)
|
||||
if depth > 64:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 256, "vpi 默认值表达式递归过深(AST 环或嵌套超过 64 层)")
|
||||
VLogger.error(VLogger.fmtbuffer, "StubGen")
|
||||
VLogger.error("vpi 默认值表达式递归过深(AST 环或嵌套超过 64 层)", "StubGen")
|
||||
sys.exit(1)
|
||||
if node is None:
|
||||
return pos
|
||||
|
||||
@@ -1318,6 +1318,27 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
|
||||
if sifr_mod is not None:
|
||||
stub_imported_modules = _stub_add_imported(
|
||||
pool, stub_imported_modules, sifr_mod)
|
||||
# 方案A:包 __init__.vpi 的重导出类以包 sha1 注册别名 entry
|
||||
# (ast.Attribute 定义在 ast.exprs 但归属 ast 包;查找端按包
|
||||
# sha1 匹配,注册端按定义文件 sha1 → name+sha1 错位 fail-fast。
|
||||
# 仅 __init__ 触发,普通模块 vpi 的跨模块 from-import 不注册,
|
||||
# 避免把类错误归属到当前模块 sha1)
|
||||
is_pkg_init_rt: int = 0
|
||||
rel_path_rt: str = StubMergerStore.LookupSha1RelPath(src_sha1)
|
||||
if rel_path_rt is not None:
|
||||
rp_len_rt: t.CSizeT = string.strlen(rel_path_rt)
|
||||
if rp_len_rt >= 9 \
|
||||
and string.strcmp(rel_path_rt + rp_len_rt - 9, "__init__.vp") == 0:
|
||||
is_pkg_init_rt = 1
|
||||
# 解析被 from-import 的模块 sha1:相对导入(.exprs)按 StubMergerStore
|
||||
# 约定去前导点后缀匹配;绝对模块名精确匹配
|
||||
sub_sha1_rt: str = None
|
||||
if sifr_mod[0] != '\0' and sifr_mod[0] == '.':
|
||||
sub_suffix_rt: str = sifr_mod + 1
|
||||
if sub_suffix_rt is not None and sub_suffix_rt[0] != '\0':
|
||||
sub_sha1_rt = StubMergerStore.LookupSha1ByModNameSuffix(sub_suffix_rt)
|
||||
else:
|
||||
sub_sha1_rt = StubMergerStore.LookupSha1ByModName(sifr_mod)
|
||||
if sifr.names is not None:
|
||||
sifr_nn: t.CSizeT = len(sifr.names)
|
||||
sifri: t.CSizeT
|
||||
@@ -1333,6 +1354,11 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
|
||||
sorig = sial.name
|
||||
stub_from_imports = _stub_add_from(
|
||||
pool, stub_from_imports, slocal, sifr_mod, sorig)
|
||||
# 重导出类以包 sha1 注册(asname 时按别名注册)
|
||||
if is_pkg_init_rt != 0 and sub_sha1_rt is not None:
|
||||
_inject_reexport_class_declare(
|
||||
pool, mod, sial.name, slocal,
|
||||
sub_sha1_rt, src_sha1)
|
||||
|
||||
# 用 stub 自己的 imports 替代当前模块的(stub 注解解析上下文正确性)
|
||||
imported_modules: str = trans._imported_modules
|
||||
@@ -1498,6 +1524,54 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr,
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _inject_reexport_class_declare - 包重导出类以包 sha1 注册别名 entry
|
||||
#
|
||||
# 场景:TPV 源码 `import ast` 后写 `ast.Attribute`(模块限定名)。查找端
|
||||
# _resolve_module_sha1("ast") 经 "__init__ 包回退"解析为包 sha1,但 Attribute
|
||||
# 定义在 ast/exprs.vp 并以定义文件 sha1 注册 → find_struct_by_module
|
||||
# name+sha1 双精确匹配错位 fail-fast(lookup_field_by_class 'ast.Attribute'
|
||||
# 未找到根因)。
|
||||
#
|
||||
# 本函数在 _ParseVpiIntoModuleImpl 处理包 __init__.vpi 的 `from .exprs import
|
||||
# Attribute` 重导出行时调用:复用子模块已注册的 entry(Ty/Fields/父类/虚表),
|
||||
# 以包 sha1 追加别名注册,使 ast.Attribute 按包归属可解析。非类名(函数/
|
||||
# 常量)find_struct_by_module 查不到自然跳过。仅包 __init__ 触发,避免把
|
||||
# 普通模块的跨模块 from-import 错误归属到当前模块 sha1。
|
||||
#
|
||||
# Args:
|
||||
# src_name: 子模块中的原类名(如 Attribute)
|
||||
# local_name: 包命名空间中的名字(如 Attribute,或 `as 别名` 的别名)
|
||||
# sub_sha1: 子模块(定义文件)sha1
|
||||
# pkg_sha1: 当前包 __init__ 的 sha1
|
||||
# ============================================================
|
||||
def _inject_reexport_class_declare(pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
src_name: str,
|
||||
local_name: str,
|
||||
sub_sha1: str,
|
||||
pkg_sha1: str) -> int:
|
||||
"""把包子模块重导出的类以包 sha1 注册别名 entry(方案A:ast.Attribute 归属 ast 包)"""
|
||||
if pool is None or mod is None or src_name is None or local_name is None:
|
||||
return 0
|
||||
if sub_sha1 is None or sub_sha1[0] == '\0' or pkg_sha1 is None or pkg_sha1[0] == '\0':
|
||||
return 0
|
||||
if src_name[0] == '\0' or local_name[0] == '\0':
|
||||
return 0
|
||||
# 幂等:包 sha1 下 local_name 已注册则跳过(重复 import 不重复注册)
|
||||
if GenApi.find_struct_by_module(local_name, pkg_sha1) is not None:
|
||||
return 1
|
||||
# 找子模块源 entry(仅类有 entry;函数/常量查不到自然跳过)
|
||||
src_entry: GenTypes.StructEntry | t.CPtr = GenApi.find_struct_by_module(src_name, sub_sha1)
|
||||
if src_entry is None or src_entry.Ty is None:
|
||||
return 0
|
||||
# 注册别名 entry(绕过 register_struct 的 Ty 去重)
|
||||
if GenApi.register_struct_alias(pool, local_name, pkg_sha1, src_entry) is None:
|
||||
return 0
|
||||
GenApi.add_visible_struct(pool, local_name)
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _inject_class_struct_declare - 从 vpi ClassDef 注册类为 struct 类型
|
||||
#
|
||||
|
||||
@@ -119,8 +119,7 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult | t.CPtr) -> in
|
||||
StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0'
|
||||
StubMerger._sha1_store_count += 1
|
||||
if p_bad > 0:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "includes SHA1 存储有 %d 个条目失败", p_bad)
|
||||
VLogger.error(VLogger.fmtbuffer, "StubMerger")
|
||||
VLogger.error(f"includes SHA1 存储有 {p_bad} 个条目失败", "StubMerger")
|
||||
return StubMerger._sha1_store_count
|
||||
# ============================================================
|
||||
# AppendToSha1MapStore - 追加条目到全局 SHA1 映射存储器
|
||||
|
||||
@@ -62,8 +62,8 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
|
||||
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None:
|
||||
return 0
|
||||
indegree: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
queue: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
rel_idx: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
queue: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
rel_idx: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(stdlib.malloc(8 * (store_count + 1)))
|
||||
if indegree is None or queue is None or rel_idx is None:
|
||||
if indegree is not None:
|
||||
stdlib.free(indegree)
|
||||
@@ -91,8 +91,7 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes,
|
||||
rel_idx[rel_count] = i_t
|
||||
rel_count += 1
|
||||
# 诊断:可达 store 索引数(rel_count=0 → topo_count=0 → PhaseB+ 循环不执行)
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "诊断 topo rel_count=%d", rel_count)
|
||||
VLogger.debug(VLogger.fmtbuffer, "StubMergerTopo")
|
||||
VLogger.debug(f"诊断 topo rel_count={rel_count}", "StubMergerTopo")
|
||||
# 计算入度:m 在可达集合内的直接依赖数
|
||||
i_r: int
|
||||
for i_r in range(rel_count):
|
||||
|
||||
@@ -127,6 +127,12 @@ class CallGenCtor(CallGenBase.CallGenBase):
|
||||
OOP 结构体:alloca → call __before_init__(ptr) → call __init__(ptr, args) 或 store 参数 → load
|
||||
普通结构体:alloca → zero + defaults → store 参数 → load
|
||||
"""
|
||||
# [DET] callee 入口 can 核对(无条件首条打印):与槽位悬空处 can 对比——
|
||||
# 入口即垃圾 = ABI 栈参数错位(caller 存/callee 读错位 4 字节);
|
||||
# 入口正常但槽位悬空处垃圾 = 函数内部 can 被改写(栈槽复用)。
|
||||
# 无条件打印仅在进入结构体构造时执行一次(每 ctor 一条,量可控)。
|
||||
stdio.printf("[DET] ctor入口: can=%llu cargs=%llu\n",
|
||||
t.CUInt64T(can), t.CUInt64T(cargs))
|
||||
# [DET] 读取侧存活校验(detection 模式启用):cargs(cl.args)必须指向
|
||||
# 存活块——不在记录表(或已 free)= use-after-free 读取(SlotsBugTest
|
||||
# KwEntry cargs 悬空根因定位)。is_alive 内部处理 detection==0 时不拦截,
|
||||
@@ -432,10 +438,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if renum_entry is None:
|
||||
renum_entry = self.Gen.StructGen.find_struct(class_name)
|
||||
if renum_entry is None:
|
||||
fb_re: str = VLogger.fmt_buf()
|
||||
if fb_re is not None:
|
||||
viperlib.snprintf(fb_re, 1024, "REnum construct: type %s not registered", class_name)
|
||||
VLogger.error(fb_re, "RENUM")
|
||||
VLogger.error(f"REnum construct: type {class_name} not registered", "RENUM")
|
||||
return None
|
||||
|
||||
renum_ty: llvmlite.LLVMType | t.CPtr = renum_entry.Ty
|
||||
@@ -709,7 +712,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if kw is None or kw.arg is None or kw.value is None:
|
||||
continue
|
||||
fe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field_by_class(
|
||||
class_name, kw.arg, kw_sha1)
|
||||
class_name, kw.arg, kw_sha1, kw, trans._from_imports)
|
||||
if fe is None:
|
||||
# 关键字字段不存在:fail-fast(AttributeError),绝不静默跳过
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
|
||||
@@ -85,7 +85,6 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
can: t.CSizeT = 0
|
||||
if cargs is not None:
|
||||
can = len(cargs)
|
||||
|
||||
# super()/ctraits 检测:void 方法(如 __init__)命中后 build_call 返回
|
||||
# None 属预期(void call 无 SSA 值),必须用 hit_box 区分"命中已处理"
|
||||
# 与"未命中",否则 None 被误判继续主路径 → 内层 Call(super) 当裸名
|
||||
@@ -227,8 +226,30 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# 用 find_struct_by_module 规避跨模块同名 find_struct 找错
|
||||
struct_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
gen_ctor_entry: GenTypes.StructEntry | t.CPtr = None
|
||||
# 模块限定构造 Module.Class(...) 优先:按模块名解析 SHA1 精确命中类。
|
||||
# 必须在裸名可见性分支之前——跨模块同名类(VKernel gfx 与 App gfx 都
|
||||
# 定义 Scene3D)存在时,裸名 find_struct 返回先注册的错误 entry
|
||||
# (0ae → ctor 生成 @0ae.Scene3D.__before_init__ → 链接 undefined
|
||||
# symbol,Scene3D 根因)。模块解析成功即锁定,跳过下方裸名回退。
|
||||
mod_qual_done: int = 0
|
||||
if trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at_qc0: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
|
||||
mod_path0: str = None
|
||||
if at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Name:
|
||||
mod_nm0: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_qc0.value)
|
||||
if mod_nm0.id is not None:
|
||||
mod_path0 = mod_nm0.id
|
||||
elif at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Attribute:
|
||||
mod_path0 = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc0.value)
|
||||
if mod_path0 is not None:
|
||||
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_path0) != 0:
|
||||
mod_sha1_0: str = self._lookup_module_sha1(mod_path0)
|
||||
if mod_sha1_0 is not None:
|
||||
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(
|
||||
func_name, mod_sha1_0)
|
||||
mod_qual_done = 1
|
||||
vis: int = self.Gen.StructGen.is_struct_visible(func_name)
|
||||
if vis != 0:
|
||||
if mod_qual_done == 0 and vis != 0:
|
||||
if trans is not None:
|
||||
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, trans.ModuleSha1)
|
||||
if gen_ctor_entry is None:
|
||||
@@ -239,10 +260,20 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
at_qc: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
|
||||
if at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Name:
|
||||
mod_nm_qc: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_qc.value)
|
||||
imp_ok: int = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, mod_nm_qc.id)
|
||||
imp_ok: int = self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_nm_qc.id)
|
||||
if imp_ok != 0:
|
||||
if trans is not None:
|
||||
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, trans.ModuleSha1)
|
||||
# 模块限定构造 Module.Class(...):按模块名解析 SHA1 精确命中类。
|
||||
# 不能用 trans.ModuleSha1(那是当前文件 sha1,类在 gfx 模块)——
|
||||
# find_struct_by_module 落空后裸名回退 find_struct 会命中跨模块
|
||||
# 同名类中先注册的 entry(VKernel gfx 与 App gfx 都定义 Scene3D,
|
||||
# 0ae 先注册 → ctor 生成 @0ae.Scene3D.__before_init__ → 链接
|
||||
# undefined symbol,Scene3D 根因)。
|
||||
mod_sha1_qc2: str = None
|
||||
if mod_nm_qc.id is not None:
|
||||
mod_sha1_qc2 = self._lookup_module_sha1(mod_nm_qc.id)
|
||||
if mod_sha1_qc2 is not None:
|
||||
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(
|
||||
func_name, mod_sha1_qc2)
|
||||
if gen_ctor_entry is None:
|
||||
gen_ctor_entry = self.Gen.StructGen.find_struct(func_name)
|
||||
if gen_ctor_entry is not None:
|
||||
@@ -306,7 +337,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
ctor_is_oop = gen_ctor_entry.IsOOP
|
||||
if ctor_is_oop != 0:
|
||||
return self.Gen.CallGenCtor._translate_struct_ctor(
|
||||
pool, builder, mod, struct_ty, func_name, cargs, 0, trans)
|
||||
pool, builder, mod, struct_ty, func_name, cargs, t.CSizeT(0), trans)
|
||||
# @t.NoVTable 类(无方法、类语义/指针对象):无参构造也应分配内存
|
||||
# 返回指针。否则按值结构体返回 ConstZero(struct_ty),LHS 联合注解
|
||||
# (如 Plain|CPtr)存 0 指针 → p.v 解引用 NULL 段错误
|
||||
@@ -316,7 +347,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
ctor_is_novt = gen_ctor_entry.IsNoVTable
|
||||
if ctor_is_novt != 0:
|
||||
return self.Gen.CallGenCtor._translate_struct_ctor(
|
||||
pool, builder, mod, struct_ty, func_name, cargs, 0, trans)
|
||||
pool, builder, mod, struct_ty, func_name, cargs, t.CSizeT(0), trans)
|
||||
# 普通结构体:检查是否有默认值(用 entry 直接访问)
|
||||
gen_has_defaults: int = 0
|
||||
if gen_ctor_entry is not None:
|
||||
@@ -331,7 +362,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if gen_has_defaults == 1:
|
||||
# 有默认值:走构造函数路径应用默认值
|
||||
return self.Gen.CallGenCtor._translate_struct_ctor(
|
||||
pool, builder, mod, struct_ty, func_name, cargs, 0, trans)
|
||||
pool, builder, mod, struct_ty, func_name, cargs, t.CSizeT(0), trans)
|
||||
# 无默认值:返回 zeroinitializer
|
||||
ret: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, struct_ty)
|
||||
return ret
|
||||
@@ -340,7 +371,6 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
return self.Gen.CallGenCtor._translate_struct_ctor_kw(
|
||||
pool, builder, mod, struct_ty, func_name, cargs, can,
|
||||
keywords, kw_count, trans)
|
||||
# 仅位置参数:按字段顺序构建
|
||||
return self.Gen.CallGenCtor._translate_struct_ctor(
|
||||
pool, builder, mod, struct_ty, func_name, cargs, can, trans)
|
||||
|
||||
@@ -592,9 +622,19 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
xm_mod_path = self.Gen.ExprGenAttr._attr_module_path_str(
|
||||
pool, xm_at.value)
|
||||
if xm_mod_path is not None:
|
||||
if self.Gen.ImportsGen.is_module_imported(
|
||||
if self.Gen.ImportsGen.is_module_imported_or_known(
|
||||
trans._imported_modules, xm_mod_path) != 0:
|
||||
mod_sha1_chk: str = self._lookup_module_sha1(xm_mod_path)
|
||||
if mod_sha1_chk is None and trans._from_imports is not None:
|
||||
# import X as Y(如 import fat32_diskio as diskio):
|
||||
# 精确查不到别名,必须按别名还原后再查(精确→后缀→
|
||||
# LookupModNameByAlias 三级)。否则本校验被跳过 →
|
||||
# find_func_in_module 后缀匹配误命中本模块同名函数
|
||||
# (diskio.read 命中 fat32 自身 read)→ call 到错误
|
||||
# 符号 + 参数错位 + define 被 4 参调用污染(VKernel
|
||||
# llc "instruction expected to be numbered" 根因)。
|
||||
mod_sha1_chk = self.Gen.StructGen._resolve_module_sha1(
|
||||
xm_mod_path, trans._from_imports)
|
||||
if mod_sha1_chk is not None:
|
||||
ff_nm2: str = llvmlite.function_get_name(found_func)
|
||||
if ff_nm2 is not None:
|
||||
@@ -640,12 +680,19 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if bi_sha1 is not None:
|
||||
bi_ret: llvmlite.LLVMType | t.CPtr = None
|
||||
bi_p1: llvmlite.LLVMType | t.CPtr = None
|
||||
bi_p2: llvmlite.LLVMType | t.CPtr = None
|
||||
if string.strcmp(func_name, "ord") == 0:
|
||||
bi_ret = llvmlite.Int32(pool)
|
||||
bi_p1 = llvmlite.Int8(pool)
|
||||
elif string.strcmp(func_name, "chr") == 0:
|
||||
bi_ret = llvmlite.Int8(pool)
|
||||
bi_p1 = llvmlite.Int32(pool)
|
||||
elif string.strcmp(func_name, "min") == 0 or string.strcmp(func_name, "max") == 0:
|
||||
# min/max 数值比较(builtins.vp 定义 double 参数/返回,
|
||||
# int/float 实参经 coerce 提升;两参数)
|
||||
bi_ret = llvmlite.Double(pool)
|
||||
bi_p1 = llvmlite.Double(pool)
|
||||
bi_p2 = llvmlite.Double(pool)
|
||||
if bi_ret is not None:
|
||||
bi_sym: str = func_name
|
||||
if self.Gen.CallGenBase.is_cexport_func(bi_sha1, func_name) == 0:
|
||||
@@ -658,6 +705,8 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
pool, mod, bi_sym, bi_ret)
|
||||
if bi_decl is not None:
|
||||
llvmlite.add_param(pool, bi_decl, bi_p1, "v")
|
||||
if bi_p2 is not None:
|
||||
llvmlite.add_param(pool, bi_decl, bi_p2, "w")
|
||||
found_func = bi_decl
|
||||
# 使用访问器绕过 stub 类型限制
|
||||
found_ret_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
@@ -678,12 +727,20 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# (依赖 rhs_val.Ty 是 Struct)静默跳过 → 目标变量从未赋值 →
|
||||
# free 未初始化栈垃圾指针崩溃(HashLibTest hqx 段)。
|
||||
vpi_ret_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
# 跨模块返回类型解析的来源模块 SHA1:仅在下方 cl.func 是
|
||||
# Attribute(模块.函数) 分支赋值。必须显式初始化 None——此前声明在
|
||||
# if 块内、elif mod_ret_sha1 is not None 在外层读取,裸名调用
|
||||
# (cl.func 是 Name,如 LLVMModuleCore 方法内裸名 create_global_int)
|
||||
# 时该 alloca 从未 store,读到栈残留垃圾(0x1)→
|
||||
# _LookupMethodRetTyFromTextLL 的 strlen(0x1) 零页崩溃
|
||||
# (llvmlite/__init__.vp CreateGlobalInt 翻译时 0xC0000005 根因)。
|
||||
mod_ret_sha1: str = None
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at_ret: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
|
||||
if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name:
|
||||
mod_ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_ret.value)
|
||||
if mod_ret_nm.id is not None:
|
||||
mod_ret_sha1: str = self._lookup_module_sha1(mod_ret_nm.id)
|
||||
mod_ret_sha1 = self._lookup_module_sha1(mod_ret_nm.id)
|
||||
if mod_ret_sha1 is not None:
|
||||
# 函数内懒 import:VpiReader imports HandlesExprCall,
|
||||
# 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致)
|
||||
@@ -697,18 +754,52 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
llvmlite.Ptr(pool, llvmlite.Int8(pool)))
|
||||
if vpi_ret_ty is not None:
|
||||
call_ret_ty = vpi_ret_ty
|
||||
elif mod_ret_sha1 is not None:
|
||||
# 跨模块裸名 CExport 函数(如 mm.malloc → @malloc):found_func=None
|
||||
# 且 vpi 返回注解缓存未命中时,仍从依赖模块 text.ll/vpi 精确解析
|
||||
# 返回类型(对齐 mangled 路径),避免静默默认 i32 截断指针/i64
|
||||
# (uhci.vp mm.malloc 根因:call i32 @malloc 截断 0xbb69000 → null)。
|
||||
# 依赖模块尚未编译(text.ll/vpi 均未生成)→ 标记 _deps_pending=1
|
||||
# 下遍重试,本遍保持默认类型(Phase A 多遍收敛)。
|
||||
cross_ret: llvmlite.LLVMType | t.CPtr = None
|
||||
if trans is not None and trans._declare_only == 0:
|
||||
cross_ret = self._LookupMethodRetTyFromTextLL(
|
||||
pool, mod_ret_sha1, None, func_name, 1)
|
||||
if cross_ret is None:
|
||||
cross_ret = self._lookup_method_ret_ty_via_vpi(
|
||||
pool, mod_ret_sha1, None, func_name)
|
||||
if cross_ret is not None:
|
||||
call_ret_ty = cross_ret
|
||||
else:
|
||||
if trans is not None and trans._declare_only == 0:
|
||||
if self._dep_text_ll_exists(pool, mod_ret_sha1) == 0:
|
||||
trans._deps_pending = 1
|
||||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name)
|
||||
else:
|
||||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name)
|
||||
|
||||
# 如果模块中找不到函数,仅裸名调用(Name)才回退查局部变量(函数指针/
|
||||
# 闭包调用);模块属性调用(VLogger.fmt_buf())的目标是模块符号,绝不
|
||||
# 回退局部变量——跨模块任意同名可接受(CallGenLeafKit 局部 fmt_buf
|
||||
# 曾劫持 VLogger.fmt_buf → 误走函数指针调用报 t.Callable 缺失),
|
||||
# 仅 from-import 的裸名 yyy 与局部重名才可能冲突(该场景 func 是 Name)。
|
||||
if found_func is None:
|
||||
fp_alloca: llvmlite.Value | t.CPtr = None
|
||||
if cl.func is None or cl.func.kind() == ast.ASTKind.Name:
|
||||
fp_alloca = self.Gen.VarGen.lookup_var(trans.SymTab, func_name)
|
||||
# 闭包内调用捕获的闭包变量(如 _cmd_cd 调 _build_path):
|
||||
# 从 env 读闭包对象指针,不能 lookup_var 跨函数命中父函数
|
||||
# 闭包 alloca(SSA 错乱 → llc "instruction expected to be
|
||||
# numbered",Terminal _cmd_cd 根因)。
|
||||
if trans is not None and HT.is_nonlocal_name(trans, func_name) != 0:
|
||||
fp_alloca = self.Gen.StmtGenDecl.load_nonlocal_var(trans, func_name)
|
||||
if fp_alloca is not None:
|
||||
# translate_closure_call 期望 i8** 存储(load 一次得闭包
|
||||
# 对象指针);load_nonlocal_var 已返回对象指针 → 临时
|
||||
# alloca 暂存
|
||||
i8_ty_nl: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty_nl: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_nl)
|
||||
tmp_alloca_nl: llvmlite.Value | t.CPtr = llvmlite.build_alloca(
|
||||
builder, i8_ptr_ty_nl)
|
||||
if tmp_alloca_nl is not None:
|
||||
llvmlite.build_store(builder, fp_alloca, tmp_alloca_nl)
|
||||
fp_alloca = tmp_alloca_nl
|
||||
else:
|
||||
fp_alloca = self.Gen.VarGen.lookup_var(trans.SymTab, func_name)
|
||||
if fp_alloca is not None:
|
||||
# 闭包变量(嵌套函数提升,已注册到闭包名集合):提取 fn_ptr/env_ptr 间接调用
|
||||
if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0:
|
||||
@@ -950,6 +1041,24 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
found_name: str = llvmlite.function_get_name(found_func)
|
||||
if found_name is not None:
|
||||
call_name = found_name
|
||||
# CExport 裸名兜底:跨模块 stub declare 可能带 SHA1 前缀
|
||||
# (@"1fdad1c76fb116bc.malloc"),但定义端 CExport 函数
|
||||
# (mm.vp malloc/free/malloc_direct,返回注解含 t.CExport)
|
||||
# 用裸名 @malloc → 链接 undefined symbol: 1fdad1c76fb116bc.malloc。
|
||||
# found_func 非 None 时下方 is_cexport_func 裸名分支不执行
|
||||
# (该分支仅 found_func None 的模块限定路径),这里对带前缀
|
||||
# 的 declare 提取 sha1 再查 CExport 注册表,命中则改用裸名。
|
||||
if found_func.IsDeclared != 0:
|
||||
dot_pos_cx: str = string.strchr(found_name, 46)
|
||||
if dot_pos_cx is not None:
|
||||
cx_pre_len: t.CSizeT = string.strlen(found_name) - string.strlen(dot_pos_cx)
|
||||
if cx_pre_len == 16:
|
||||
cx_sha1_buf: str = pool.alloc(17)
|
||||
if cx_sha1_buf is not None:
|
||||
string.memcpy(cx_sha1_buf, found_name, 16)
|
||||
cx_sha1_buf[16] = '\0'
|
||||
if self.Gen.CallGenBase.is_cexport_func(cx_sha1_buf, func_name) != 0:
|
||||
call_name = func_name
|
||||
else:
|
||||
# 跨模块函数调用:found_func 为 None 时,检查是否是模块限定调用
|
||||
# 如 string.strchr(...) → @{sha1_of_string}.strchr
|
||||
@@ -979,7 +1088,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# 静默生成 @{sha1}.func 调用而非报错——违反"找不到 fast-fail"原则。
|
||||
is_xmod_imported: int = 1
|
||||
if trans is not None and trans._imported_modules is not None:
|
||||
is_xmod_imported = self.Gen.ImportsGen.is_module_imported(
|
||||
is_xmod_imported = self.Gen.ImportsGen.is_module_imported_or_known(
|
||||
trans._imported_modules, xmod_path)
|
||||
if is_xmod_imported == 0:
|
||||
err_ni: str = pool.alloc(256)
|
||||
@@ -1213,13 +1322,6 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# 保持白名单类型继续,Phase A 多遍循环下遍重译——
|
||||
# 届时依赖已生成 text.ll,可精确解析(治本:依赖先翻译)。
|
||||
if self._dep_text_ll_exists(pool, cf_sha1) != 0:
|
||||
cf_err: str = VLogger.fmt_buf()
|
||||
if cf_err is not None:
|
||||
viperlib.snprintf(
|
||||
cf_err, 1024,
|
||||
"跨模块函数 '%s' 返回类型无法精确获取(text.ll/vpi 均未命中),"
|
||||
"默认 i32 会截断指针/i64,请检查依赖模块是否已编译/注册", call_name)
|
||||
VLogger.error(cf_err, "HandlesExprCall")
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
node,
|
||||
f"跨模块函数返回类型无法精确获取(text.ll/vpi 均未命中),默认 i32 会截断指针: {call_name}",
|
||||
@@ -1227,14 +1329,9 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
else:
|
||||
if trans is not None:
|
||||
trans._deps_pending = 1
|
||||
cf_dbg: str = VLogger.fmt_buf()
|
||||
if cf_dbg is not None:
|
||||
viperlib.snprintf(
|
||||
cf_dbg, 1024,
|
||||
"跨模块函数 '%s' 依赖模块 text.ll 尚未生成(Phase A 顺序),"
|
||||
"标记重试,本遍保持默认类型", call_name)
|
||||
if VLogger.nowarn_register() == 0:
|
||||
VLogger.warning(cf_dbg, "HandlesExprCall")
|
||||
if VLogger._nowarn_register == 0:
|
||||
VLogger.warning(f"跨模块函数 '{call_name}' 依赖模块 text.ll 尚未生成(Phase A 顺序),"
|
||||
"标记重试,本遍保持默认类型", "HandlesExprCall")
|
||||
stdlib.free(cf_sha1)
|
||||
stdlib.free(cf_bare)
|
||||
# 跨模块调用:创建 declare 声明,避免 llc 报 undefined value
|
||||
@@ -1242,7 +1339,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# 检查被调用函数是否为变长函数(*args),若是则对变长参数进行 C ABI 提升
|
||||
# (cargs 为本函数参数 AST 链表,self_offset=0 普通函数调用)
|
||||
is_variadic_call: int = self.Gen.CallGenVa._check_and_promote_variadic_call(
|
||||
pool, builder, mod, call_name, head, can, cargs, 0, trans)
|
||||
pool, builder, mod, call_name, head, can, cargs, t.CSizeT(0), trans)
|
||||
# try-except:被调函数带 EH 输出参数时,追加输出实参 + 调用后检测异常
|
||||
return self.Gen.CallGenLeafKit._emit_eh_guarded_call(
|
||||
builder, pool, mod, trans, found_func, call_name, head, can,
|
||||
|
||||
@@ -421,8 +421,7 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
vtable_node: llvmlite.ParamNode | t.CPtr = pool.alloc(llvmlite.ParamNode.__sizeof__())
|
||||
if vtable_node is None:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "failed to alloc vtable node for %s", class_name)
|
||||
VLogger.error(VLogger.fmtbuffer, "CLASS")
|
||||
VLogger.error(f"failed to alloc vtable node for {class_name}", "CLASS")
|
||||
return 0
|
||||
string.memset(vtable_node, 0, llvmlite.ParamNode.__sizeof__())
|
||||
vtable_node.Ty = i8_ptr_ty
|
||||
@@ -469,8 +468,7 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
# 创建 StructType(命名结构体)
|
||||
struct_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, first_node, total_field_count, type_name)
|
||||
if struct_ty is None:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "failed to create StructType for %s", class_name)
|
||||
VLogger.error(VLogger.fmtbuffer, "CLASS")
|
||||
VLogger.error(f"failed to create StructType for {class_name}", "CLASS")
|
||||
return 0
|
||||
|
||||
# Phase 1b(declare_only=0)新注册的 struct_ty 会被注册到全局表并被后续文件
|
||||
@@ -507,8 +505,7 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.register_struct(
|
||||
pool, class_name, struct_ty, trans.ModuleSha1)
|
||||
if entry is None:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "failed to register %s", class_name)
|
||||
VLogger.error(VLogger.fmtbuffer, "CLASS")
|
||||
VLogger.error(f"failed to register {class_name}", "CLASS")
|
||||
return 0
|
||||
|
||||
# 标记 VTable 状态(直接用 entry,规避跨模块同名 find_struct 找错)
|
||||
|
||||
@@ -375,6 +375,19 @@ def register_struct(pool: memhub.MemBuddy | t.CPtr,
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- register_struct_alias(委托 StructGenMixin.register_struct_alias) ----------------
|
||||
def register_struct_alias(pool: memhub.MemBuddy | t.CPtr,
|
||||
name: str,
|
||||
sha1: str,
|
||||
src_entry: GenTypes.StructEntry | t.CPtr) -> GenTypes.StructEntry | t.CPtr:
|
||||
"""委托到 gen.StructGen.register_struct_alias(包重导出类别名注册,原 Handles 壳)"""
|
||||
import lib.core.Gen.Generator as Generator
|
||||
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
|
||||
if g is not None:
|
||||
return g.StructGen.register_struct_alias(pool, name, sha1, src_entry)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------- get_stdlib_alloc(委托 StructGenMixin.get_stdlib_alloc) ----------------
|
||||
def get_stdlib_alloc() -> int:
|
||||
"""委托到 gen.get_stdlib_alloc(原 Handles 壳)"""
|
||||
|
||||
@@ -461,8 +461,7 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return existing
|
||||
|
||||
if self.EnumCount >= ENUM_MAX:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "table full, cannot register %s", name)
|
||||
VLogger.error(VLogger.fmtbuffer, "ENUM")
|
||||
VLogger.error(f"table full, cannot register {name}", "ENUM")
|
||||
return None
|
||||
|
||||
entry: GenTypes.EnumEntry | t.CPtr = self._get_enum_entry(self.EnumCount)
|
||||
@@ -522,8 +521,7 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
return ei_d
|
||||
|
||||
if enum_entry.MemberCount >= ENUM_MEMBER_MAX:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "member table full for %s", enum_entry.Name)
|
||||
VLogger.error(VLogger.fmtbuffer, "ENUM")
|
||||
VLogger.error(f"member table full for {enum_entry.Name}", "ENUM")
|
||||
return -1
|
||||
|
||||
idx: int = enum_entry.MemberCount
|
||||
|
||||
@@ -190,8 +190,7 @@ class StructGenMixin:
|
||||
return existing
|
||||
|
||||
if self.StructCount >= STRUCT_MAX:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "table full, cannot register %s", name)
|
||||
VLogger.error(VLogger.fmtbuffer, "STRUCT")
|
||||
VLogger.error(f"table full, cannot register {name}", "STRUCT")
|
||||
return None
|
||||
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(self.StructCount)
|
||||
@@ -241,6 +240,73 @@ class StructGenMixin:
|
||||
self.StructCount += 1
|
||||
return entry
|
||||
|
||||
def register_struct_alias(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
name: str,
|
||||
sha1: str,
|
||||
src_entry: GenTypes.StructEntry | t.CPtr) -> GenTypes.StructEntry | t.CPtr:
|
||||
"""为已注册类追加包重导出别名 entry(方案A:ast.Attribute 归属 ast 包)
|
||||
|
||||
子模块类从包 __init__ 重导出时,查找端 _resolve_module_sha1 按
|
||||
"__init__ 包回退"解析为包 sha1,但类以定义文件 sha1 注册 → name+sha1
|
||||
双精确匹配错位 fail-fast(lookup_field_by_class 'ast.Attribute' 未找到
|
||||
根因)。本函数绕过 register_struct 的 Ty 去重,追加同名 entry:
|
||||
ModuleSha1=包 sha1,Ty/Fields/父类/虚表标志复用源 entry(同一类型,
|
||||
仅归属模块不同,不影响 Ty 去重与 find_struct_by_type 指针匹配)。
|
||||
|
||||
幂等:目标 (name, sha1) 已注册则返回现有 entry。返回 None 失败。
|
||||
"""
|
||||
if name is None or sha1 is None or src_entry is None:
|
||||
return None
|
||||
if self.init_struct_table(pool) == 0:
|
||||
return None
|
||||
# 幂等:目标 (name, sha1) 已注册则跳过(find_struct_by_module 不回退裸名)
|
||||
if sha1[0] != '\0':
|
||||
existing: GenTypes.StructEntry | t.CPtr = self.find_struct_by_module(name, sha1)
|
||||
if existing is not None:
|
||||
return existing
|
||||
if self.StructCount >= STRUCT_MAX:
|
||||
return None
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(self.StructCount)
|
||||
if entry is None:
|
||||
return None
|
||||
# 复制类名与 SHA1(别名注册同样按 StdlibAlloc 选分配源,跨 1a/1b reset 存活)
|
||||
alias_pool: memhub.MemBuddy | t.CPtr = pool
|
||||
if self.StdlibAlloc != 0:
|
||||
alias_pool = CompilerFactory.factory.PermPool
|
||||
if alias_pool is None:
|
||||
alias_pool = pool
|
||||
name_len: t.CSizeT = string.strlen(name)
|
||||
name_buf: str = alias_pool.alloc(name_len + 1)
|
||||
if name_buf is None:
|
||||
return None
|
||||
string.strcpy(name_buf, name)
|
||||
entry.Name = name_buf
|
||||
sha1_len: t.CSizeT = string.strlen(sha1)
|
||||
sha1_buf: str = alias_pool.alloc(sha1_len + 1)
|
||||
if sha1_buf is None:
|
||||
return None
|
||||
string.strcpy(sha1_buf, sha1)
|
||||
entry.ModuleSha1 = sha1_buf
|
||||
# 复用源 entry 的类型、字段与类属性(同一类,仅归属模块不同)
|
||||
entry.Ty = src_entry.Ty
|
||||
entry.FieldCount = src_entry.FieldCount
|
||||
entry.Fields = src_entry.Fields
|
||||
entry.IsUnion = src_entry.IsUnion
|
||||
entry.IsOOP = src_entry.IsOOP
|
||||
entry.HasInit = src_entry.HasInit
|
||||
entry.HasNew = src_entry.HasNew
|
||||
entry.HasVTable = src_entry.HasVTable
|
||||
entry.IsNoVTable = src_entry.IsNoVTable
|
||||
entry.ParentName = src_entry.ParentName
|
||||
entry.ParentNames = src_entry.ParentNames
|
||||
entry.ParentCount = src_entry.ParentCount
|
||||
entry.VTableMethodCount = src_entry.VTableMethodCount
|
||||
entry.VTableMethods = src_entry.VTableMethods
|
||||
entry.MethodsEmitted = 0
|
||||
entry.SpecTriggerSha1 = None
|
||||
self.StructCount += 1
|
||||
return entry
|
||||
|
||||
def set_struct_sha1(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
class_name: str, sha1: str) -> int:
|
||||
"""设置结构体所属模块的 SHA1,返回 1=成功 / 0=失败"""
|
||||
@@ -287,8 +353,7 @@ class StructGenMixin:
|
||||
return -1
|
||||
|
||||
if struct_entry.FieldCount >= FIELD_MAX:
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024, "field table full for %s", struct_entry.Name)
|
||||
VLogger.error(VLogger.fmtbuffer, "STRUCT")
|
||||
VLogger.error(f"field table full for {struct_entry.Name}", "STRUCT")
|
||||
return -1
|
||||
|
||||
idx: int = struct_entry.FieldCount
|
||||
@@ -882,7 +947,6 @@ class StructGenMixin:
|
||||
return rn_fe_l
|
||||
# 诊断:找到结构体但字段确实未找到
|
||||
# 拼接注册字段列表(一行逗号分隔,供报错一眼定位)
|
||||
# 注意:不能用 VLogger.fmtbuffer(与 fb_fields 同一静态缓冲,会被覆盖)
|
||||
fb_flds_lst: str = stdlib.malloc(1024)
|
||||
if fb_flds_lst is not None:
|
||||
fb_flds_lst[0] = '\0'
|
||||
|
||||
@@ -11,6 +11,7 @@ import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
@@ -328,13 +328,6 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
self.ConstEvalOk = 1
|
||||
return x_val
|
||||
# 定位日志:跨模块 CDefine 查找失败(t.CArray count 场景)
|
||||
viperlib.snprintf(VLogger.fmtbuffer, 1024,
|
||||
"[CDEF-MISS] name=%s fi=%s im=%s",
|
||||
nm.id,
|
||||
from_imports if from_imports is not None else "(null)",
|
||||
imported_modules if imported_modules is not None else "(null)")
|
||||
VLogger.error(VLogger.fmtbuffer, "CDEF")
|
||||
self.ConstEvalOk = 0
|
||||
return 0
|
||||
self.ConstEvalOk = 1
|
||||
|
||||
+98
-189
@@ -42,12 +42,12 @@ CODE_PAGE: t.CDefine = 65001
|
||||
# Source code buffer size (1MB)
|
||||
SRC_BUF_SIZE: t.CDefine = 1048576
|
||||
# Version (dev/beta/rc)
|
||||
V_VIPER_VERSION: t.CDefine = "1.0.3-dev.30 2026-07-27 Non-LTS"
|
||||
V_TPV_VERSION: t.CDefine = "0.0.3-dev.230"
|
||||
V_RUNTIME_VERSION: t.CDefine = "0.2.2+4-Non-LTS"
|
||||
V_TPC_COMMIT: t.CDefine = "1dc23ffdde"
|
||||
V_TPV_COMMIT: t.CDefine = "f4fee79d4d"
|
||||
V_RUNTIME_COMMIT: t.CDefine = "4ce6095359"
|
||||
V_VIPER_VERSION: t.CDefine = "1.0.3-dev.31 2026-07-27 Non-LTS"
|
||||
V_TPV_VERSION: t.CDefine = "0.0.3-dev.428"
|
||||
V_RUNTIME_VERSION: t.CDefine = "0.2.3+19-Non-LTS"
|
||||
V_TPC_COMMIT: t.CDefine = "e376b949c9"
|
||||
V_TPV_COMMIT: t.CDefine = "ab0f37e1a8"
|
||||
V_RUNTIME_COMMIT: t.CDefine = "f087aa88e2"
|
||||
V_TARGET: t.CDefine = "x86_64-w64-windows-gnu"
|
||||
V_LLVM: t.CDefine = "22.1.8"
|
||||
|
||||
@@ -58,10 +58,6 @@ def main() -> int:
|
||||
w32cmd.SetConsoleOutputCP(CODE_PAGE)
|
||||
w32cmd.SetConsoleCP(CODE_PAGE)
|
||||
|
||||
# [DET] 分配检测开关:在创建内存池前扫描命令行(sys._init_argv 依赖 mb,
|
||||
# 不能提前;GetCommandLineA 直接读原始命令行,不含任何 pool 分配)。
|
||||
# --detection 开启后 mb/perm_pool 均启用前后 0xCDCD 标记 + 记录表探测
|
||||
# 越界写(定位"存活块被部分覆盖"的写入者,如 KwEntry cargs)。
|
||||
detect_flag: int = 0
|
||||
cmd_line_d: str = w32base.GetCommandLineA()
|
||||
if cmd_line_d is not None and string.strstr(cmd_line_d, "--detection") is not None:
|
||||
@@ -88,7 +84,7 @@ def main() -> int:
|
||||
stdio.printf("FAIL: perm_pool init failed\n")
|
||||
return 1
|
||||
|
||||
CompilerFactory.set_factory(CompilerFactory.CompilerFactory(mb, perm_pool))
|
||||
CompilerFactory.factory = CompilerFactory.CompilerFactory(mb, perm_pool)
|
||||
|
||||
# 设置全局 mbuddy 指针(sys 和 argparse 都需要)
|
||||
sys._mbuddy = mb
|
||||
@@ -128,7 +124,8 @@ def main() -> int:
|
||||
# P6: 注入语句/赋值/变量/导入/翻译驱动模块的单例
|
||||
|
||||
# 初始化 VLogger 并打印启动日志
|
||||
log: VLogger.Logger | t.CPtr = VLogger.get_logger()
|
||||
VLogger.init_logger()
|
||||
log: VLogger.Logger | t.CPtr = VLogger.logger
|
||||
if log is None:
|
||||
log.error("TransPyV Failed to parse parameters")
|
||||
|
||||
@@ -153,14 +150,15 @@ def main() -> int:
|
||||
parser.add_argument("--phase2command", None, argparse.STRING, 0, None, False, argparse.STORE, "Do not run llc/link: write Phase2 llc(opt+llc) and clang link commands to the given .bat path")
|
||||
parser.add_argument("--nowarn-register", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Suppress Phase A dependency WARN")
|
||||
parser.add_argument("--detection", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Enable MemBuddy allocation detection (0xCDCD markers + record-table probe for out-of-bounds writes; applies to both mb and perm_pool)")
|
||||
parser.add_argument("--show-args", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show all parsed arguments")
|
||||
parser.add_argument("--build-dir", None, argparse.STRING, 0, None, False, argparse.STORE, "Override project.vpj build_dir (default ./.tpv_build; relative paths resolve against the project dir)")
|
||||
parser.add_argument("--version", "-v", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show version and exit")
|
||||
parser.add_argument("--help", "-h", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show help and exit")
|
||||
|
||||
# 解析命令行参数
|
||||
args: argparse.ParsedArgs | t.CPtr = parser.parse_args(sys._argc, sys._argv)
|
||||
if args is None:
|
||||
if log is not None:
|
||||
log.error("参数解析失败", "argparse")
|
||||
log.error("参数解析失败", "argparse")
|
||||
return 1
|
||||
|
||||
if args.get_bool("help"):
|
||||
@@ -184,39 +182,40 @@ Target: %s
|
||||
return 0
|
||||
|
||||
# 打印解析结果
|
||||
#if log is not None:
|
||||
# log.banner("TransPyV 参数解析结果")
|
||||
# log.banner("TransPyV 参数解析结果")
|
||||
|
||||
# 字符串参数
|
||||
project: str = args.get_str("project")
|
||||
src: str = args.get_str("src")
|
||||
temp: str = args.get_str("temp")
|
||||
output: str = args.get_str("output")
|
||||
phase: str = args.get_str("phase")
|
||||
cc: str = args.get_str("cc")
|
||||
_gb_clean: INT = args.get_bool("clean")
|
||||
#if project is not None: stdio.printf(" --project: %s\n", project)
|
||||
#else: stdio.printf(" --project: (未指定)\n")
|
||||
#if src is not None: stdio.printf(" --src: %s\n", src)
|
||||
#else: stdio.printf(" --src: (未指定)\n")
|
||||
#if temp is not None: stdio.printf(" --temp: %s\n", temp)
|
||||
#else: stdio.printf(" --temp: (未指定)\n")
|
||||
#if output is not None: stdio.printf(" --output: %s\n", output)
|
||||
#else: stdio.printf(" --output: (未指定)\n")
|
||||
#if phase is not None: stdio.printf(" --phase: %s\n", phase)
|
||||
#else: stdio.printf(" --phase: (未指定,默认 all)\n")
|
||||
#if cc is not None: stdio.printf(" --cc: %s\n", cc)
|
||||
#else: stdio.printf(" --cc: (未指定)\n")
|
||||
#if _gb_clean: stdio.printf(" --clean: True\n")
|
||||
#else: stdio.printf(" --clean: False\n")
|
||||
#if args.get_bool("run"): stdio.printf(" --run: True\n")
|
||||
#else: stdio.printf(" --run: False\n")
|
||||
#if args.get_bool("rebuild-includes"): stdio.printf(" --rebuild-includes: True\n")
|
||||
#else: stdio.printf(" --rebuild-includes: False\n")
|
||||
#if args.get_bool("clear-cache"): stdio.printf(" --clear-cache: True\n")
|
||||
#else: stdio.printf(" --clear-cache: False\n")
|
||||
|
||||
#stdio.printf("\n参数解析完成。\n")
|
||||
project: str = args.get_str("project")
|
||||
src: str = args.get_str("src")
|
||||
temp: str = args.get_str("temp")
|
||||
output: str = args.get_str("output")
|
||||
phase: str = args.get_str("phase")
|
||||
cc: str = args.get_str("cc")
|
||||
build_dir: str = args.get_str("build-dir")
|
||||
_gb_clean: bool = args.get_bool("clean")
|
||||
show_args: bool = args.get_bool("show-args")
|
||||
if show_args:
|
||||
if project is not None: stdio.printf(" --project: %s\n", project)
|
||||
else: stdio.printf(" --project: (未指定)\n")
|
||||
if src is not None: stdio.printf(" --src: %s\n", src)
|
||||
else: stdio.printf(" --src: (未指定)\n")
|
||||
if temp is not None: stdio.printf(" --temp: %s\n", temp)
|
||||
else: stdio.printf(" --temp: (未指定)\n")
|
||||
if output is not None: stdio.printf(" --output: %s\n", output)
|
||||
else: stdio.printf(" --output: (未指定)\n")
|
||||
if phase is not None: stdio.printf(" --phase: %s\n", phase)
|
||||
else: stdio.printf(" --phase: (未指定,默认 all)\n")
|
||||
if cc is not None: stdio.printf(" --cc: %s\n", cc)
|
||||
else: stdio.printf(" --cc: (未指定)\n")
|
||||
if _gb_clean: stdio.printf(" --clean: True\n")
|
||||
else: stdio.printf(" --clean: False\n")
|
||||
if args.get_bool("run"): stdio.printf(" --run: True\n")
|
||||
else: stdio.printf(" --run: False\n")
|
||||
if args.get_bool("rebuild-includes"): stdio.printf(" --rebuild-includes: True\n")
|
||||
else: stdio.printf(" --rebuild-includes: False\n")
|
||||
if args.get_bool("clear-cache"): stdio.printf(" --clear-cache: True\n")
|
||||
else: stdio.printf(" --clear-cache: False\n")
|
||||
stdio.printf("参数解析完成。\n\n")
|
||||
|
||||
# ASan 模式
|
||||
# includes 缓存用独立目录 .binary_asan 隔离 asan 插桩 obj,避免与非 asan obj 混链
|
||||
@@ -224,17 +223,12 @@ Target: %s
|
||||
if args.get_bool("asan"):
|
||||
BuildPipeline._asan_mode = True
|
||||
Config._inc_bin_suffix = ".binary_asan"
|
||||
if log is not None:
|
||||
log.info("ASan 模式已启用(opt -passes=asan + libclang_rt)", "asan")
|
||||
log.info("ASan 模式已启用(opt -passes=asan + libclang_rt)", "asan")
|
||||
# ASan 模式清理 asan includes 缓存(强制重建 asan 插桩 obj)
|
||||
mf_inc_bin_asan: str = Config.get_includes_binary_dir()
|
||||
if mf_inc_bin_asan is not None:
|
||||
n_asan: int = Utils.CleanDir(mf_inc_bin_asan)
|
||||
if log is not None:
|
||||
fb_asan: str = VLogger.fmt_buf()
|
||||
if fb_asan is not None:
|
||||
viperlib.snprintf(fb_asan, 1024, "ASan 模式清理 includes 缓存: %d 个文件(将重建 asan obj)", n_asan)
|
||||
log.info(fb_asan, "asan")
|
||||
log.info(f"ASan 模式清理 includes 缓存: {n_asan} 个文件(将重建 asan obj)", "asan")
|
||||
else:
|
||||
BuildPipeline._asan_mode = False
|
||||
Config._inc_bin_suffix = ".binary"
|
||||
@@ -245,34 +239,23 @@ Target: %s
|
||||
p2c: str = args.get_str("phase2command")
|
||||
if p2c is not None:
|
||||
BuildPipeline._phase2_cmd_path = p2c
|
||||
if log is not None:
|
||||
fb_p2c: str = VLogger.fmt_buf()
|
||||
if fb_p2c is not None:
|
||||
viperlib.snprintf(fb_p2c, 1024, "--phase2command: 不执行 llc/链接,命令写入 %s", p2c)
|
||||
log.info(fb_p2c, "phase2cmd")
|
||||
log.info(f"--phase2command: 不执行 llc/链接,命令写入 {p2c}", "phase2cmd")
|
||||
|
||||
# --nowarn-register:抑制"外部函数返回类型未登记"类 WARN 输出
|
||||
if args.get_bool("nowarn-register"):
|
||||
VLogger.set_nowarn_register(1)
|
||||
if log is not None:
|
||||
log.info("--nowarn-register 已启用:抑制注册类 WARN 输出", "vlogger")
|
||||
VLogger._nowarn_register = 1
|
||||
log.info("--nowarn-register 已启用:抑制注册类 WARN 输出", "vlogger")
|
||||
|
||||
# 加载 project.vpj 配置
|
||||
proj_loaded: int = 0
|
||||
proj_path: str = project
|
||||
if proj_path is not None:
|
||||
if log is not None:
|
||||
log.banner("工程配置")
|
||||
log.banner("工程配置")
|
||||
cfg_ret: int = Config.Load_project_config(proj_path)
|
||||
if cfg_ret == 0:
|
||||
proj_loaded = 1
|
||||
else:
|
||||
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)
|
||||
log.warning(f"无法加载 project.vpj: {proj_path}", "config")
|
||||
else:
|
||||
# 尝试默认路径 project.vpj(当前目录和上级目录)
|
||||
proj_path = "project.vpj"
|
||||
@@ -298,63 +281,45 @@ Target: %s
|
||||
if proj_dir is not None:
|
||||
string.memcpy(proj_dir, proj_path, slash_pos)
|
||||
proj_dir[slash_pos] = '\0'
|
||||
# [--build-dir] 命令行覆盖 project.vpj 的 build_dir:在 resolve_paths 前
|
||||
# 设置,使相对路径仍基于 project_dir 解析(如 ./tpv_build2 → Test/tpv_build2),
|
||||
# 绝对路径(/ 或 \ 开头)由 _join_path 原样保留。
|
||||
if build_dir is not None:
|
||||
Config.BuildDir = build_dir
|
||||
Config.resolve_paths(proj_dir)
|
||||
Config.print_config()
|
||||
|
||||
# === --clean: 清理 temp、output 和 includes.binary 目录 ===
|
||||
if _gb_clean:
|
||||
if log is not None:
|
||||
log.info("清理临时目录...", "clean")
|
||||
log.info("清理临时目录...", "clean")
|
||||
if Config.TempDir is not None:
|
||||
n: int = Utils.CleanDir(Config.TempDir)
|
||||
if log is not None:
|
||||
fb: str = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "%s: 删除 %d 个文件", Config.TempDir, n)
|
||||
log.info(fb, "clean")
|
||||
log.info(f"{Config.TempDir}: 删除 {n} 个文件", "clean")
|
||||
if Config.OutputDir is not None:
|
||||
n2: int = Utils.CleanDir(Config.OutputDir)
|
||||
if log is not None:
|
||||
fb2: str = VLogger.fmt_buf()
|
||||
if fb2 is not None:
|
||||
viperlib.snprintf(fb2, 1024, "%s: 删除 %d 个文件", Config.OutputDir, n2)
|
||||
log.info(fb2, "clean")
|
||||
log.info(f"{Config.OutputDir}: 删除 {n2} 个文件", "clean")
|
||||
# 清理 includes.binary 目录(旧 .obj 文件会导致跳过重新编译)
|
||||
mf_inc_bin_clean: str = Config.get_includes_binary_dir()
|
||||
if mf_inc_bin_clean is not None:
|
||||
n3: int = Utils.CleanDir(mf_inc_bin_clean)
|
||||
if log is not None:
|
||||
fb3: str = VLogger.fmt_buf()
|
||||
if fb3 is not None:
|
||||
viperlib.snprintf(fb3, 1024, "%s: 删除 %d 个文件", mf_inc_bin_clean, n3)
|
||||
log.info(fb3, "clean")
|
||||
log.info(f"{mf_inc_bin_clean}: 删除 {n3} 个文件", "clean")
|
||||
|
||||
# === --rebuild-includes: 删除 includes.binary 预编译缓存 ===
|
||||
# 强制下次编译时重新编译所有 includes(不清 temp/output)
|
||||
if args.get_bool("rebuild-includes"):
|
||||
if log is not None:
|
||||
log.info("重建 includes 缓存...", "rebuild-includes")
|
||||
log.info("重建 includes 缓存...", "rebuild-includes")
|
||||
mf_inc_bin_rb: str = Config.get_includes_binary_dir()
|
||||
if mf_inc_bin_rb is not None:
|
||||
n_rb: int = Utils.CleanDir(mf_inc_bin_rb)
|
||||
if log is not None:
|
||||
fb_rb: str = VLogger.fmt_buf()
|
||||
if fb_rb is not None:
|
||||
viperlib.snprintf(fb_rb, 1024, "%s: 删除 %d 个文件(includes 将重新编译)", mf_inc_bin_rb, n_rb)
|
||||
log.info(fb_rb, "rebuild-includes")
|
||||
log.info(f"{mf_inc_bin_rb}: 删除 {n_rb} 个文件(includes 将重新编译)", "rebuild-includes")
|
||||
|
||||
# === --clear-cache: 清除 temp 目录缓存(.vpi/.stub.ll/.text.ll) ===
|
||||
# 强制下次 Phase1 重新生成 stub(不清 output/includes.binary)
|
||||
if args.get_bool("clear-cache"):
|
||||
if log is not None:
|
||||
log.info("清除 temp 缓存...", "clear-cache")
|
||||
log.info("清除 temp 缓存...", "clear-cache")
|
||||
if Config.TempDir is not None:
|
||||
n_cc: int = Utils.CleanDir(Config.TempDir)
|
||||
if log is not None:
|
||||
fb_cc: str = VLogger.fmt_buf()
|
||||
if fb_cc is not None:
|
||||
viperlib.snprintf(fb_cc, 1024, "%s: 删除 %d 个文件(.vpi/.stub.ll 缓存)", Config.TempDir, n_cc)
|
||||
log.info(fb_cc, "clear-cache")
|
||||
log.info(f"{Config.TempDir}: 删除 {n_cc} 个文件(.vpi/.stub.ll 缓存)", "clear-cache")
|
||||
|
||||
# === --phase 参数控制 ===
|
||||
# phase=1: 仅 stub 分离(生成 .stub.ll/.text.ll),不编译
|
||||
@@ -370,11 +335,7 @@ Target: %s
|
||||
do_phase1 = 1
|
||||
if string.strcmp(phase_mode, "2") == 0 or string.strcmp(phase_mode, "all") == 0:
|
||||
do_phase2 = 1
|
||||
if log is not None:
|
||||
fb3: str = 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")
|
||||
log.info(f"模式: {phase_mode} (phase1={do_phase1} phase2={do_phase2})", "phase")
|
||||
|
||||
# === Phase1: 扫描 includes(按需翻译)===
|
||||
# 对 includes 目录中每个 .vp/.py 文件,若 stub 不存在则翻译并分离 stub
|
||||
@@ -387,8 +348,7 @@ Target: %s
|
||||
return 1
|
||||
# 如果仅 Phase1(不执行 Phase2),直接退出
|
||||
if do_phase2 == 0:
|
||||
if log is not None:
|
||||
log.info("Phase1 完成,退出", "phase")
|
||||
log.info("Phase1 完成,退出", "phase")
|
||||
return 0
|
||||
|
||||
# AST 解析:如果指定了 --src 或 --project,读取文件并解析为 AST 树
|
||||
@@ -399,21 +359,20 @@ Target: %s
|
||||
# 未初始化的 Config 字段可能为垃圾指针 → is not None 检查通过 →
|
||||
# 用非法路径 → 崩溃且 ASan 堆栈来不及输出)
|
||||
if proj_loaded == 0 and src_path is None:
|
||||
if log is not None:
|
||||
log.error("未找到 project.vpj(当前目录/上级目录)且未指定 --src,无法构建", "config")
|
||||
log.error("未找到 project.vpj(当前目录/上级目录)且未指定 --src,无法构建", "config")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
|
||||
# === 多文件项目模式 ===
|
||||
# 当 --project 指定但 --src 未指定时,扫描 source_dir 下所有 .vp/.py 文件编译
|
||||
if src_path is None and project is not None and Config.SourceDir is not None:
|
||||
mf_temp: str = Config.TempDir if Config.TempDir is not None else "."
|
||||
mf_output: str = Config.OutputDir if Config.OutputDir is not None else "."
|
||||
mf_cc: str = Config.CompilerCmd if Config.CompilerCmd is not None else "llc"
|
||||
mf_cc_flags: str = Config.CompilerFlags if Config.CompilerFlags is not None else "-filetype=obj -relocation-model=pic"
|
||||
mf_linker: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++"
|
||||
mf_temp: str = Config.TempDir if Config.TempDir is not None else "."
|
||||
mf_output: str = Config.OutputDir if Config.OutputDir is not None else "."
|
||||
mf_cc: str = Config.CompilerCmd if Config.CompilerCmd is not None else "llc"
|
||||
mf_cc_flags: str = Config.CompilerFlags if Config.CompilerFlags is not None else "-filetype=obj -relocation-model=pic"
|
||||
mf_linker: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++"
|
||||
mf_linker_flags: str = Config.LinkerFlags if Config.LinkerFlags is not None else "-lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -lgcc -Wl,--allow-multiple-definition"
|
||||
mf_linker_out: str = Config.LinkerOutput if Config.LinkerOutput is not None else "app.exe"
|
||||
mf_linker_out: str = Config.LinkerOutput if Config.LinkerOutput is not None else "app.exe"
|
||||
mf_includes_bin: str = Config.get_includes_binary_dir()
|
||||
|
||||
mf_ret: int = Phase2.RunMultiFileProject(
|
||||
@@ -421,49 +380,32 @@ Target: %s
|
||||
mf_cc, mf_cc_flags, mf_linker, mf_linker_flags, mf_linker_out,
|
||||
mf_includes_bin, Config.IncludesDirs, do_phase1, do_phase2, log, args)
|
||||
argparse.release(args)
|
||||
if log is not None:
|
||||
log.success("TransPyV 完成")
|
||||
log.success("TransPyV 完成")
|
||||
return mf_ret
|
||||
|
||||
# === 单文件模式:--src 指定时使用指定文件;--project 但无 SourceDir 时回退 ===
|
||||
if src_path is None and project is not None:
|
||||
if Config.SourceDir is not None:
|
||||
# 构造入口路径: 优先 SourceDir/main.vp,回退 SourceDir/main.py
|
||||
# 构造入口路径: SourceDir/main.vp(不再做 vp -> py 后缀识别回退)
|
||||
sd_len: t.CSizeT = string.strlen(Config.SourceDir)
|
||||
path_buf: bytes = stdlib.malloc(sd_len + 16)
|
||||
if path_buf is not None:
|
||||
# 优先尝试 main.vp
|
||||
viperlib.snprintf(path_buf, sd_len + 16, "%s/main.vp", Config.SourceDir)
|
||||
ef: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
|
||||
if ef.closed:
|
||||
# .vp 不存在,回退 main.py
|
||||
viperlib.snprintf(path_buf, sd_len + 16, "%s/main.py", Config.SourceDir)
|
||||
else:
|
||||
ef.close()
|
||||
src_path = path_buf
|
||||
if log is not None:
|
||||
fb4: str = VLogger.fmt_buf()
|
||||
if fb4 is not None:
|
||||
viperlib.snprintf(fb4, 1024, "入口文件: %s", src_path)
|
||||
log.info(fb4, "project")
|
||||
log.info(f"入口文件: {src_path}", "project")
|
||||
|
||||
if src_path is not None:
|
||||
if log is not None:
|
||||
log.banner("AST 解析")
|
||||
log.banner("AST 解析")
|
||||
# 打开文件
|
||||
f: fileio.File | t.CPtr = fileio.File(src_path, fileio.MODE.R)
|
||||
if f.closed:
|
||||
if log is not None:
|
||||
fb5: str = VLogger.fmt_buf()
|
||||
if fb5 is not None:
|
||||
viperlib.snprintf(fb5, 1024, "无法打开文件: %s", src_path)
|
||||
log.error(fb5, "fileio")
|
||||
log.error(f"无法打开文件: {src_path}", "fileio")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
# 分配源代码缓冲区
|
||||
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
|
||||
if src_buf is None:
|
||||
if log is not None:
|
||||
log.error("malloc for src_buf failed", "memhub")
|
||||
log.error("malloc for src_buf failed", "memhub")
|
||||
f.close()
|
||||
argparse.release(args)
|
||||
return 1
|
||||
@@ -471,11 +413,7 @@ Target: %s
|
||||
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
|
||||
f.close()
|
||||
if bytes_read < 0:
|
||||
if log is not None:
|
||||
fb6: str = VLogger.fmt_buf()
|
||||
if fb6 is not None:
|
||||
viperlib.snprintf(fb6, 1024, "读取文件失败 (错误码: %d)", bytes_read)
|
||||
log.error(fb6, "fileio")
|
||||
log.error(f"读取文件失败 (错误码: {bytes_read})", "fileio")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
# 添加 null 终止符,确保 strlen 和 SHA1 计算正确
|
||||
@@ -483,12 +421,8 @@ Target: %s
|
||||
src_buf[bytes_read] = 0
|
||||
else:
|
||||
src_buf[SRC_BUF_SIZE - 1] = 0
|
||||
if log is not None:
|
||||
log.info("文件读取完成", "fileio")
|
||||
fb7: str = VLogger.fmt_buf()
|
||||
if fb7 is not None:
|
||||
viperlib.snprintf(fb7, 1024, "读取 %d 字节", bytes_read)
|
||||
log.info(fb7, "fileio")
|
||||
log.info(f"读取 {bytes_read} 字节", "fileio")
|
||||
# 解析 AST(拆分为词法+语法两阶段,便于调试)
|
||||
ast._init_tables(mb)
|
||||
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
|
||||
@@ -496,14 +430,12 @@ Target: %s
|
||||
tokens: ast.Token | t.CPtr = ast.tokenize(lx)
|
||||
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
|
||||
if tree is None:
|
||||
if log is not None:
|
||||
log.error("AST 解析失败", "ast")
|
||||
log.error("AST 解析失败", "ast")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
|
||||
# === 翻译 AST → LLVM IR ===
|
||||
if log is not None:
|
||||
log.banner("LLVM IR 翻译")
|
||||
log.banner("LLVM IR 翻译")
|
||||
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
|
||||
result: int = tr.translate(tree)
|
||||
if result == 0:
|
||||
@@ -513,8 +445,7 @@ Target: %s
|
||||
tr.dump_ir(ir_buf, IR_BUF_SIZE, llvmlite.OUTPUT_FULL)
|
||||
|
||||
# === 编译管线: .ll → .obj → .exe ===
|
||||
if log is not None:
|
||||
log.banner("编译管线")
|
||||
log.banner("编译管线")
|
||||
|
||||
# module_name 用源代码内容的 SHA1(前 16 字符),与 Projectrans.py 一致
|
||||
# output_name 从文件名推导(如 test.vp -> test.exe),保持可读性
|
||||
@@ -582,8 +513,7 @@ Target: %s
|
||||
|
||||
# === Phase1 stub 分离:使用 OUTPUT_STUB/TEXT 模式分别生成 stub.ll 和 text.ll ===
|
||||
if do_phase1 != 0 and temp_dir is not None and module_name is not None:
|
||||
if log is not None:
|
||||
log.banner("stub 分离")
|
||||
log.banner("stub 分离")
|
||||
SF_IR_SIZE: t.CSizeT = 262144
|
||||
td_len_sf: t.CSizeT = string.strlen(temp_dir)
|
||||
# 保存 stub.ll
|
||||
@@ -615,8 +545,7 @@ Target: %s
|
||||
|
||||
# phase=1 模式:仅生成 stub,不执行编译
|
||||
if do_phase2 == 0:
|
||||
if log is not None:
|
||||
log.success("Phase1 完成(仅 stub 分离,跳过编译)")
|
||||
log.success("Phase1 完成(仅 stub 分离,跳过编译)")
|
||||
argparse.release(args)
|
||||
return 0
|
||||
|
||||
@@ -624,8 +553,7 @@ Target: %s
|
||||
final_ir: bytes = ir_buf
|
||||
final_ir_len: t.CSizeT = ir_len
|
||||
if temp_dir is not None and module_name is not None:
|
||||
if log is not None:
|
||||
log.banner("IR 组合")
|
||||
log.banner("IR 组合")
|
||||
SF_COMBINED_SIZE: t.CSizeT = 4194304 # 4MB
|
||||
combined_buf: bytes = stdlib.malloc(SF_COMBINED_SIZE)
|
||||
if combined_buf is not None:
|
||||
@@ -643,47 +571,28 @@ Target: %s
|
||||
includes_binary_dir
|
||||
)
|
||||
if br is not None and br.Success == 1:
|
||||
if log is not None:
|
||||
log.success("编译管线完成")
|
||||
fb8: str = VLogger.fmt_buf()
|
||||
if fb8 is not None:
|
||||
viperlib.snprintf(fb8, 1024, "输出: %s/%s", output_dir, linker_output)
|
||||
log.info(fb8)
|
||||
log.success("编译管线完成")
|
||||
log.info(f"输出: {output_dir}/{linker_output}")
|
||||
# --run 模式:执行生成的可执行文件
|
||||
# phase2command 模式:exe 尚未链接(命令在 bat 中),跳过 --run
|
||||
if args.get_bool("run") and BuildPipeline._phase2_cmd_path is None:
|
||||
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)
|
||||
if log is not None:
|
||||
fb9: str = VLogger.fmt_buf()
|
||||
if fb9 is not None:
|
||||
viperlib.snprintf(fb9, 1024, "执行: %s", exe_path)
|
||||
log.info(fb9, "run")
|
||||
log.info(f"执行: {exe_path}")
|
||||
r: subprocess.CompletedProcess | t.CPtr = subprocess.run(exe_path, False, False)
|
||||
if r is not None:
|
||||
if log is not None:
|
||||
fb10: str = VLogger.fmt_buf()
|
||||
if fb10 is not None:
|
||||
viperlib.snprintf(fb10, 1024, "退出码: %d", r.returncode)
|
||||
log.info(fb10, "run")
|
||||
log.info(f"退出码: {r.returncode}")
|
||||
else:
|
||||
if log is not None:
|
||||
log.error("编译管线失败", "pipeline")
|
||||
if br is not None and br.ErrorMsg is not None:
|
||||
fb11: str = VLogger.fmt_buf()
|
||||
if fb11 is not None:
|
||||
viperlib.snprintf(fb11, 1024, "编译管线失败: %s", br.ErrorMsg)
|
||||
log.error(fb11, "FATAL")
|
||||
log.error("编译管线失败", "pipeline")
|
||||
if br is not None and br.ErrorMsg is not None:
|
||||
log.error(f"编译管线失败: {br.ErrorMsg}", "FATAL")
|
||||
sys.exit(1)
|
||||
else:
|
||||
if log is not None:
|
||||
log.error("malloc for IR buffer failed", "memhub")
|
||||
log.error("malloc for IR buffer failed", "memhub")
|
||||
else:
|
||||
if log is not None:
|
||||
log.error("翻译失败", "translator")
|
||||
log.error("翻译失败", "translator")
|
||||
|
||||
argparse.release(args)
|
||||
if log is not None:
|
||||
log.success("TransPyV 完成")
|
||||
log.success("TransPyV 完成")
|
||||
return 0
|
||||
Reference in new issue
Block a user